Merge "Notify DexManager about new package installs"
diff --git a/Android.mk b/Android.mk
index 21bd76b..63ad83f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -107,6 +107,7 @@
 	core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreSession.aidl \
+	core/java/android/app/backup/ISelectBackupTransportCallback.aidl \
 	core/java/android/app/usage/IStorageStatsManager.aidl \
 	core/java/android/app/usage/IUsageStatsManager.aidl \
 	core/java/android/bluetooth/IBluetooth.aidl \
@@ -346,6 +347,7 @@
 	core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
 	core/java/com/android/internal/backup/IBackupTransport.aidl \
 	core/java/com/android/internal/backup/IObbBackupService.aidl \
+	core/java/com/android/internal/font/IFontManager.aidl \
 	core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl \
 	core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl \
 	core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl \
@@ -423,10 +425,12 @@
 	media/java/android/media/projection/IMediaProjectionManager.aidl \
 	media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl \
 	media/java/android/media/session/IActiveSessionsListener.aidl \
-	media/java/android/media/session/ISessionController.aidl \
-	media/java/android/media/session/ISessionControllerCallback.aidl \
+	media/java/android/media/session/IOnMediaKeyListener.aidl \
+	media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl \
 	media/java/android/media/session/ISession.aidl \
 	media/java/android/media/session/ISessionCallback.aidl \
+	media/java/android/media/session/ISessionController.aidl \
+	media/java/android/media/session/ISessionControllerCallback.aidl \
 	media/java/android/media/session/ISessionManager.aidl \
 	media/java/android/media/tv/ITvInputClient.aidl \
 	media/java/android/media/tv/ITvInputHardware.aidl \
@@ -460,6 +464,8 @@
         telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl \
         telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl \
 	telephony/java/com/android/ims/internal/IImsService.aidl \
+	telephony/java/com/android/ims/internal/IImsServiceController.aidl \
+	telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl \
 	telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl \
 	telephony/java/com/android/ims/internal/IImsUt.aidl \
 	telephony/java/com/android/ims/internal/IImsUtListener.aidl \
@@ -535,6 +541,7 @@
     framework-protos                                    \
     android.hardware.thermal@1.0-java-constants         \
     android.hardware.health@1.0-java-constants          \
+    android.hardware.usb@1.0-java-constants             \
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := stream
 LOCAL_PROTOC_FLAGS := \
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 3a7f945..200f92f 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -8,7 +8,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    apct-perftests-utils
+    apct-perftests-utils \
+    legacy-android-test
 
 LOCAL_PACKAGE_NAME := CorePerfTests
 
diff --git a/api/current.txt b/api/current.txt
index 2aa1a7e..e50a72b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -83,6 +83,7 @@
     field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
     field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+    field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
     field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
     field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
     field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -113,6 +114,7 @@
     field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
     field public static final java.lang.String RECORD_AUDIO = "android.permission.RECORD_AUDIO";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
+    field public static final java.lang.String REQUEST_DELETE_PACKAGES = "android.permission.REQUEST_DELETE_PACKAGES";
     field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
@@ -201,6 +203,8 @@
 
   public static final class R.attr {
     ctor public R.attr();
+    field public static final int __removed0 = 16844097; // 0x1010541
+    field public static final int __removed1 = 16844099; // 0x1010543
     field public static final int absListViewStyle = 16842858; // 0x101006a
     field public static final int accessibilityEventTypes = 16843648; // 0x1010380
     field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -361,6 +365,7 @@
     field public static final int centerMedium = 16842959; // 0x10100cf
     field public static final int centerX = 16843170; // 0x10101a2
     field public static final int centerY = 16843171; // 0x10101a3
+    field public static final int certDigest = 16844106; // 0x101054a
     field public static final int checkBoxPreferenceStyle = 16842895; // 0x101008f
     field public static final int checkMark = 16843016; // 0x1010108
     field public static final int checkMarkTint = 16843943; // 0x10104a7
@@ -403,6 +408,7 @@
     field public static final int colorForeground = 16842800; // 0x1010030
     field public static final int colorForegroundInverse = 16843270; // 0x1010206
     field public static final int colorLongPressedHighlight = 16843662; // 0x101038e
+    field public static final int colorMode = 16844108; // 0x101054c
     field public static final int colorMultiSelectHighlight = 16843665; // 0x1010391
     field public static final int colorPressedHighlight = 16843661; // 0x101038d
     field public static final int colorPrimary = 16843827; // 0x1010433
@@ -733,6 +739,7 @@
     field public static final int isScrollContainer = 16843342; // 0x101024e
     field public static final int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
+    field public static final int isolatedSplits = 16844109; // 0x101054d
     field public static final int itemBackground = 16843056; // 0x1010130
     field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131
     field public static final int itemPadding = 16843565; // 0x101032d
@@ -755,7 +762,6 @@
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
     field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
-    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -905,7 +911,6 @@
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
-    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -989,6 +994,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
+    field public static final int primaryContentAlpha = 16843367; // 0x1010267
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -1052,6 +1058,7 @@
     field public static final int resizeable = 16843405; // 0x101028d
     field public static final int resizeableActivity = 16844022; // 0x10104f6
     field public static final int resource = 16842789; // 0x1010025
+    field public static final int restartOnConfigChanges = 16844105; // 0x1010549
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
     field public static final int restrictedAccountType = 16843733; // 0x10103d5
@@ -1164,6 +1171,7 @@
     field public static final int spinnerStyle = 16842881; // 0x1010081
     field public static final int spinnersShown = 16843595; // 0x101034b
     field public static final int splitMotionEvents = 16843503; // 0x10102ef
+    field public static final int splitName = 16844107; // 0x101054b
     field public static final int splitTrack = 16843852; // 0x101044c
     field public static final int spotShadowAlpha = 16843967; // 0x10104bf
     field public static final int src = 16843033; // 0x1010119
@@ -1376,7 +1384,7 @@
     field public static final int toYDelta = 16843209; // 0x10101c9
     field public static final int toYScale = 16843205; // 0x10101c5
     field public static final int toolbarStyle = 16843946; // 0x10104aa
-    field public static final int tooltip = 16844084; // 0x1010534
+    field public static final int tooltipText = 16844084; // 0x1010534
     field public static final int top = 16843182; // 0x10101ae
     field public static final int topBright = 16842955; // 0x10100cb
     field public static final int topDark = 16842951; // 0x10100c7
@@ -1435,7 +1443,7 @@
     field public static final int viewportWidth = 16843778; // 0x1010402
     field public static final int visibility = 16842972; // 0x10100dc
     field public static final int visible = 16843156; // 0x1010194
-    field public static final int visibleToEphemeral = 16844095; // 0x101053f
+    field public static final int visibleToInstantApps = 16844095; // 0x101053f
     field public static final int vmSafeMode = 16843448; // 0x10102b8
     field public static final int voiceIcon = 16843908; // 0x1010484
     field public static final int voiceLanguage = 16843349; // 0x1010255
@@ -1812,6 +1820,7 @@
     field public static final int tabs = 16908307; // 0x1020013
     field public static final int text1 = 16908308; // 0x1020014
     field public static final int text2 = 16908309; // 0x1020015
+    field public static final int textAssist = 16908353; // 0x1020041
     field public static final int title = 16908310; // 0x1020016
     field public static final int toggle = 16908311; // 0x1020017
     field public static final int undo = 16908338; // 0x1020032
@@ -2878,15 +2887,18 @@
   public class AccountManager {
     method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
-    method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, int[]);
+    method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, java.util.Map<java.lang.Integer, java.lang.Integer>);
     method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
+    method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[]);
     method public java.lang.String blockingGetAuthToken(android.accounts.Account, java.lang.String, boolean) throws android.accounts.AuthenticatorException, java.io.IOException, android.accounts.OperationCanceledException;
     method public void clearPassword(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(java.lang.String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public static android.accounts.AccountManager get(android.content.Context);
+    method public int getAccountVisibility(android.accounts.Account, int);
     method public android.accounts.Account[] getAccounts();
+    method public java.util.Map<android.accounts.Account, java.lang.Integer> getAccountsAndVisibilityForPackage(java.lang.String, java.lang.String);
     method public android.accounts.Account[] getAccountsByType(java.lang.String);
     method public android.accounts.AccountManagerFuture<android.accounts.Account[]> getAccountsByTypeAndFeatures(java.lang.String, java.lang.String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler);
     method public android.accounts.Account[] getAccountsByTypeForPackage(java.lang.String, java.lang.String);
@@ -2897,13 +2909,11 @@
     method public android.accounts.AuthenticatorDescription[] getAuthenticatorTypes();
     method public java.lang.String getPassword(android.accounts.Account);
     method public java.lang.String getPreviousName(android.accounts.Account);
-    method public int[] getRequestingUidsForType(java.lang.String);
+    method public java.util.Map<java.lang.Integer, java.lang.Integer> getUidsAndVisibilityForAccount(android.accounts.Account);
     method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public void invalidateAuthToken(java.lang.String, java.lang.String);
-    method public boolean isAccountVisible(android.accounts.Account, int);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
-    method public boolean makeAccountVisible(android.accounts.Account, int);
     method public static deprecated android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], boolean, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
     method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.List<android.accounts.Account>, java.lang.String[], java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
     method public boolean notifyAccountAuthenticated(android.accounts.Account);
@@ -2911,9 +2921,9 @@
     method public deprecated android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean removeAccountExplicitly(android.accounts.Account);
-    method public boolean removeAccountVisibility(android.accounts.Account, int);
     method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
     method public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
+    method public boolean setAccountVisibility(android.accounts.Account, int, int);
     method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
     method public void setPassword(android.accounts.Account, java.lang.String);
     method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
@@ -2952,7 +2962,14 @@
     field public static final java.lang.String KEY_LAST_AUTHENTICATED_TIME = "lastAuthenticatedTime";
     field public static final java.lang.String KEY_PASSWORD = "password";
     field public static final java.lang.String KEY_USERDATA = "userdata";
-    field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+    field public static final deprecated java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+    field public static final int UID_KEY_DEFAULT_LEGACY_VISIBILITY = -3; // 0xfffffffd
+    field public static final int UID_KEY_DEFAULT_VISIBILITY = -2; // 0xfffffffe
+    field public static final int VISIBILITY_NOT_VISIBLE = 3; // 0x3
+    field public static final int VISIBILITY_UNDEFINED = 0; // 0x0
+    field public static final int VISIBILITY_USER_MANAGED_NOT_VISIBLE = 4; // 0x4
+    field public static final int VISIBILITY_USER_MANAGED_VISIBLE = 2; // 0x2
+    field public static final int VISIBILITY_VISIBLE = 1; // 0x1
   }
 
   public abstract interface AccountManagerCallback<V> {
@@ -3051,8 +3068,10 @@
 
   public static abstract interface Animator.AnimatorListener {
     method public abstract void onAnimationCancel(android.animation.Animator);
+    method public default void onAnimationEnd(android.animation.Animator, boolean);
     method public abstract void onAnimationEnd(android.animation.Animator);
     method public abstract void onAnimationRepeat(android.animation.Animator);
+    method public default void onAnimationStart(android.animation.Animator, boolean);
     method public abstract void onAnimationStart(android.animation.Animator);
   }
 
@@ -3088,6 +3107,8 @@
     method public void playSequentially(java.util.List<android.animation.Animator>);
     method public void playTogether(android.animation.Animator...);
     method public void playTogether(java.util.Collection<android.animation.Animator>);
+    method public void reverse();
+    method public void setCurrentPlayTime(long);
     method public android.animation.AnimatorSet setDuration(long);
     method public void setInterpolator(android.animation.TimeInterpolator);
     method public void setStartDelay(long);
@@ -3490,8 +3511,7 @@
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
-    method public void enterPictureInPictureMode(float);
-    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
+    method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -3524,7 +3544,6 @@
     method public int getRequestedOrientation();
     method public final android.view.SearchEvent getSearchEvent();
     method public int getTaskId();
-    method public android.text.TextAssistant getTextAssistant();
     method public final java.lang.CharSequence getTitle();
     method public final int getTitleColor();
     method public android.app.VoiceInteractor getVoiceInteractor();
@@ -3664,8 +3683,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
-    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
-    method public void setPictureInPictureAspectRatio(float);
+    method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
     method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -3675,7 +3693,6 @@
     method public final void setResult(int, android.content.Intent);
     method public final deprecated void setSecondaryProgress(int);
     method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
-    method public void setTextAssistant(android.text.TextAssistant);
     method public void setTitle(java.lang.CharSequence);
     method public void setTitle(int);
     method public deprecated void setTitleColor(int);
@@ -4591,6 +4608,7 @@
 
   public abstract class FragmentContainer {
     ctor public FragmentContainer();
+    method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
     method public abstract android.view.View onFindViewById(int);
     method public abstract boolean onHasView();
   }
@@ -4826,11 +4844,13 @@
   public static class Instrumentation.ActivityMonitor {
     ctor public Instrumentation.ActivityMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean);
     ctor public Instrumentation.ActivityMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean);
+    ctor public Instrumentation.ActivityMonitor();
     method public final android.content.IntentFilter getFilter();
     method public final int getHits();
     method public final android.app.Activity getLastActivity();
     method public final android.app.Instrumentation.ActivityResult getResult();
     method public final boolean isBlocking();
+    method public android.app.Instrumentation.ActivityResult onMatchIntent(android.content.Intent);
     method public final android.app.Activity waitForActivity();
     method public final android.app.Activity waitForActivityWithTimeout(long);
   }
@@ -5002,6 +5022,7 @@
     method public android.graphics.drawable.Icon getLargeIcon();
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
+    method public long getTimeout();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
     field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -5025,11 +5046,14 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+    field public static final java.lang.String EXTRA_COLORIZED = "android.colorized";
     field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final java.lang.String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
     field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
@@ -5108,6 +5132,7 @@
     method public android.app.Notification.Action clone();
     method public int describeContents();
     method public boolean getAllowGeneratedReplies();
+    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
     method public android.os.Bundle getExtras();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
@@ -5172,7 +5197,8 @@
   }
 
   public static class Notification.Builder {
-    ctor public Notification.Builder(android.content.Context);
+    ctor public Notification.Builder(android.content.Context, java.lang.String);
+    ctor public deprecated Notification.Builder(android.content.Context);
     method public deprecated android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
     method public android.app.Notification.Builder addAction(android.app.Notification.Action);
     method public android.app.Notification.Builder addExtras(android.os.Bundle);
@@ -5191,6 +5217,7 @@
     method public android.app.Notification.Builder setChannel(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
     method public android.app.Notification.Builder setColor(int);
+    method public android.app.Notification.Builder setColorized(boolean);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
@@ -5228,6 +5255,7 @@
     method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
     method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
     method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public android.app.Notification.Builder setTimeout(long);
     method public android.app.Notification.Builder setUsesChronometer(boolean);
     method public android.app.Notification.Builder setVibrate(long[]);
     method public android.app.Notification.Builder setVisibility(int);
@@ -5294,9 +5322,11 @@
 
   public static class Notification.MessagingStyle extends android.app.Notification.Style {
     ctor public Notification.MessagingStyle(java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addHistoricMessage(android.app.Notification.MessagingStyle.Message);
     method public android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
     method public android.app.Notification.MessagingStyle addMessage(android.app.Notification.MessagingStyle.Message);
     method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.app.Notification.MessagingStyle.Message> getHistoricMessages();
     method public java.util.List<android.app.Notification.MessagingStyle.Message> getMessages();
     method public java.lang.CharSequence getUserDisplayName();
     method public android.app.Notification.MessagingStyle setConversationTitle(java.lang.CharSequence);
@@ -5391,14 +5421,15 @@
     method public boolean canShowBadge();
     method public int describeContents();
     method public void enableVibration(boolean);
+    method public java.lang.String getGroup();
     method public java.lang.String getId();
     method public int getImportance();
     method public int getLockscreenVisibility();
     method public java.lang.CharSequence getName();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
-    method public boolean isAllowed();
     method public void setBypassDnd(boolean);
+    method public void setGroup(java.lang.String);
     method public void setImportance(int);
     method public void setLights(boolean);
     method public void setLockscreenVisibility(int);
@@ -5412,6 +5443,17 @@
     field public static final java.lang.String DEFAULT_CHANNEL_ID = "miscellaneous";
   }
 
+  public final class NotificationChannelGroup implements android.os.Parcelable {
+    ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
+    ctor protected NotificationChannelGroup(android.os.Parcel);
+    method public int describeContents();
+    method public java.util.List<android.app.NotificationChannel> getChannels();
+    method public java.lang.String getId();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
+  }
+
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
     method public boolean areNotificationsEnabled();
@@ -5419,6 +5461,8 @@
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
     method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+    method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup>);
     method public void createNotificationChannels(java.util.List<android.app.NotificationChannel>);
     method public void deleteNotificationChannel(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
@@ -5435,6 +5479,7 @@
     method public boolean removeAutomaticZenRule(java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -5520,6 +5565,17 @@
     method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
   }
 
+  public final class PictureInPictureArgs implements android.os.Parcelable {
+    ctor public PictureInPictureArgs();
+    ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
+    method public android.app.PictureInPictureArgs clone();
+    method public int describeContents();
+    method public void setActions(java.util.List<android.app.RemoteAction>);
+    method public void setAspectRatio(float);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
+  }
+
   public class Presentation extends android.app.Dialog {
     ctor public Presentation(android.content.Context, android.view.Display);
     ctor public Presentation(android.content.Context, android.view.Display, int);
@@ -5556,6 +5612,18 @@
     field public static final int STYLE_SPINNER = 0; // 0x0
   }
 
+  public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
+    ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
+    method public int describeContents();
+    method public android.app.PendingIntent getUserAction();
+    method public java.lang.CharSequence getUserActionTitle();
+    method public java.lang.CharSequence getUserMessage();
+    method public void showAsDialog(android.app.Activity);
+    method public void showAsNotification(android.content.Context);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR;
+  }
+
   public final class RemoteAction implements android.os.Parcelable {
     ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
     method public android.app.RemoteAction clone();
@@ -5573,14 +5641,18 @@
   }
 
   public final class RemoteInput implements android.os.Parcelable {
+    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
     method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
     method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
     method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getResultKey();
     method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
     field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5592,6 +5664,7 @@
     method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
     method public android.app.RemoteInput build();
     method public android.os.Bundle getExtras();
+    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
     method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
     method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
     method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
@@ -6068,6 +6141,7 @@
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearProfileOwner(android.content.ComponentName);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+    method public android.content.Intent createAdminSupportIntent(java.lang.String);
     method public android.os.UserHandle createAndManageUser(android.content.ComponentName, java.lang.String, android.content.ComponentName, android.os.PersistableBundle, int);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
@@ -6076,16 +6150,18 @@
     method public java.util.List<java.lang.String> getAffiliationIds(android.content.ComponentName);
     method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
-    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
+    method public deprecated java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(android.content.ComponentName);
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
-    method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+    method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
     method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
+    method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String);
+    method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
     method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
@@ -6107,6 +6183,7 @@
     method public int getPasswordMinimumSymbols(android.content.ComponentName);
     method public int getPasswordMinimumUpperCase(android.content.ComponentName);
     method public int getPasswordQuality(android.content.ComponentName);
+    method public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(android.content.ComponentName);
     method public int getPermissionGrantState(android.content.ComponentName, java.lang.String, java.lang.String);
     method public int getPermissionPolicy(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
@@ -6128,7 +6205,8 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
-    method public boolean isCallerApplicationRestrictionsManagingPackage();
+    method public boolean isBackupServiceEnabled(android.content.ComponentName);
+    method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isManagedProfile(android.content.ComponentName);
@@ -6156,13 +6234,15 @@
     method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
-    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public deprecated void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
+    method public void setBackupServiceEnabled(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
-    method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
+    method public deprecated void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
     method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
+    method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
     method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -6210,6 +6290,7 @@
     method public void uninstallCaCert(android.content.ComponentName, byte[]);
     method public void wipeData(int);
     field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
+    field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
     field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
@@ -6219,6 +6300,12 @@
     field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+    field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
+    field public static final java.lang.String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall";
+    field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
+    field public static final java.lang.String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
+    field public static final java.lang.String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
+    field public static final java.lang.String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
     field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
@@ -6226,6 +6313,7 @@
     field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
     field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
     field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
+    field public static final java.lang.String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
     field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
     field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
     field public static final java.lang.String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
@@ -6284,6 +6372,8 @@
     field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
     field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
     field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
+    field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
+    field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -6328,6 +6418,17 @@
     field public static final android.os.Parcelable.Creator<android.app.admin.SecurityLog.SecurityEvent> CREATOR;
   }
 
+  public final class SystemUpdateInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getReceivedTime();
+    method public int getSecurityPatchState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdateInfo> CREATOR;
+    field public static final int SECURITY_PATCH_STATE_FALSE = 1; // 0x1
+    field public static final int SECURITY_PATCH_STATE_TRUE = 2; // 0x2
+    field public static final int SECURITY_PATCH_STATE_UNKNOWN = 0; // 0x0
+  }
+
   public class SystemUpdatePolicy implements android.os.Parcelable {
     method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy();
     method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy();
@@ -7911,6 +8012,7 @@
     ctor public ClipData(android.content.ClipDescription, android.content.ClipData.Item);
     ctor public ClipData(android.content.ClipData);
     method public void addItem(android.content.ClipData.Item);
+    method public void addItem(android.content.ClipData.Item, android.content.ContentResolver);
     method public int describeContents();
     method public android.content.ClipDescription getDescription();
     method public android.content.ClipData.Item getItemAt(int);
@@ -8296,6 +8398,7 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public abstract android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.Context createDeviceProtectedStorageContext();
     method public abstract android.content.Context createDisplayContext(android.view.Display);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -8429,6 +8532,7 @@
     field public static final java.lang.String DOWNLOAD_SERVICE = "download";
     field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
     field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
+    field public static final java.lang.String FONT_SERVICE = "font";
     field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
     field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
     field public static final java.lang.String INPUT_SERVICE = "input";
@@ -8493,6 +8597,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.Context createDeviceProtectedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -8801,6 +8906,7 @@
     field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
     field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
     field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
+    field public static final java.lang.String ACTION_CLEAR_PACKAGE = "android.intent.action.CLEAR_PACKAGE";
     field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
     field public static final java.lang.String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
@@ -8999,10 +9105,10 @@
     field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
     field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER";
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
-    field public static final java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
-    field public static final java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
     field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
     field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
@@ -9515,6 +9621,10 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     method public final int getThemeResource();
+    field public static final int COLOR_MODE_DEFAULT = 0; // 0x0
+    field public static final int COLOR_MODE_HDR = 2; // 0x2
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT = 1; // 0x1
+    field public static final int CONFIG_COLOR_MODE = 16384; // 0x4000
     field public static final int CONFIG_DENSITY = 4096; // 0x1000
     field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
     field public static final int CONFIG_KEYBOARD = 16; // 0x10
@@ -9575,6 +9685,7 @@
     field public static final int SCREEN_ORIENTATION_USER_LANDSCAPE = 11; // 0xb
     field public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12; // 0xc
     field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1
+    field public int colorMode;
     field public int configChanges;
     field public int documentLaunchMode;
     field public int flags;
@@ -9671,6 +9782,7 @@
     field public int requiresSmallestWidthDp;
     field public java.lang.String[] sharedLibraryFiles;
     field public java.lang.String sourceDir;
+    field public java.lang.String[] splitNames;
     field public java.lang.String[] splitPublicSourceDirs;
     field public java.lang.String[] splitSourceDirs;
     field public int targetSdkVersion;
@@ -9699,6 +9811,7 @@
     field public boolean enabled;
     field public boolean exported;
     field public java.lang.String processName;
+    field public java.lang.String splitName;
   }
 
   public class ConfigurationInfo implements android.os.Parcelable {
@@ -9752,6 +9865,7 @@
     field public boolean handleProfiling;
     field public java.lang.String publicSourceDir;
     field public java.lang.String sourceDir;
+    field public java.lang.String[] splitNames;
     field public java.lang.String[] splitPublicSourceDirs;
     field public java.lang.String[] splitSourceDirs;
     field public java.lang.String targetPackage;
@@ -9786,7 +9900,10 @@
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
+    method public java.util.List<android.os.UserHandle> getProfiles();
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+    method public android.content.IntentSender getShortcutConfigActivityIntent(android.content.pm.LauncherActivityInfo);
+    method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(java.lang.String, android.os.UserHandle);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
@@ -9821,7 +9938,7 @@
     method public boolean accept(android.os.Bundle);
     method public boolean accept();
     method public int describeContents();
-    method public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo();
+    method public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo(android.content.Context);
     method public int getRequestType();
     method public android.content.pm.ShortcutInfo getShortcutInfo();
     method public boolean isValid();
@@ -9890,6 +10007,7 @@
     method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
     method public void uninstall(java.lang.String, android.content.IntentSender);
+    method public void uninstall(android.content.pm.VersionedPackage, android.content.IntentSender);
     method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void updateSessionAppIcon(int, android.graphics.Bitmap);
     method public void updateSessionAppLabel(int, java.lang.CharSequence);
@@ -9998,6 +10116,7 @@
     method public abstract boolean addPermission(android.content.pm.PermissionInfo);
     method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo);
     method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
+    method public abstract boolean canRequestPackageInstalls();
     method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]);
     method public abstract int checkPermission(java.lang.String, java.lang.String);
     method public abstract int checkSignatures(java.lang.String, java.lang.String);
@@ -10036,6 +10155,7 @@
     method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInstaller getPackageInstaller();
     method public abstract int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] getPackagesForUid(int);
@@ -10050,6 +10170,7 @@
     method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
     method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public abstract java.lang.String[] getSystemSharedLibraryNames();
     method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -10108,6 +10229,7 @@
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
+    field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
     field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
@@ -10208,6 +10330,7 @@
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
+    field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -10347,6 +10470,20 @@
     field public java.lang.String permission;
   }
 
+  public final class SharedLibraryInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.pm.VersionedPackage getDeclaringPackage();
+    method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
+    method public java.lang.String getName();
+    method public int getVersion();
+    method public boolean isBuiltin();
+    method public boolean isDynamic();
+    method public boolean isStatic();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+    field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
+  }
+
   public final class ShortcutInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivity();
@@ -10390,6 +10527,7 @@
 
   public class ShortcutManager {
     method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public android.content.Intent createShortcutResultIntent(android.content.pm.ShortcutInfo);
     method public void disableShortcuts(java.util.List<java.lang.String>);
     method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
     method public void enableShortcuts(java.util.List<java.lang.String>);
@@ -10421,6 +10559,15 @@
     field public static final android.os.Parcelable.Creator<android.content.pm.Signature> CREATOR;
   }
 
+  public final class VersionedPackage implements android.os.Parcelable {
+    ctor public VersionedPackage(java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getPackageName();
+    method public long getVersionCode();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.VersionedPackage> CREATOR;
+  }
+
 }
 
 package android.content.res {
@@ -10506,7 +10653,9 @@
     method public int getLayoutDirection();
     method public android.os.LocaleList getLocales();
     method public boolean isLayoutSizeAtLeast(int);
+    method public boolean isScreenHdr();
     method public boolean isScreenRound();
+    method public boolean isScreenWideColorGamut();
     method public static boolean needNewResources(int, int);
     method public void readFromParcel(android.os.Parcel);
     method public void setLayoutDirection(java.util.Locale);
@@ -10516,6 +10665,16 @@
     method public void setToDefaults();
     method public int updateFrom(android.content.res.Configuration);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int COLOR_MODE_HDR_MASK = 12; // 0xc
+    field public static final int COLOR_MODE_HDR_NO = 4; // 0x4
+    field public static final int COLOR_MODE_HDR_SHIFT = 2; // 0x2
+    field public static final int COLOR_MODE_HDR_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_HDR_YES = 8; // 0x8
+    field public static final int COLOR_MODE_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 3; // 0x3
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 1; // 0x1
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 2; // 0x2
     field public static final android.os.Parcelable.Creator<android.content.res.Configuration> CREATOR;
     field public static final int DENSITY_DPI_UNDEFINED = 0; // 0x0
     field public static final int HARDKEYBOARDHIDDEN_NO = 1; // 0x1
@@ -10581,6 +10740,7 @@
     field public static final int UI_MODE_TYPE_UNDEFINED = 0; // 0x0
     field public static final int UI_MODE_TYPE_VR_HEADSET = 7; // 0x7
     field public static final int UI_MODE_TYPE_WATCH = 6; // 0x6
+    field public int colorMode;
     field public int densityDpi;
     field public float fontScale;
     field public int hardKeyboardHidden;
@@ -10635,6 +10795,7 @@
     method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
     method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme);
+    method public android.graphics.Typeface getFont(int) throws android.content.res.Resources.NotFoundException;
     method public float getFraction(int, int, int);
     method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String);
     method public int[] getIntArray(int) throws android.content.res.Resources.NotFoundException;
@@ -12121,15 +12282,57 @@
     method public static int HSVToColor(float[]);
     method public static int HSVToColor(int, float[]);
     method public static void RGBToHSV(int, int, int, float[]);
+    method public float alpha();
+    method public static float alpha(long);
     method public static int alpha(int);
     method public static int argb(int, int, int, int);
+    method public static int argb(float, float, float, float);
+    method public float blue();
+    method public static float blue(long);
     method public static int blue(int);
+    method public static android.graphics.ColorSpace colorSpace(long);
     method public static void colorToHSV(int, float[]);
+    method public android.graphics.Color convert(android.graphics.ColorSpace);
+    method public static long convert(int, android.graphics.ColorSpace);
+    method public static long convert(long, android.graphics.ColorSpace);
+    method public static long convert(float, float, float, float, android.graphics.ColorSpace, android.graphics.ColorSpace);
+    method public static long convert(long, android.graphics.ColorSpace.Connector);
+    method public static long convert(float, float, float, float, android.graphics.ColorSpace.Connector);
+    method public android.graphics.ColorSpace getColorSpace();
+    method public float getComponent(int);
+    method public int getComponentCount();
+    method public float[] getComponents();
+    method public android.graphics.ColorSpace.Model getModel();
+    method public float green();
+    method public static float green(long);
     method public static int green(int);
+    method public static boolean isInColorSpace(long, android.graphics.ColorSpace);
+    method public boolean isSrgb();
+    method public static boolean isSrgb(long);
+    method public boolean isWideGamut();
+    method public static boolean isWideGamut(long);
+    method public float luminance();
+    method public static float luminance(long);
     method public static float luminance(int);
+    method public long pack();
+    method public static long pack(int);
+    method public static long pack(float, float, float);
+    method public static long pack(float, float, float, float);
+    method public static long pack(float, float, float, float, android.graphics.ColorSpace);
     method public static int parseColor(java.lang.String);
+    method public float red();
+    method public static float red(long);
     method public static int red(int);
     method public static int rgb(int, int, int);
+    method public static int rgb(float, float, float);
+    method public int toArgb();
+    method public static int toArgb(long);
+    method public static android.graphics.Color valueOf(int);
+    method public static android.graphics.Color valueOf(long);
+    method public static android.graphics.Color valueOf(float, float, float);
+    method public static android.graphics.Color valueOf(float, float, float, float);
+    method public static android.graphics.Color valueOf(float, float, float, float, android.graphics.ColorSpace);
+    method public static android.graphics.Color valueOf(float[], android.graphics.ColorSpace);
     field public static final int BLACK = -16777216; // 0xff000000
     field public static final int BLUE = -16776961; // 0xff0000ff
     field public static final int CYAN = -16711681; // 0xff00ffff
@@ -12201,7 +12404,7 @@
     field public static final float[] ILLUMINANT_D65;
     field public static final float[] ILLUMINANT_D75;
     field public static final float[] ILLUMINANT_E;
-    field public static final int MAX_ID = 64; // 0x40
+    field public static final int MAX_ID = 63; // 0x3f
     field public static final int MIN_ID = -1; // 0xffffffff
   }
 
@@ -12524,7 +12727,6 @@
     method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt);
     method public android.graphics.Paint.FontMetricsInt getFontMetricsInt();
     method public float getFontSpacing();
-    method public java.lang.String getFontVariationSettings();
     method public int getHinting();
     method public float getLetterSpacing();
     method public android.graphics.MaskFilter getMaskFilter();
@@ -12582,7 +12784,6 @@
     method public void setFilterBitmap(boolean);
     method public void setFlags(int);
     method public void setFontFeatureSettings(java.lang.String);
-    method public void setFontVariationSettings(java.lang.String);
     method public void setHinting(int);
     method public void setLetterSpacing(float);
     method public void setLinearText(boolean);
@@ -13080,6 +13281,7 @@
   }
 
   public class Typeface {
+    method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
     method public static android.graphics.Typeface create(java.lang.String, int);
     method public static android.graphics.Typeface create(android.graphics.Typeface, int);
     method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
@@ -13100,6 +13302,14 @@
     field public static final android.graphics.Typeface SERIF;
   }
 
+  public static abstract interface Typeface.FontRequestCallback {
+    method public abstract void onTypefaceRequestFailed(int);
+    method public abstract void onTypefaceRetrieved(android.graphics.Typeface);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0
+  }
+
   public class Xfermode {
     ctor public Xfermode();
   }
@@ -13506,6 +13716,23 @@
     method public void addLevel(int, int, android.graphics.drawable.Drawable);
   }
 
+  public class MaskableIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public MaskableIconDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.drawable.Drawable getBackground();
+    method public android.graphics.drawable.Drawable getForeground();
+    method public android.graphics.Path getIconMask();
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setOpacity(int);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+    field public static final float DEFAULT_VIEW_PORT_SCALE = 0.6666667f;
+    field public static final float MASK_SIZE = 100.0f;
+  }
+
   public class NinePatchDrawable extends android.graphics.drawable.Drawable {
     ctor public deprecated NinePatchDrawable(android.graphics.Bitmap, byte[], android.graphics.Rect, java.lang.String);
     ctor public NinePatchDrawable(android.content.res.Resources, android.graphics.Bitmap, byte[], android.graphics.Rect, java.lang.String);
@@ -13654,6 +13881,19 @@
 
 }
 
+package android.graphics.fonts {
+
+  public final class FontRequest implements android.os.Parcelable {
+    ctor public FontRequest(java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public java.lang.String getProviderAuthority();
+    method public java.lang.String getQuery();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
+  }
+
+}
+
 package android.graphics.pdf {
 
   public class PdfDocument {
@@ -13981,9 +14221,42 @@
     method public float getZ();
   }
 
+  public final class HardwareBuffer implements android.os.Parcelable {
+    method public static android.hardware.HardwareBuffer create(int, int, int, int, long);
+    method public int describeContents();
+    method public void destroy();
+    method public int getFormat();
+    method public int getHeight();
+    method public int getLayers();
+    method public long getUsage();
+    method public int getWidth();
+    method public boolean isDestroyed();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BLOB = 33; // 0x21
+    field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR;
+    field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_FP16 = 22; // 0x16
+    field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGB_565 = 4; // 0x4
+    field public static final int RGB_888 = 3; // 0x3
+    field public static final long USAGE0_CPU_READ = 2L; // 0x2L
+    field public static final long USAGE0_CPU_READ_OFTEN = 6L; // 0x6L
+    field public static final long USAGE0_CPU_WRITE = 32L; // 0x20L
+    field public static final long USAGE0_CPU_WRITE_OFTEN = 96L; // 0x60L
+    field public static final long USAGE0_GPU_COLOR_OUTPUT = 2048L; // 0x800L
+    field public static final long USAGE0_GPU_CUBEMAP = 8192L; // 0x2000L
+    field public static final long USAGE0_GPU_DATA_BUFFER = 16384L; // 0x4000L
+    field public static final long USAGE0_GPU_SAMPLED_IMAGE = 1024L; // 0x400L
+    field public static final long USAGE0_GPU_STORAGE_IMAGE = 3072L; // 0xc00L
+    field public static final long USAGE0_PROTECTED_CONTENT = 262144L; // 0x40000L
+    field public static final long USAGE0_SENSOR_DIRECT_DATA = 536870912L; // 0x20000000L
+    field public static final long USAGE0_VIDEO_ENCODE = 2097152L; // 0x200000L
+  }
+
   public final class Sensor {
     method public int getFifoMaxEventCount();
     method public int getFifoReservedEventCount();
+    method public int getHighestDirectReportRateLevel();
     method public int getId();
     method public int getMaxDelay();
     method public float getMaximumRange();
@@ -13997,6 +14270,7 @@
     method public java.lang.String getVendor();
     method public int getVersion();
     method public boolean isAdditionalInfoSupported();
+    method public boolean isDirectChannelTypeSupported(int);
     method public boolean isDynamicSensor();
     method public boolean isWakeUpSensor();
     field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
@@ -14074,6 +14348,17 @@
     field public final int type;
   }
 
+  public final class SensorDirectChannel implements java.lang.AutoCloseable {
+    method public void close();
+    method public boolean isValid();
+    field public static final int RATE_FAST = 2; // 0x2
+    field public static final int RATE_NORMAL = 1; // 0x1
+    field public static final int RATE_STOP = 0; // 0x0
+    field public static final int RATE_VERY_FAST = 3; // 0x3
+    field public static final int TYPE_ASHMEM = 1; // 0x1
+    field public static final int TYPE_HARDWARE_BUFFER = 2; // 0x2
+  }
+
   public class SensorEvent {
     field public int accuracy;
     field public android.hardware.Sensor sensor;
@@ -14105,6 +14390,9 @@
 
   public abstract class SensorManager {
     method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+    method public int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
+    method public android.hardware.SensorDirectChannel createDirectChannel(android.os.MemoryFile);
+    method public android.hardware.SensorDirectChannel createDirectChannel(android.hardware.HardwareBuffer);
     method public boolean flush(android.hardware.SensorEventListener);
     method public static float getAltitude(float, float);
     method public static void getAngleChange(float[], float[], float[]);
@@ -14234,7 +14522,7 @@
     method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close();
-    method public abstract void finishDeferredConfiguration(java.util.List<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void finalizeOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CameraDevice getDevice();
     method public abstract android.view.Surface getInputSurface();
     method public abstract boolean isReprocessable();
@@ -14649,6 +14937,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_CAPTURE_INTENT;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> CONTROL_ENABLE_ZSL;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SCENE_MODE;
@@ -14728,6 +15017,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AWB_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_CAPTURE_INTENT;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_ENABLE_ZSL;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE;
@@ -14875,10 +15165,12 @@
     ctor public OutputConfiguration(android.view.Surface);
     ctor public OutputConfiguration(int, android.view.Surface);
     ctor public OutputConfiguration(android.util.Size, java.lang.Class<T>);
+    method public void addSurface(android.view.Surface);
     method public int describeContents();
+    method public void enableSurfaceSharing();
     method public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
-    method public void setDeferredSurface(android.view.Surface);
+    method public java.util.List<android.view.Surface> getSurfaces();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -17428,6 +17720,15 @@
     method public boolean isTransitionalDifferent();
   }
 
+  public final class ListFormatter {
+    method public java.lang.String format(java.lang.Object...);
+    method public java.lang.String format(java.util.Collection<?>);
+    method public static android.icu.text.ListFormatter getInstance(android.icu.util.ULocale);
+    method public static android.icu.text.ListFormatter getInstance(java.util.Locale);
+    method public static android.icu.text.ListFormatter getInstance();
+    method public java.lang.String getPatternForNumItems(int);
+  }
+
   public abstract class LocaleDisplayNames {
     method public abstract android.icu.text.DisplayContext getContext(android.icu.text.DisplayContext.Type);
     method public abstract android.icu.text.LocaleDisplayNames.DialectHandling getDialectHandling();
@@ -17437,6 +17738,8 @@
     method public static android.icu.text.LocaleDisplayNames getInstance(android.icu.util.ULocale, android.icu.text.DisplayContext...);
     method public static android.icu.text.LocaleDisplayNames getInstance(java.util.Locale, android.icu.text.DisplayContext...);
     method public abstract android.icu.util.ULocale getLocale();
+    method public java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiList(java.util.Set<android.icu.util.ULocale>, boolean, java.util.Comparator<java.lang.Object>);
+    method public abstract java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiListCompareWholeItems(java.util.Set<android.icu.util.ULocale>, java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem>);
     method public abstract java.lang.String keyDisplayName(java.lang.String);
     method public abstract java.lang.String keyValueDisplayName(java.lang.String, java.lang.String);
     method public abstract java.lang.String languageDisplayName(java.lang.String);
@@ -17456,9 +17759,19 @@
     enum_constant public static final android.icu.text.LocaleDisplayNames.DialectHandling STANDARD_NAMES;
   }
 
+  public static class LocaleDisplayNames.UiListItem {
+    ctor public LocaleDisplayNames.UiListItem(android.icu.util.ULocale, android.icu.util.ULocale, java.lang.String, java.lang.String);
+    method public static java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem> getComparator(java.util.Comparator<java.lang.Object>, boolean);
+    field public final android.icu.util.ULocale minimized;
+    field public final android.icu.util.ULocale modified;
+    field public final java.lang.String nameInDisplayLocale;
+    field public final java.lang.String nameInSelf;
+  }
+
   public class MeasureFormat extends android.icu.text.UFormat {
     method public final boolean equals(java.lang.Object);
     method public java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
+    method public java.lang.StringBuilder formatMeasurePerUnit(android.icu.util.Measure, android.icu.util.MeasureUnit, java.lang.StringBuilder, java.text.FieldPosition);
     method public final java.lang.String formatMeasures(android.icu.util.Measure...);
     method public java.lang.StringBuilder formatMeasures(java.lang.StringBuilder, java.text.FieldPosition, android.icu.util.Measure...);
     method public static android.icu.text.MeasureFormat getCurrencyFormat(android.icu.util.ULocale);
@@ -17927,6 +18240,14 @@
     method public void setUpperCaseFirst(boolean);
   }
 
+  public final class ScientificNumberFormatter {
+    method public java.lang.String format(java.lang.Object);
+    method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.util.ULocale, java.lang.String, java.lang.String);
+    method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.text.DecimalFormat, java.lang.String, java.lang.String);
+    method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.util.ULocale);
+    method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.text.DecimalFormat);
+  }
+
   public abstract class SearchIterator {
     ctor protected SearchIterator(java.text.CharacterIterator, android.icu.text.BreakIterator);
     method public final int first();
@@ -18702,6 +19023,34 @@
     method public long getToDate();
   }
 
+  public final class EthiopicCalendar extends android.icu.util.CECalendar {
+    ctor public EthiopicCalendar();
+    ctor public EthiopicCalendar(android.icu.util.TimeZone);
+    ctor public EthiopicCalendar(java.util.Locale);
+    ctor public EthiopicCalendar(android.icu.util.ULocale);
+    ctor public EthiopicCalendar(android.icu.util.TimeZone, java.util.Locale);
+    ctor public EthiopicCalendar(android.icu.util.TimeZone, android.icu.util.ULocale);
+    ctor public EthiopicCalendar(int, int, int);
+    ctor public EthiopicCalendar(java.util.Date);
+    ctor public EthiopicCalendar(int, int, int, int, int, int);
+    method protected deprecated int handleGetExtendedYear();
+    method public boolean isAmeteAlemEra();
+    method public void setAmeteAlemEra(boolean);
+    field public static final int GENBOT = 8; // 0x8
+    field public static final int HAMLE = 10; // 0xa
+    field public static final int HEDAR = 2; // 0x2
+    field public static final int MEGABIT = 6; // 0x6
+    field public static final int MESKEREM = 0; // 0x0
+    field public static final int MIAZIA = 7; // 0x7
+    field public static final int NEHASSE = 11; // 0xb
+    field public static final int PAGUMEN = 12; // 0xc
+    field public static final int SENE = 9; // 0x9
+    field public static final int TAHSAS = 3; // 0x3
+    field public static final int TEKEMT = 1; // 0x1
+    field public static final int TER = 4; // 0x4
+    field public static final int YEKATIT = 5; // 0x5
+  }
+
   public abstract interface Freezable<T> implements java.lang.Cloneable {
     method public abstract T cloneAsThawed();
     method public abstract T freeze();
@@ -19230,6 +19579,35 @@
     enum_constant public static final android.icu.util.ULocale.Category FORMAT;
   }
 
+  public final class UniversalTimeScale {
+    method public static android.icu.math.BigDecimal bigDecimalFrom(double, int);
+    method public static android.icu.math.BigDecimal bigDecimalFrom(long, int);
+    method public static android.icu.math.BigDecimal bigDecimalFrom(android.icu.math.BigDecimal, int);
+    method public static long from(long, int);
+    method public static long getTimeScaleValue(int, int);
+    method public static android.icu.math.BigDecimal toBigDecimal(long, int);
+    method public static android.icu.math.BigDecimal toBigDecimal(android.icu.math.BigDecimal, int);
+    method public static long toLong(long, int);
+    field public static final int DB2_TIME = 8; // 0x8
+    field public static final int DOTNET_DATE_TIME = 4; // 0x4
+    field public static final int EPOCH_OFFSET_PLUS_1_VALUE = 6; // 0x6
+    field public static final int EPOCH_OFFSET_VALUE = 1; // 0x1
+    field public static final int EXCEL_TIME = 7; // 0x7
+    field public static final int FROM_MAX_VALUE = 3; // 0x3
+    field public static final int FROM_MIN_VALUE = 2; // 0x2
+    field public static final int ICU4C_TIME = 2; // 0x2
+    field public static final int JAVA_TIME = 0; // 0x0
+    field public static final int MAC_OLD_TIME = 5; // 0x5
+    field public static final int MAC_TIME = 6; // 0x6
+    field public static final int MAX_SCALE = 10; // 0xa
+    field public static final int TO_MAX_VALUE = 5; // 0x5
+    field public static final int TO_MIN_VALUE = 4; // 0x4
+    field public static final int UNITS_VALUE = 0; // 0x0
+    field public static final int UNIX_MICROSECONDS_TIME = 9; // 0x9
+    field public static final int UNIX_TIME = 1; // 0x1
+    field public static final int WINDOWS_FILE_TIME = 3; // 0x3
+  }
+
   public abstract interface ValueIterator {
     method public abstract boolean next(android.icu.util.ValueIterator.Element);
     method public abstract void reset();
@@ -19686,6 +20064,7 @@
     method public double getAccumulatedDeltaRangeMeters();
     method public int getAccumulatedDeltaRangeState();
     method public double getAccumulatedDeltaRangeUncertaintyMeters();
+    method public double getAutomaticGainControlLevelInDb();
     method public long getCarrierCycles();
     method public float getCarrierFrequencyHz();
     method public double getCarrierPhase();
@@ -19701,6 +20080,7 @@
     method public int getState();
     method public int getSvid();
     method public double getTimeOffsetNanos();
+    method public boolean hasAutomaticGainControlLevelInDb();
     method public boolean hasCarrierCycles();
     method public boolean hasCarrierFrequencyHz();
     method public boolean hasCarrierPhase();
@@ -19724,11 +20104,13 @@
     field public static final int STATE_GAL_E1C_2ND_CODE_LOCK = 2048; // 0x800
     field public static final int STATE_GLO_STRING_SYNC = 64; // 0x40
     field public static final int STATE_GLO_TOD_DECODED = 128; // 0x80
+    field public static final int STATE_GLO_TOD_KNOWN = 32768; // 0x8000
     field public static final int STATE_MSEC_AMBIGUOUS = 16; // 0x10
     field public static final int STATE_SBAS_SYNC = 8192; // 0x2000
     field public static final int STATE_SUBFRAME_SYNC = 4; // 0x4
     field public static final int STATE_SYMBOL_SYNC = 32; // 0x20
     field public static final int STATE_TOW_DECODED = 8; // 0x8
+    field public static final int STATE_TOW_KNOWN = 16384; // 0x4000
     field public static final int STATE_UNKNOWN = 0; // 0x0
   }
 
@@ -19785,12 +20167,14 @@
 
   public final class GnssStatus {
     method public float getAzimuthDegrees(int);
+    method public float getCarrierFrequencyHz(int);
     method public float getCn0DbHz(int);
     method public int getConstellationType(int);
     method public float getElevationDegrees(int);
     method public int getSatelliteCount();
     method public int getSvid(int);
     method public boolean hasAlmanacData(int);
+    method public boolean hasCarrierFrequency(int);
     method public boolean hasEphemerisData(int);
     method public boolean usedInFix(int);
     field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
@@ -19851,34 +20235,46 @@
     method public float getAccuracy();
     method public double getAltitude();
     method public float getBearing();
+    method public float getBearingAccuracyDegrees();
     method public long getElapsedRealtimeNanos();
     method public android.os.Bundle getExtras();
     method public double getLatitude();
     method public double getLongitude();
     method public java.lang.String getProvider();
     method public float getSpeed();
+    method public float getSpeedAccuracyMetersPerSecond();
     method public long getTime();
+    method public float getVerticalAccuracyMeters();
     method public boolean hasAccuracy();
     method public boolean hasAltitude();
     method public boolean hasBearing();
+    method public boolean hasBearingAccuracy();
     method public boolean hasSpeed();
+    method public boolean hasSpeedAccuracy();
+    method public boolean hasVerticalAccuracy();
     method public boolean isFromMockProvider();
     method public void removeAccuracy();
     method public void removeAltitude();
     method public void removeBearing();
+    method public void removeBearingAccuracy();
     method public void removeSpeed();
+    method public void removeSpeedAccuracy();
+    method public void removeVerticalAccuracy();
     method public void reset();
     method public void set(android.location.Location);
     method public void setAccuracy(float);
     method public void setAltitude(double);
     method public void setBearing(float);
+    method public void setBearingAccuracyDegrees(float);
     method public void setElapsedRealtimeNanos(long);
     method public void setExtras(android.os.Bundle);
     method public void setLatitude(double);
     method public void setLongitude(double);
     method public void setProvider(java.lang.String);
     method public void setSpeed(float);
+    method public void setSpeedAccuracyMetersPerSecond(float);
     method public void setTime(long);
+    method public void setVerticalAccuracyMeters(float);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.Location> CREATOR;
     field public static final int FORMAT_DEGREES = 0; // 0x0
@@ -20010,7 +20406,7 @@
     field public static final android.os.Parcelable.Creator<android.media.AudioAttributes> CREATOR;
     field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
     field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
-    field public static final int FLAG_LOW_LATENCY = 256; // 0x100
+    field public static final deprecated int FLAG_LOW_LATENCY = 256; // 0x100
     field public static final int USAGE_ALARM = 4; // 0x4
     field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
     field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
@@ -20451,6 +20847,7 @@
     method protected deprecated int getNativeFrameCount();
     method public static int getNativeOutputSampleRate(int);
     method public int getNotificationMarkerPosition();
+    method public int getPerformanceMode();
     method public int getPlayState();
     method public int getPlaybackHeadPosition();
     method public android.media.PlaybackParams getPlaybackParams();
@@ -20497,6 +20894,9 @@
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
+    field public static final int PERFORMANCE_MODE_LOW_LATENCY = 1; // 0x1
+    field public static final int PERFORMANCE_MODE_NONE = 0; // 0x0
+    field public static final int PERFORMANCE_MODE_POWER_SAVING = 2; // 0x2
     field public static final int PLAYSTATE_PAUSED = 2; // 0x2
     field public static final int PLAYSTATE_PLAYING = 3; // 0x3
     field public static final int PLAYSTATE_STOPPED = 1; // 0x1
@@ -20514,6 +20914,7 @@
     method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioTrack.Builder setPerformanceMode(int);
     method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
   }
@@ -20528,6 +20929,40 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public final class BufferingParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getInitialBufferingMode();
+    method public int getInitialBufferingWatermarkKB();
+    method public int getInitialBufferingWatermarkMs();
+    method public int getRebufferingMode();
+    method public int getRebufferingWatermarkHighKB();
+    method public int getRebufferingWatermarkHighMs();
+    method public int getRebufferingWatermarkLowKB();
+    method public int getRebufferingWatermarkLowMs();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUFFERING_MODE_NONE = 0; // 0x0
+    field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2
+    field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1
+    field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
+  }
+
+  public static class BufferingParams.Builder {
+    ctor public BufferingParams.Builder();
+    ctor public BufferingParams.Builder(android.media.BufferingParams);
+    method public android.media.BufferingParams build();
+    method public android.media.BufferingParams.Builder setInitialBufferingMode(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingMode(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -21672,6 +22107,7 @@
 
   public final class MediaMuxer {
     ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
+    ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
     method public int addTrack(android.media.MediaFormat);
     method public void release();
     method public void setLocation(float, float);
@@ -21700,7 +22136,9 @@
     method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
     method public void deselectTrack(int) throws java.lang.IllegalStateException;
     method public int getAudioSessionId();
+    method public android.media.BufferingParams getBufferingParams();
     method public int getCurrentPosition();
+    method public android.media.BufferingParams getDefaultBufferingParams();
     method public int getDuration();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -21723,6 +22161,7 @@
     method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public deprecated void setAudioStreamType(int);
     method public void setAuxEffectSendLevel(float);
+    method public void setBufferingParams(android.media.BufferingParams);
     method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
@@ -21848,6 +22287,8 @@
     method public void setLocation(float, float);
     method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
     method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
+    method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
+    method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
     method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
     method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
     method public void setOrientationHint(int);
@@ -21866,7 +22307,9 @@
     field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
     field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
+    field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322
     field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321
+    field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323
     field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1
   }
 
@@ -23381,6 +23824,7 @@
     field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
     field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
     field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_PREVIEW = "TYPE_PREVIEW";
     field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
     field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
     field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
@@ -23408,6 +23852,7 @@
 
   public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns {
     field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
     field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
     field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
     field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
@@ -23416,14 +23861,24 @@
     field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
     field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
     field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_DURATION = "preview_duration";
+    field public static final java.lang.String COLUMN_PREVIEW_INTENT_URI = "preview_intent_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_LAST_PLAYBACK_POSITION = "preview_last_playback_position";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_WEIGHT = "preview_weight";
     field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
     field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
     field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -23438,6 +23893,16 @@
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS";
+    field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS";
+    field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES";
+    field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS";
+    field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS";
+    field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS";
+    field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS";
+    field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE";
+    field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS";
+    field public static final java.lang.String REVIEW_RATING_STYLE_THUMPS_UP_DOWN = "REVIEW_RATING_STYLE_THUMPS_UP_DOWN";
   }
 
   public static final class TvContract.Programs.Genres {
@@ -23548,9 +24013,13 @@
     method public void unregisterCallback(android.media.tv.TvInputManager.TvInputCallback);
     method public void updateTvInputInfo(android.media.tv.TvInputInfo);
     field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
+    field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE";
     field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
     field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
     field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
+    field public static final java.lang.String ACTION_VIEW_RECORDING_SCHEDULES = "android.media.tv.action.VIEW_RECORDING_SCHEDULES";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
     field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -24388,6 +24857,7 @@
   public class TrafficStats {
     ctor public TrafficStats();
     method public static void clearThreadStatsTag();
+    method public static int getAndSetThreadStatsTag(int);
     method public static long getMobileRxBytes();
     method public static long getMobileRxPackets();
     method public static long getMobileTxBytes();
@@ -24997,7 +25467,9 @@
   public class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
+    method public android.net.ProxyInfo getHttpProxy();
     method public boolean isPasspoint();
+    method public void setHttpProxy(android.net.ProxyInfo);
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
     field public java.lang.String FQDN;
@@ -29607,6 +30079,7 @@
     method public final android.util.SizeF readSizeF();
     method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
     method public final android.util.SparseBooleanArray readSparseBooleanArray();
+    method public final android.util.SparseIntArray readSparseIntArray();
     method public final java.lang.String readString();
     method public final void readStringArray(java.lang.String[]);
     method public final void readStringList(java.util.List<java.lang.String>);
@@ -29651,6 +30124,7 @@
     method public final void writeSizeF(android.util.SizeF);
     method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
     method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
+    method public final void writeSparseIntArray(android.util.SparseIntArray);
     method public final void writeString(java.lang.String);
     method public final void writeStringArray(java.lang.String[]);
     method public final void writeStringList(java.util.List<java.lang.String>);
@@ -29844,6 +30318,15 @@
     field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
   }
 
+  public abstract class ProxyFileDescriptorCallback {
+    ctor public ProxyFileDescriptorCallback();
+    method public void onFsync() throws android.system.ErrnoException;
+    method public long onGetSize() throws android.system.ErrnoException;
+    method public int onRead(long, int, byte[]) throws android.system.ErrnoException;
+    method public abstract void onRelease();
+    method public int onWrite(long, int, byte[]) throws android.system.ErrnoException;
+  }
+
   public class RecoverySystem {
     method public static void installPackage(android.content.Context, java.io.File) throws java.io.IOException;
     method public static void rebootWipeCache(android.content.Context) throws java.io.IOException;
@@ -29958,6 +30441,7 @@
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
+    method public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
@@ -30254,13 +30738,22 @@
   }
 
   public class StorageManager {
+    method public long getCacheQuotaBytes();
+    method public long getCacheSizeBytes();
+    method public long getExternalCacheQuotaBytes();
+    method public long getExternalCacheSizeBytes();
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
+    method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
+    method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
+    method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
+    method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
     field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
   }
@@ -30388,6 +30881,7 @@
     method public android.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
     method public android.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
     method public int getOrder();
+    method public android.preference.PreferenceGroup getParent();
     method protected boolean getPersistedBoolean(boolean);
     method protected float getPersistedFloat(float);
     method protected int getPersistedInt(int);
@@ -32358,7 +32852,7 @@
     ctor public ContactsContract.Intents();
     field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS";
     field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
-    field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
+    field public static final deprecated java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
     field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
     field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
@@ -32468,8 +32962,10 @@
   public static final class ContactsContract.ProviderStatus {
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/provider_status";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DATABASE_CREATION_TIMESTAMP = "database_creation_timestamp";
     field public static final java.lang.String STATUS = "status";
     field public static final int STATUS_BUSY = 1; // 0x1
+    field public static final android.net.Uri STATUS_CHANGE_NOTIFICATION_CONTENT_URI;
     field public static final int STATUS_EMPTY = 2; // 0x2
     field public static final int STATUS_NORMAL = 0; // 0x0
   }
@@ -32615,6 +33111,7 @@
     method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
     method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
+    method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle);
     method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
     method public static java.util.List<java.lang.String> findDocumentPath(android.content.ContentResolver, android.net.Uri);
     method public static java.lang.String getDocumentId(android.net.Uri);
@@ -32659,6 +33156,7 @@
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
     field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
+    field public static final int FLAG_WEB_LINKABLE = 4096; // 0x1000
     field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
   }
 
@@ -32692,6 +33190,7 @@
     ctor public DocumentsProvider();
     method public java.lang.String copyDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public java.lang.String createDocument(java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+    method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException;
     method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
@@ -32711,7 +33210,6 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public android.content.res.AssetFileDescriptor openTypedDocument(java.lang.String, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
-    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public abstract android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
     method public android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], android.os.Bundle) throws java.io.FileNotFoundException;
@@ -32725,6 +33223,16 @@
     method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
+  public class FontsContract {
+  }
+
+  public static final class FontsContract.Columns implements android.provider.BaseColumns {
+    ctor public FontsContract.Columns();
+    field public static final java.lang.String STYLE = "font_style";
+    field public static final java.lang.String TTC_INDEX = "font_ttc_index";
+    field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
+  }
+
   public final deprecated class LiveFolders implements android.provider.BaseColumns {
     field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
     field public static final java.lang.String DESCRIPTION = "description";
@@ -33090,6 +33598,7 @@
     field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
     field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
     field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
+    field public static final java.lang.String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
     field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
     field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
     field public static final java.lang.String ACTION_DEVICE_INFO_SETTINGS = "android.settings.DEVICE_INFO_SETTINGS";
@@ -33107,6 +33616,7 @@
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
+    field public static final java.lang.String ACTION_MANAGE_EXTERNAL_SOURCES = "android.settings.action.MANAGE_EXTERNAL_SOURCES";
     field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
     field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
     field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -33142,8 +33652,10 @@
     field public static final java.lang.String AUTHORITY = "settings";
     field public static final java.lang.String EXTRA_ACCOUNT_TYPES = "account_types";
     field public static final java.lang.String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled";
+    field public static final java.lang.String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
     field public static final java.lang.String EXTRA_AUTHORITIES = "authorities";
     field public static final java.lang.String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
     field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
     field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
     field public static final java.lang.String EXTRA_INPUT_METHOD_ID = "input_method_id";
@@ -33252,7 +33764,7 @@
     field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
     field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
     field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
-    field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+    field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String LOCATION_MODE = "location_mode";
     field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
     field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
@@ -33807,6 +34319,8 @@
 
   public static final class VoicemailContract.Voicemails implements android.provider.BaseColumns android.provider.OpenableColumns {
     method public static android.net.Uri buildSourceUri(java.lang.String);
+    field public static final java.lang.String ARCHIVED = "archived";
+    field public static final java.lang.String BACKED_UP = "backed_up";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATE = "date";
     field public static final java.lang.String DELETED = "deleted";
@@ -33814,6 +34328,7 @@
     field public static final java.lang.String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String HAS_CONTENT = "has_content";
+    field public static final java.lang.String IS_OMTP_VOICEMAIL = "is_omtp_voicemail";
     field public static final java.lang.String IS_READ = "is_read";
     field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
     field public static final java.lang.String LAST_MODIFIED = "last_modified";
@@ -33821,6 +34336,7 @@
     field public static final java.lang.String NUMBER = "number";
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
     field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
+    field public static final java.lang.String RESTORED = "restored";
     field public static final java.lang.String SOURCE_DATA = "source_data";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String TRANSCRIPTION = "transcription";
@@ -35144,6 +35660,7 @@
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
     field public static final int PURPOSE_VERIFY = 8; // 0x8
+    field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
   }
@@ -35197,16 +35714,25 @@
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
+    method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
     method public void onDisconnected();
     method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
+    field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
+    field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
+    field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
+    field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
+    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
   public final class FillCallback {
+    method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
     method public void onFailure(java.lang.CharSequence);
+    method public void onFillResponseAuthentication(int);
     method public void onSuccess(android.view.autofill.FillResponse);
   }
 
@@ -35408,7 +35934,6 @@
     field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
     field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
     field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-    field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
   }
 
   public class MediaBrowserService.Result<T> {
@@ -35492,6 +36017,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
+    method public final void unsnoozeNotification(java.lang.String);
     method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
@@ -35507,6 +36033,7 @@
     method public final int getCurrentInterruptionFilter();
     method public final int getCurrentListenerHints();
     method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
+    method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
@@ -35525,8 +36052,6 @@
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void snoozeNotification(java.lang.String, java.lang.String);
     method public final void snoozeNotification(java.lang.String, long);
-    method public final void snoozeNotification(java.lang.String);
-    method public final void unsnoozeNotification(java.lang.String);
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
@@ -35551,9 +36076,9 @@
     field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
     field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
     field public static final int REASON_SNOOZED = 18; // 0x12
+    field public static final int REASON_TIMEOUT = 19; // 0x13
     field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10
     field public static final int REASON_USER_STOPPED = 6; // 0x6
-    field public static final int REASON_USER_SWITCH = 19; // 0x13
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
     field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2
@@ -35561,6 +36086,7 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public boolean canShowBadge();
     method public java.util.List<java.lang.String> getAdditionalPeople();
     method public android.app.NotificationChannel getChannel();
     method public int getImportance();
@@ -35602,7 +36128,6 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
-    method public android.app.NotificationChannel getNotificationChannel();
     method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
@@ -36018,6 +36543,7 @@
     method public abstract int getMaxBufferSize();
     method public abstract boolean hasFinished();
     method public abstract boolean hasStarted();
+    method public default void rangeStart(int, int, int);
     method public abstract int start(int, int, int);
   }
 
@@ -36170,6 +36696,7 @@
     method public void onError(java.lang.String, int);
     method public abstract void onStart(java.lang.String);
     method public void onStop(java.lang.String, boolean);
+    method public void onUtteranceRangeStart(java.lang.String, int, int);
   }
 
   public class Voice implements android.os.Parcelable {
@@ -37082,6 +37609,7 @@
     method public void onReject();
     method public void onReject(java.lang.String);
     method public void onSeparate();
+    method public void onShowIncomingCallUi();
     method public void onStateChanged(int);
     method public void onStopDtmfTone();
     method public void onUnhold();
@@ -37093,6 +37621,7 @@
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
     method public final void setAudioModeIsVoip(boolean);
+    method public final void setAudioRoute(int);
     method public final void setCallerDisplayName(java.lang.String, int);
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -37141,6 +37670,7 @@
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+    field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
     field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -37208,7 +37738,9 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
     method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
     method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -37321,6 +37853,7 @@
     field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
     field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
+    field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
     field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -37502,6 +38035,8 @@
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
+    method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+    method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
     method public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
@@ -37512,7 +38047,7 @@
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
     field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
-    field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+    field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -37615,7 +38150,8 @@
     field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
     field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
     field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
-    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+    field public static final deprecated java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY = "carrier_vvm_package_name_string_array";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
     field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
     field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
@@ -37626,6 +38162,7 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
+    field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
@@ -37707,9 +38244,13 @@
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
     field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL = "vvm_cellular_data_required_bool";
+    field public static final java.lang.String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
     field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
+    field public static final java.lang.String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY = "vvm_disabled_capabilities_string_array";
+    field public static final java.lang.String KEY_VVM_LEGACY_MODE_ENABLED_BOOL = "vvm_legacy_mode_enabled_bool";
     field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
     field public static final java.lang.String KEY_VVM_PREFETCH_BOOL = "vvm_prefetch_bool";
+    field public static final java.lang.String KEY_VVM_SSL_ENABLED_BOOL = "vvm_ssl_enabled_bool";
     field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
     field public static final java.lang.String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
   }
@@ -38237,6 +38778,7 @@
     method public int getSimState();
     method public int getSimState(int);
     method public java.lang.String getSubscriberId();
+    method public java.lang.String getVisualVoicemailPackageName(android.telecom.PhoneAccountHandle);
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
     method public int getVoiceNetworkType();
@@ -38248,6 +38790,7 @@
     method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String);
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
+    method public boolean isConcurrentVoiceAndDataAllowed();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
     method public boolean isSmsCapable();
@@ -38288,6 +38831,7 @@
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+    field public static final java.lang.String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
     field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
     field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
     field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
@@ -38296,6 +38840,7 @@
     field public static final java.lang.String EXTRA_STATE_OFFHOOK;
     field public static final java.lang.String EXTRA_STATE_RINGING;
     field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
+    field public static final java.lang.String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
     field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
     field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -38841,6 +39386,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.Context createDeviceProtectedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -38925,6 +39471,7 @@
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public android.content.ComponentName startService(android.content.Intent);
+    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -38987,6 +39534,7 @@
     method public boolean addPermission(android.content.pm.PermissionInfo);
     method public boolean addPermissionAsync(android.content.pm.PermissionInfo);
     method public void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
+    method public boolean canRequestPackageInstalls();
     method public java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]);
     method public int checkPermission(java.lang.String, java.lang.String);
     method public int checkSignatures(java.lang.String, java.lang.String);
@@ -39025,6 +39573,7 @@
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInstaller getPackageInstaller();
     method public int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] getPackagesForUid(int);
@@ -39039,6 +39588,7 @@
     method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo);
     method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
     method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public java.lang.String[] getSystemSharedLibraryNames();
     method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -39268,6 +39818,65 @@
     method public android.text.Editable newEditable(java.lang.CharSequence);
   }
 
+  public final class FontConfig implements android.os.Parcelable {
+    ctor public FontConfig();
+    ctor public FontConfig(android.text.FontConfig);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Alias> getAliases();
+    method public java.util.List<android.text.FontConfig.Family> getFamilies();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
+  }
+
+  public static final class FontConfig.Alias implements android.os.Parcelable {
+    ctor public FontConfig.Alias(java.lang.String, java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getName();
+    method public java.lang.String getToName();
+    method public int getWeight();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Alias> CREATOR;
+  }
+
+  public static final class FontConfig.Axis implements android.os.Parcelable {
+    ctor public FontConfig.Axis(int, float);
+    method public int describeContents();
+    method public float getStyleValue();
+    method public int getTag();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Axis> CREATOR;
+  }
+
+  public static final class FontConfig.Family implements android.os.Parcelable {
+    ctor public FontConfig.Family(java.lang.String, java.util.List<android.text.FontConfig.Font>, java.lang.String, java.lang.String);
+    ctor public FontConfig.Family(android.text.FontConfig.Family);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Font> getFonts();
+    method public java.lang.String getLanguage();
+    method public java.lang.String getName();
+    method public java.lang.String getVariant();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR;
+  }
+
+  public static final class FontConfig.Font implements android.os.Parcelable {
+    ctor public FontConfig.Font(java.lang.String, int, java.util.List<android.text.FontConfig.Axis>, int, boolean);
+    ctor public FontConfig.Font(android.text.FontConfig.Font);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Axis> getAxes();
+    method public android.os.ParcelFileDescriptor getFd();
+    method public java.lang.String getFontName();
+    method public int getTtcIndex();
+    method public int getWeight();
+    method public boolean isItalic();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Font> CREATOR;
+  }
+
+  public final class FontManager {
+    method public android.text.FontConfig getSystemFonts();
+  }
+
   public abstract interface GetChars implements java.lang.CharSequence {
     method public abstract void getChars(int, int, char[], int);
   }
@@ -39623,22 +40232,6 @@
     method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
   }
 
-  public abstract interface TextAssistant {
-    method public abstract void addLinks(android.text.Spannable, int);
-    method public abstract android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int);
-  }
-
-  public class TextClassification {
-    ctor public TextClassification();
-    method public java.util.Map<java.lang.String, java.lang.Float> getTypeConfidence();
-  }
-
-  public final class TextClassificationManager implements android.text.TextAssistant {
-    method public void addLinks(android.text.Spannable, int);
-    method public java.util.List<android.text.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int);
-  }
-
   public abstract interface TextDirectionHeuristic {
     method public abstract boolean isRtl(char[], int, int);
     method public abstract boolean isRtl(java.lang.CharSequence, int, int);
@@ -39654,13 +40247,6 @@
     field public static final android.text.TextDirectionHeuristic RTL;
   }
 
-  public final class TextLanguage {
-    ctor public TextLanguage(int, int, java.util.Map<java.lang.String, java.lang.Float>);
-    method public int getEndIndex();
-    method public java.util.Map<java.lang.String, java.lang.Float> getLanguageConfidence();
-    method public int getStartIndex();
-  }
-
   public class TextPaint extends android.graphics.Paint {
     ctor public TextPaint();
     ctor public TextPaint(int);
@@ -39673,13 +40259,6 @@
     field public int linkColor;
   }
 
-  public class TextSelection {
-    ctor public TextSelection();
-    method public int getSelectionEndIndex();
-    method public int getSelectionStartIndex();
-    method public android.text.TextClassification getTextClassification();
-  }
-
   public class TextUtils {
     method public static deprecated java.lang.CharSequence commaEllipsize(java.lang.CharSequence, android.text.TextPaint, float, java.lang.String, java.lang.String);
     method public static java.lang.CharSequence concat(java.lang.CharSequence...);
@@ -41933,7 +42512,9 @@
     method public android.view.Display.Mode[] getSupportedModes();
     method public deprecated float[] getSupportedRefreshRates();
     method public deprecated int getWidth();
+    method public boolean isHdr();
     method public boolean isValid();
+    method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
@@ -42002,7 +42583,7 @@
     method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
     method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
     method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int);
-    method public android.view.View findNextKeyboardNavigationGroup(int, android.view.View, android.view.View, int);
+    method public android.view.View findNextKeyboardNavigationCluster(android.view.View, android.view.View, int);
     method public static android.view.FocusFinder getInstance();
   }
 
@@ -42185,6 +42766,7 @@
     field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
     field public static final int SOURCE_KEYBOARD = 257; // 0x101
     field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_MOUSE_RELATIVE = 131076; // 0x20004
     field public static final int SOURCE_STYLUS = 16386; // 0x4002
     field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
     field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
@@ -42787,7 +43369,7 @@
     method public abstract android.view.SubMenu getSubMenu();
     method public abstract java.lang.CharSequence getTitle();
     method public abstract java.lang.CharSequence getTitleCondensed();
-    method public default java.lang.CharSequence getTooltip();
+    method public default java.lang.CharSequence getTooltipText();
     method public abstract boolean hasSubMenu();
     method public abstract boolean isActionViewExpanded();
     method public abstract boolean isCheckable();
@@ -42814,7 +43396,7 @@
     method public abstract android.view.MenuItem setTitle(java.lang.CharSequence);
     method public abstract android.view.MenuItem setTitle(int);
     method public abstract android.view.MenuItem setTitleCondensed(java.lang.CharSequence);
-    method public default android.view.MenuItem setTooltip(java.lang.CharSequence);
+    method public default android.view.MenuItem setTooltipText(java.lang.CharSequence);
     method public abstract android.view.MenuItem setVisible(boolean);
     field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
     field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
@@ -43300,7 +43882,7 @@
     method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
-    method public void addKeyboardNavigationGroups(int, java.util.Collection<android.view.View>, int);
+    method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
     method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
     method public void addTouchables(java.util.ArrayList<android.view.View>);
@@ -43339,6 +43921,7 @@
     method public void createContextMenu(android.view.ContextMenu);
     method public void destroyDrawingCache();
     method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
+    method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
     method public void dispatchDisplayHint(int);
     method public boolean dispatchDragEvent(android.view.DragEvent);
@@ -43357,6 +43940,7 @@
     method public boolean dispatchNestedPrePerformAccessibilityAction(int, android.os.Bundle);
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public void dispatchPointerCaptureChanged(boolean);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int);
     method public void dispatchProvideStructure(android.view.ViewStructure);
@@ -43422,6 +44006,7 @@
     method public float getElevation();
     method public boolean getFilterTouchesWhenObscured();
     method public boolean getFitsSystemWindows();
+    method public int getFocusable();
     method public java.util.ArrayList<android.view.View> getFocusables(int);
     method public void getFocusedRect(android.graphics.Rect);
     method public android.graphics.drawable.Drawable getForeground();
@@ -43464,7 +44049,6 @@
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
-    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -43508,7 +44092,7 @@
     method public java.lang.Object getTag(int);
     method public int getTextAlignment();
     method public int getTextDirection();
-    method public final java.lang.CharSequence getTooltip();
+    method public final java.lang.CharSequence getTooltipText();
     method public final int getTop();
     method protected float getTopFadingEdgeStrength();
     method protected int getTopPaddingOffset();
@@ -43538,6 +44122,7 @@
     method public boolean hasNestedScrollingParent();
     method public boolean hasOnClickListeners();
     method public boolean hasOverlappingRendering();
+    method public boolean hasPointerCapture();
     method public boolean hasTransientState();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
@@ -43569,7 +44154,6 @@
     method public boolean isInLayout();
     method public boolean isInTouchMode();
     method public final boolean isKeyboardNavigationCluster();
-    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -43592,7 +44176,7 @@
     method public boolean isVerticalFadingEdgeEnabled();
     method public boolean isVerticalScrollBarEnabled();
     method public void jumpDrawablesToCurrentState();
-    method public android.view.View keyboardNavigationGroupSearch(int, android.view.View, int);
+    method public android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public void layout(int, int, int, int);
     method public final void measure(int, int);
     method protected static int[] mergeDrawableStates(int[], int[]);
@@ -43603,6 +44187,7 @@
     method public android.view.WindowInsets onApplyWindowInsets(android.view.WindowInsets);
     method protected void onAttachedToWindow();
     method public void onCancelPendingInputEvents();
+    method public boolean onCapturedPointerEvent(android.view.MotionEvent);
     method public boolean onCheckIsTextEditor();
     method protected void onConfigurationChanged(android.content.res.Configuration);
     method protected void onCreateContextMenu(android.view.ContextMenu);
@@ -43632,6 +44217,7 @@
     method protected void onLayout(boolean, int, int, int, int);
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
+    method public void onPointerCaptureChange(boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void onProvideAutoFillStructure(android.view.ViewStructure, int);
     method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int);
@@ -43674,6 +44260,7 @@
     method public void postOnAnimation(java.lang.Runnable);
     method public void postOnAnimationDelayed(java.lang.Runnable, long);
     method public void refreshDrawableState();
+    method public void releasePointerCapture();
     method public boolean removeCallbacks(java.lang.Runnable);
     method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -43684,6 +44271,7 @@
     method public boolean requestFocus(int, android.graphics.Rect);
     method public final boolean requestFocusFromTouch();
     method public void requestLayout();
+    method public void requestPointerCapture();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
     method public final void requestUnbufferedDispatch(android.view.MotionEvent);
@@ -43727,6 +44315,7 @@
     method public void setFilterTouchesWhenObscured(boolean);
     method public void setFitsSystemWindows(boolean);
     method public void setFocusable(boolean);
+    method public void setFocusable(int);
     method public void setFocusableInTouchMode(boolean);
     method public void setFocusedByDefault(boolean);
     method public void setForeground(android.graphics.drawable.Drawable);
@@ -43742,7 +44331,6 @@
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
     method public void setKeyboardNavigationCluster(boolean);
-    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -43760,8 +44348,8 @@
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
-    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
+    method public void setOnCapturedPointerListener(android.view.View.OnCapturedPointerListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
     method public void setOnCreateContextMenuListener(android.view.View.OnCreateContextMenuListener);
@@ -43809,7 +44397,7 @@
     method public void setTag(int, java.lang.Object);
     method public void setTextAlignment(int);
     method public void setTextDirection(int);
-    method public final void setTooltip(java.lang.CharSequence);
+    method public final void setTooltipText(java.lang.CharSequence);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
     method public final void setTransitionName(java.lang.String);
@@ -43866,8 +44454,10 @@
     field protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET;
     field public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2; // 0x2
     field public static final int FIND_VIEWS_WITH_TEXT = 1; // 0x1
+    field public static final int FOCUSABLE = 1; // 0x1
     field public static final int FOCUSABLES_ALL = 0; // 0x0
     field public static final int FOCUSABLES_TOUCH_MODE = 1; // 0x1
+    field public static final int FOCUSABLE_AUTO = 16; // 0x10
     field protected static final int[] FOCUSED_SELECTED_STATE_SET;
     field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_STATE_SET;
@@ -43886,8 +44476,6 @@
     field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
     field public static final int INVISIBLE = 4; // 0x4
     field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
-    field public static final int KEYBOARD_NAVIGATION_GROUP_CLUSTER = 1; // 0x1
-    field public static final int KEYBOARD_NAVIGATION_GROUP_SECTION = 2; // 0x2
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
     field public static final int LAYER_TYPE_NONE = 0; // 0x0
     field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
@@ -43899,6 +44487,7 @@
     field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
     field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
     field public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field public static final int NOT_FOCUSABLE = 0; // 0x0
     field public static final int NO_ID = -1; // 0xffffffff
     field public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
     field public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
@@ -44033,6 +44622,10 @@
     method public abstract void onViewDetachedFromWindow(android.view.View);
   }
 
+  public static abstract interface View.OnCapturedPointerListener {
+    method public abstract boolean onCapturedPointer(android.view.View, android.view.MotionEvent);
+  }
+
   public static abstract interface View.OnClickListener {
     method public abstract void onClick(android.view.View);
   }
@@ -44406,7 +44999,7 @@
     method public abstract boolean isLayoutRequested();
     method public abstract boolean isTextAlignmentResolved();
     method public abstract boolean isTextDirectionResolved();
-    method public abstract android.view.View keyboardNavigationGroupSearch(int, android.view.View, int);
+    method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
     method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
     method public abstract boolean onNestedPreFling(android.view.View, float, float);
@@ -44603,6 +45196,7 @@
     method public boolean getAllowReturnTransitionOverlap();
     method public final android.view.WindowManager.LayoutParams getAttributes();
     method public final android.view.Window.Callback getCallback();
+    method public int getColorMode();
     method public final android.view.Window getContainer();
     method public android.transition.Scene getContentScene();
     method public final android.content.Context getContext();
@@ -44659,6 +45253,7 @@
     method public abstract void setChildDrawable(int, android.graphics.drawable.Drawable);
     method public abstract void setChildInt(int, int);
     method public void setClipToOutline(boolean);
+    method public void setColorMode(int);
     method public void setContainer(android.view.Window);
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
@@ -44763,6 +45358,7 @@
     method public abstract boolean onMenuItemSelected(int, android.view.MenuItem);
     method public abstract boolean onMenuOpened(int, android.view.Menu);
     method public abstract void onPanelClosed(int, android.view.Menu);
+    method public default void onPointerCaptureChanged(boolean);
     method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
     method public abstract boolean onSearchRequested();
@@ -44857,8 +45453,10 @@
     method public final int copyFrom(android.view.WindowManager.LayoutParams);
     method public java.lang.String debug(java.lang.String);
     method public int describeContents();
+    method public int getColorMode();
     method public final java.lang.CharSequence getTitle();
     method public static boolean mayUseInputMethod(int);
+    method public void setColorMode(int);
     method public final void setTitle(java.lang.CharSequence);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ALPHA_CHANGED = 128; // 0x80
@@ -45727,6 +46325,13 @@
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
   }
 
+  public final class AutoFillManager {
+    method public void updateAutoFillInput(android.view.View, int);
+    method public void updateAutoFillInput(android.view.View, int, android.graphics.Rect, int);
+    field public static final int FLAG_UPDATE_UI_HIDE = 2; // 0x2
+    field public static final int FLAG_UPDATE_UI_SHOW = 1; // 0x1
+  }
+
   public final class AutoFillType implements android.os.Parcelable {
     method public int describeContents();
     method public static android.view.autofill.AutoFillType forList();
@@ -45761,6 +46366,8 @@
   public static final class Dataset.Builder {
     ctor public Dataset.Builder(java.lang.CharSequence);
     method public android.view.autofill.Dataset build();
+    method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
+    method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
     method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
     method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
@@ -45776,6 +46383,8 @@
     method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
     method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
     method public android.view.autofill.FillResponse build();
+    method public android.view.autofill.FillResponse.Builder requiresCustomAuthentication(android.os.Bundle, int);
+    method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
     method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
@@ -45786,7 +46395,7 @@
 
   public static abstract class VirtualViewDelegate.Callback {
     ctor public VirtualViewDelegate.Callback();
-    method public void onFocusChanged(int, boolean);
+    method public void onAutoFillInputUpdated(int, android.graphics.Rect, int);
     method public void onNodeRemoved(int...);
     method public void onValueChanged(int);
   }
@@ -46193,6 +46802,83 @@
 
 }
 
+package android.view.textclassifier {
+
+  public abstract interface LinksInfo {
+    method public abstract boolean apply(java.lang.CharSequence);
+  }
+
+  public final class TextClassificationManager {
+    method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
+    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+  }
+
+  public final class TextClassificationResult {
+    method public float getConfidenceScore(java.lang.String);
+    method public java.lang.String getEntity(int);
+    method public int getEntityCount();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public android.content.Intent getIntent();
+    method public java.lang.CharSequence getLabel();
+    method public android.view.View.OnClickListener getOnClickListener();
+    method public java.lang.String getText();
+  }
+
+  public static final class TextClassificationResult.Builder {
+    ctor public TextClassificationResult.Builder();
+    method public android.view.textclassifier.TextClassificationResult build();
+    method public android.view.textclassifier.TextClassificationResult.Builder setEntityType(java.lang.String, float);
+    method public android.view.textclassifier.TextClassificationResult.Builder setIcon(android.graphics.drawable.Drawable);
+    method public android.view.textclassifier.TextClassificationResult.Builder setIntent(android.content.Intent);
+    method public android.view.textclassifier.TextClassificationResult.Builder setLabel(java.lang.String);
+    method public android.view.textclassifier.TextClassificationResult.Builder setOnClickListener(android.view.View.OnClickListener);
+    method public android.view.textclassifier.TextClassificationResult.Builder setText(java.lang.String);
+  }
+
+  public abstract interface TextClassifier {
+    method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
+    method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
+    method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+    field public static final android.view.textclassifier.TextClassifier NO_OP;
+    field public static final java.lang.String TYPE_ADDRESS = "address";
+    field public static final java.lang.String TYPE_EMAIL = "email";
+    field public static final java.lang.String TYPE_OTHER = "other";
+    field public static final java.lang.String TYPE_PHONE = "phone";
+  }
+
+  public static abstract class TextClassifier.EntityType implements java.lang.annotation.Annotation {
+  }
+
+  public final class TextLanguage {
+    method public float getConfidenceScore(java.util.Locale);
+    method public int getEndIndex();
+    method public java.util.Locale getLanguage(int);
+    method public int getLanguageCount();
+    method public int getStartIndex();
+  }
+
+  public static final class TextLanguage.Builder {
+    ctor public TextLanguage.Builder(int, int);
+    method public android.view.textclassifier.TextLanguage build();
+    method public android.view.textclassifier.TextLanguage.Builder setLanguage(java.util.Locale, float);
+  }
+
+  public final class TextSelection {
+    method public float getConfidenceScore(java.lang.String);
+    method public java.lang.String getEntity(int);
+    method public int getEntityCount();
+    method public int getSelectionEndIndex();
+    method public int getSelectionStartIndex();
+  }
+
+  public static final class TextSelection.Builder {
+    ctor public TextSelection.Builder(int, int);
+    method public android.view.textclassifier.TextSelection build();
+    method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
+  }
+
+}
+
 package android.view.textservice {
 
   public final class SentenceSuggestionsInfo implements android.os.Parcelable {
@@ -46423,6 +47109,7 @@
   public abstract class RenderProcessGoneDetail {
     ctor public RenderProcessGoneDetail();
     method public abstract boolean didCrash();
+    method public abstract int rendererPriorityAtExit();
   }
 
   public class ServiceWorkerClient {
@@ -46839,6 +47526,8 @@
     method public deprecated java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String);
     method public java.lang.String getOriginalUrl();
     method public int getProgress();
+    method public boolean getRendererPriorityWaivedWhenNotVisible();
+    method public int getRendererRequestedPriority();
     method public deprecated float getScale();
     method public android.webkit.WebSettings getSettings();
     method public java.lang.String getTitle();
@@ -46886,6 +47575,7 @@
     method public deprecated void setMapTrackballToArrowKeys(boolean);
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
+    method public void setRendererPriorityPolicy(int, boolean);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
@@ -46895,6 +47585,9 @@
     method public void zoomBy(float);
     method public boolean zoomIn();
     method public boolean zoomOut();
+    field public static final int RENDERER_PRIORITY_BOUND = 1; // 0x1
+    field public static final int RENDERER_PRIORITY_IMPORTANT = 2; // 0x2
+    field public static final int RENDERER_PRIORITY_WAIVED = 0; // 0x0
     field public static final java.lang.String SCHEME_GEO = "geo:0,0?q=";
     field public static final java.lang.String SCHEME_MAILTO = "mailto:";
     field public static final java.lang.String SCHEME_TEL = "tel:";
@@ -49200,6 +49893,10 @@
     method public void endBatchEdit();
     method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText);
     method public final int getAutoLinkMask();
+    method public int getAutoSizeMaxTextSize();
+    method public int getAutoSizeMinTextSize();
+    method public int getAutoSizeStepGranularity();
+    method public int getAutoSizeTextType();
     method public int getBreakStrategy();
     method public int getCompoundDrawablePadding();
     method public android.content.res.ColorStateList getCompoundDrawableTintList();
@@ -49225,7 +49922,6 @@
     method public int getExtendedPaddingTop();
     method public android.text.InputFilter[] getFilters();
     method public java.lang.String getFontFeatureSettings();
-    method public java.lang.String getFontVariationSettings();
     method public boolean getFreezesText();
     method public int getGravity();
     method public int getHighlightColor();
@@ -49272,7 +49968,7 @@
     method public float getShadowRadius();
     method public final boolean getShowSoftInputOnFocus();
     method public java.lang.CharSequence getText();
-    method public android.text.TextAssistant getTextAssistant();
+    method public android.view.textclassifier.TextClassifier getTextClassifier();
     method public final android.content.res.ColorStateList getTextColors();
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
@@ -49309,6 +50005,10 @@
     method public void removeTextChangedListener(android.text.TextWatcher);
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
+    method public void setAutoSizeMaxTextSize(int, float);
+    method public void setAutoSizeMinTextSize(int, float);
+    method public void setAutoSizeStepGranularity(int, float);
+    method public void setAutoSizeTextType(int);
     method public void setBreakStrategy(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
@@ -49331,7 +50031,6 @@
     method public void setExtractedText(android.view.inputmethod.ExtractedText);
     method public void setFilters(android.text.InputFilter[]);
     method public void setFontFeatureSettings(java.lang.String);
-    method public void setFontVariationSettings(java.lang.String);
     method protected boolean setFrame(int, int, int, int);
     method public void setFreezesText(boolean);
     method public void setGravity(int);
@@ -49385,7 +50084,7 @@
     method public final void setText(int, android.widget.TextView.BufferType);
     method public void setTextAppearance(int);
     method public deprecated void setTextAppearance(android.content.Context, int);
-    method public void setTextAssistant(android.text.TextAssistant);
+    method public void setTextClassifier(android.view.textclassifier.TextClassifier);
     method public void setTextColor(int);
     method public void setTextColor(android.content.res.ColorStateList);
     method public void setTextIsSelectable(boolean);
@@ -49400,8 +50099,8 @@
     method public void setTypeface(android.graphics.Typeface, int);
     method public void setTypeface(android.graphics.Typeface);
     method public void setWidth(int);
-    field public static final int AUTO_SIZE_TYPE_NONE = 0; // 0x0
-    field public static final int AUTO_SIZE_TYPE_XY = 1; // 0x1
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_XY = 1; // 0x1
   }
 
   public static final class TextView.BufferType extends java.lang.Enum {
@@ -50020,6 +50719,10 @@
     method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
   }
 
+  public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+    ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
+  }
+
   public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
     ctor public PathClassLoader(java.lang.String, java.lang.ClassLoader);
     ctor public PathClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader);
@@ -53118,6 +53821,133 @@
 
 }
 
+package java.lang.invoke {
+
+  public class LambdaConversionException extends java.lang.Exception {
+    ctor public LambdaConversionException();
+    ctor public LambdaConversionException(java.lang.String);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable, boolean, boolean);
+  }
+
+  public abstract class MethodHandle {
+    method public java.lang.invoke.MethodHandle asFixedArity();
+    method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
+    method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
+    method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
+    method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
+    method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+    method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
+    method public boolean isVarargsCollector();
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public abstract interface MethodHandleInfo {
+    method public abstract java.lang.Class<?> getDeclaringClass();
+    method public abstract java.lang.invoke.MethodType getMethodType();
+    method public abstract int getModifiers();
+    method public abstract java.lang.String getName();
+    method public abstract int getReferenceKind();
+    method public default boolean isVarArgs();
+    method public static boolean refKindIsField(int);
+    method public static boolean refKindIsValid(int);
+    method public static java.lang.String refKindName(int);
+    method public static java.lang.String referenceKindToString(int);
+    method public abstract <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandles.Lookup);
+    method public static java.lang.String toString(int, java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType);
+    field public static final int REF_getField = 1; // 0x1
+    field public static final int REF_getStatic = 2; // 0x2
+    field public static final int REF_invokeInterface = 9; // 0x9
+    field public static final int REF_invokeSpecial = 7; // 0x7
+    field public static final int REF_invokeStatic = 6; // 0x6
+    field public static final int REF_invokeVirtual = 5; // 0x5
+    field public static final int REF_newInvokeSpecial = 8; // 0x8
+    field public static final int REF_putField = 3; // 0x3
+    field public static final int REF_putStatic = 4; // 0x4
+  }
+
+  public class MethodHandles {
+    method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandles.Lookup lookup();
+    method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
+    method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+    method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
+  }
+
+  public static final class MethodHandles.Lookup {
+    method public java.lang.invoke.MethodHandle bind(java.lang.Object, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findConstructor(java.lang.Class<?>, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSpecial(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStatic(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStaticGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findStaticSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findVirtual(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
+    method public java.lang.Class<?> lookupClass();
+    method public int lookupModes();
+    method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSpecial(java.lang.reflect.Method, java.lang.Class<?>) throws java.lang.IllegalAccessException;
+    field public static final int PACKAGE = 8; // 0x8
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+    field public static final int PUBLIC = 1; // 0x1
+  }
+
+  public final class MethodType implements java.io.Serializable {
+    method public java.lang.invoke.MethodType appendParameterTypes(java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType appendParameterTypes(java.util.List<java.lang.Class<?>>);
+    method public java.lang.invoke.MethodType changeParameterType(int, java.lang.Class<?>);
+    method public java.lang.invoke.MethodType changeReturnType(java.lang.Class<?>);
+    method public java.lang.invoke.MethodType dropParameterTypes(int, int);
+    method public java.lang.invoke.MethodType erase();
+    method public static java.lang.invoke.MethodType fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) throws java.lang.IllegalArgumentException, java.lang.TypeNotPresentException;
+    method public java.lang.invoke.MethodType generic();
+    method public static java.lang.invoke.MethodType genericMethodType(int, boolean);
+    method public static java.lang.invoke.MethodType genericMethodType(int);
+    method public boolean hasPrimitives();
+    method public boolean hasWrappers();
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>[]);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.invoke.MethodType);
+    method public java.lang.Class<?>[] parameterArray();
+    method public int parameterCount();
+    method public java.util.List<java.lang.Class<?>> parameterList();
+    method public java.lang.Class<?> parameterType(int);
+    method public java.lang.Class<?> returnType();
+    method public java.lang.String toMethodDescriptorString();
+    method public java.lang.invoke.MethodType unwrap();
+    method public java.lang.invoke.MethodType wrap();
+  }
+
+  public class WrongMethodTypeException extends java.lang.RuntimeException {
+    ctor public WrongMethodTypeException();
+    ctor public WrongMethodTypeException(java.lang.String);
+  }
+
+}
+
 package java.lang.ref {
 
   public class PhantomReference<T> extends java.lang.ref.Reference {
@@ -61397,6 +62227,9 @@
     method public static <E> java.util.Collection<E> checkedCollection(java.util.Collection<E>, java.lang.Class<E>);
     method public static <E> java.util.List<E> checkedList(java.util.List<E>, java.lang.Class<E>);
     method public static <K, V> java.util.Map<K, V> checkedMap(java.util.Map<K, V>, java.lang.Class<K>, java.lang.Class<V>);
+    method public static <K, V> java.util.NavigableMap<K, V> checkedNavigableMap(java.util.NavigableMap<K, V>, java.lang.Class<K>, java.lang.Class<V>);
+    method public static <E> java.util.NavigableSet<E> checkedNavigableSet(java.util.NavigableSet<E>, java.lang.Class<E>);
+    method public static <E> java.util.Queue<E> checkedQueue(java.util.Queue<E>, java.lang.Class<E>);
     method public static <E> java.util.Set<E> checkedSet(java.util.Set<E>, java.lang.Class<E>);
     method public static <K, V> java.util.SortedMap<K, V> checkedSortedMap(java.util.SortedMap<K, V>, java.lang.Class<K>, java.lang.Class<V>);
     method public static <E> java.util.SortedSet<E> checkedSortedSet(java.util.SortedSet<E>, java.lang.Class<E>);
@@ -61407,7 +62240,11 @@
     method public static final <T> java.util.List<T> emptyList();
     method public static <T> java.util.ListIterator<T> emptyListIterator();
     method public static final <K, V> java.util.Map<K, V> emptyMap();
+    method public static final <K, V> java.util.NavigableMap<K, V> emptyNavigableMap();
+    method public static <E> java.util.NavigableSet<E> emptyNavigableSet();
     method public static final <T> java.util.Set<T> emptySet();
+    method public static final <K, V> java.util.SortedMap<K, V> emptySortedMap();
+    method public static <E> java.util.SortedSet<E> emptySortedSet();
     method public static <T> java.util.Enumeration<T> enumeration(java.util.Collection<T>);
     method public static <T> void fill(java.util.List<? super T>, T);
     method public static int frequency(java.util.Collection<?>, java.lang.Object);
@@ -61436,12 +62273,16 @@
     method public static <T> java.util.Collection<T> synchronizedCollection(java.util.Collection<T>);
     method public static <T> java.util.List<T> synchronizedList(java.util.List<T>);
     method public static <K, V> java.util.Map<K, V> synchronizedMap(java.util.Map<K, V>);
+    method public static <K, V> java.util.NavigableMap<K, V> synchronizedNavigableMap(java.util.NavigableMap<K, V>);
+    method public static <T> java.util.NavigableSet<T> synchronizedNavigableSet(java.util.NavigableSet<T>);
     method public static <T> java.util.Set<T> synchronizedSet(java.util.Set<T>);
     method public static <K, V> java.util.SortedMap<K, V> synchronizedSortedMap(java.util.SortedMap<K, V>);
     method public static <T> java.util.SortedSet<T> synchronizedSortedSet(java.util.SortedSet<T>);
     method public static <T> java.util.Collection<T> unmodifiableCollection(java.util.Collection<? extends T>);
     method public static <T> java.util.List<T> unmodifiableList(java.util.List<? extends T>);
     method public static <K, V> java.util.Map<K, V> unmodifiableMap(java.util.Map<? extends K, ? extends V>);
+    method public static <K, V> java.util.NavigableMap<K, V> unmodifiableNavigableMap(java.util.NavigableMap<K, ? extends V>);
+    method public static <T> java.util.NavigableSet<T> unmodifiableNavigableSet(java.util.NavigableSet<T>);
     method public static <T> java.util.Set<T> unmodifiableSet(java.util.Set<? extends T>);
     method public static <K, V> java.util.SortedMap<K, V> unmodifiableSortedMap(java.util.SortedMap<K, ? extends V>);
     method public static <T> java.util.SortedSet<T> unmodifiableSortedSet(java.util.SortedSet<T>);
@@ -64886,14 +65727,14 @@
     method public java.util.logging.ErrorManager getErrorManager();
     method public java.util.logging.Filter getFilter();
     method public java.util.logging.Formatter getFormatter();
-    method public synchronized java.util.logging.Level getLevel();
+    method public java.util.logging.Level getLevel();
     method public boolean isLoggable(java.util.logging.LogRecord);
     method public abstract void publish(java.util.logging.LogRecord);
     method protected void reportError(java.lang.String, java.lang.Exception, int);
-    method public void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
-    method public void setErrorManager(java.util.logging.ErrorManager);
-    method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
-    method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+    method public synchronized void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
+    method public synchronized void setErrorManager(java.util.logging.ErrorManager);
+    method public synchronized void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+    method public synchronized void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
     method public synchronized void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
   }
 
@@ -64920,7 +65761,7 @@
   public class LogManager {
     ctor protected LogManager();
     method public boolean addLogger(java.util.logging.Logger);
-    method public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+    method public deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
     method public void checkAccess() throws java.lang.SecurityException;
     method public static java.util.logging.LogManager getLogManager();
     method public java.util.logging.Logger getLogger(java.lang.String);
@@ -64929,7 +65770,7 @@
     method public java.lang.String getProperty(java.lang.String);
     method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
     method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
-    method public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+    method public deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
     method public void reset() throws java.lang.SecurityException;
     field public static final java.lang.String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
   }
@@ -64966,14 +65807,18 @@
     ctor protected Logger(java.lang.String, java.lang.String);
     method public void addHandler(java.util.logging.Handler) throws java.lang.SecurityException;
     method public void config(java.lang.String);
+    method public void config(java.util.function.Supplier<java.lang.String>);
     method public void entering(java.lang.String, java.lang.String);
     method public void entering(java.lang.String, java.lang.String, java.lang.Object);
     method public void entering(java.lang.String, java.lang.String, java.lang.Object[]);
     method public void exiting(java.lang.String, java.lang.String);
     method public void exiting(java.lang.String, java.lang.String, java.lang.Object);
     method public void fine(java.lang.String);
+    method public void fine(java.util.function.Supplier<java.lang.String>);
     method public void finer(java.lang.String);
+    method public void finer(java.util.function.Supplier<java.lang.String>);
     method public void finest(java.lang.String);
+    method public void finest(java.util.function.Supplier<java.lang.String>);
     method public static java.util.logging.Logger getAnonymousLogger();
     method public static java.util.logging.Logger getAnonymousLogger(java.lang.String);
     method public java.util.logging.Filter getFilter();
@@ -64988,28 +65833,38 @@
     method public java.lang.String getResourceBundleName();
     method public boolean getUseParentHandlers();
     method public void info(java.lang.String);
+    method public void info(java.util.function.Supplier<java.lang.String>);
     method public boolean isLoggable(java.util.logging.Level);
     method public void log(java.util.logging.LogRecord);
     method public void log(java.util.logging.Level, java.lang.String);
+    method public void log(java.util.logging.Level, java.util.function.Supplier<java.lang.String>);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Object);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Object[]);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Throwable);
+    method public void log(java.util.logging.Level, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String);
+    method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.util.function.Supplier<java.lang.String>);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+    method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
+    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Object...);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Throwable);
     method public void removeHandler(java.util.logging.Handler) throws java.lang.SecurityException;
     method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
     method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
     method public void setParent(java.util.logging.Logger);
+    method public void setResourceBundle(java.util.ResourceBundle);
     method public void setUseParentHandlers(boolean);
     method public void severe(java.lang.String);
+    method public void severe(java.util.function.Supplier<java.lang.String>);
     method public void throwing(java.lang.String, java.lang.String, java.lang.Throwable);
     method public void warning(java.lang.String);
+    method public void warning(java.util.function.Supplier<java.lang.String>);
     field public static final java.lang.String GLOBAL_LOGGER_NAME = "global";
     field public static final deprecated java.util.logging.Logger global;
   }
@@ -65030,10 +65885,10 @@
     ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
     method public void close() throws java.lang.SecurityException;
     method public void flush();
-    method public synchronized java.util.logging.Level getPushLevel();
+    method public java.util.logging.Level getPushLevel();
     method public synchronized void publish(java.util.logging.LogRecord);
     method public synchronized void push();
-    method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
+    method public synchronized void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
   }
 
   public class SimpleFormatter extends java.util.logging.Formatter {
diff --git a/api/system-current.txt b/api/system-current.txt
index 35fd747..d1ecf95 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37,6 +37,7 @@
     field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
     field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
     field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
+    field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
     field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
@@ -138,6 +139,7 @@
     field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+    field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -199,6 +201,7 @@
     field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
     field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
+    field public static final java.lang.String REQUEST_DELETE_PACKAGES = "android.permission.REQUEST_DELETE_PACKAGES";
     field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
     field public static final java.lang.String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
@@ -215,6 +218,7 @@
     field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
     field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
     field public static final java.lang.String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
+    field public static final java.lang.String SET_MEDIA_KEY_LISTENER = "android.permission.SET_MEDIA_KEY_LISTENER";
     field public static final java.lang.String SET_ORIENTATION = "android.permission.SET_ORIENTATION";
     field public static final java.lang.String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED";
     field public static final deprecated java.lang.String SET_PREFERRED_APPLICATIONS = "android.permission.SET_PREFERRED_APPLICATIONS";
@@ -222,6 +226,7 @@
     field public static final java.lang.String SET_SCREEN_COMPATIBILITY = "android.permission.SET_SCREEN_COMPATIBILITY";
     field public static final java.lang.String SET_TIME = "android.permission.SET_TIME";
     field public static final java.lang.String SET_TIME_ZONE = "android.permission.SET_TIME_ZONE";
+    field public static final java.lang.String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
     field public static final java.lang.String SET_WALLPAPER = "android.permission.SET_WALLPAPER";
     field public static final java.lang.String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final java.lang.String SET_WALLPAPER_HINTS = "android.permission.SET_WALLPAPER_HINTS";
@@ -310,6 +315,8 @@
 
   public static final class R.attr {
     ctor public R.attr();
+    field public static final int __removed0 = 16844097; // 0x1010541
+    field public static final int __removed1 = 16844099; // 0x1010543
     field public static final int absListViewStyle = 16842858; // 0x101006a
     field public static final int accessibilityEventTypes = 16843648; // 0x1010380
     field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -470,6 +477,7 @@
     field public static final int centerMedium = 16842959; // 0x10100cf
     field public static final int centerX = 16843170; // 0x10101a2
     field public static final int centerY = 16843171; // 0x10101a3
+    field public static final int certDigest = 16844106; // 0x101054a
     field public static final int checkBoxPreferenceStyle = 16842895; // 0x101008f
     field public static final int checkMark = 16843016; // 0x1010108
     field public static final int checkMarkTint = 16843943; // 0x10104a7
@@ -512,6 +520,7 @@
     field public static final int colorForeground = 16842800; // 0x1010030
     field public static final int colorForegroundInverse = 16843270; // 0x1010206
     field public static final int colorLongPressedHighlight = 16843662; // 0x101038e
+    field public static final int colorMode = 16844108; // 0x101054c
     field public static final int colorMultiSelectHighlight = 16843665; // 0x1010391
     field public static final int colorPressedHighlight = 16843661; // 0x101038d
     field public static final int colorPrimary = 16843827; // 0x1010433
@@ -842,6 +851,7 @@
     field public static final int isScrollContainer = 16843342; // 0x101024e
     field public static final int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
+    field public static final int isolatedSplits = 16844109; // 0x101054d
     field public static final int itemBackground = 16843056; // 0x1010130
     field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131
     field public static final int itemPadding = 16843565; // 0x101032d
@@ -864,7 +874,6 @@
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
     field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
-    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -1014,7 +1023,6 @@
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
-    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -1098,6 +1106,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
+    field public static final int primaryContentAlpha = 16843367; // 0x1010267
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -1161,6 +1170,7 @@
     field public static final int resizeable = 16843405; // 0x101028d
     field public static final int resizeableActivity = 16844022; // 0x10104f6
     field public static final int resource = 16842789; // 0x1010025
+    field public static final int restartOnConfigChanges = 16844105; // 0x1010549
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
     field public static final int restrictedAccountType = 16843733; // 0x10103d5
@@ -1277,6 +1287,7 @@
     field public static final int spinnerStyle = 16842881; // 0x1010081
     field public static final int spinnersShown = 16843595; // 0x101034b
     field public static final int splitMotionEvents = 16843503; // 0x10102ef
+    field public static final int splitName = 16844107; // 0x101054b
     field public static final int splitTrack = 16843852; // 0x101044c
     field public static final int spotShadowAlpha = 16843967; // 0x10104bf
     field public static final int src = 16843033; // 0x1010119
@@ -1489,7 +1500,7 @@
     field public static final int toYDelta = 16843209; // 0x10101c9
     field public static final int toYScale = 16843205; // 0x10101c5
     field public static final int toolbarStyle = 16843946; // 0x10104aa
-    field public static final int tooltip = 16844084; // 0x1010534
+    field public static final int tooltipText = 16844084; // 0x1010534
     field public static final int top = 16843182; // 0x10101ae
     field public static final int topBright = 16842955; // 0x10100cb
     field public static final int topDark = 16842951; // 0x10100c7
@@ -1548,7 +1559,7 @@
     field public static final int viewportWidth = 16843778; // 0x1010402
     field public static final int visibility = 16842972; // 0x10100dc
     field public static final int visible = 16843156; // 0x1010194
-    field public static final int visibleToEphemeral = 16844095; // 0x101053f
+    field public static final int visibleToInstantApps = 16844095; // 0x101053f
     field public static final int vmSafeMode = 16843448; // 0x10102b8
     field public static final int voiceIcon = 16843908; // 0x1010484
     field public static final int voiceLanguage = 16843349; // 0x1010255
@@ -1925,6 +1936,7 @@
     field public static final int tabs = 16908307; // 0x1020013
     field public static final int text1 = 16908308; // 0x1020014
     field public static final int text2 = 16908309; // 0x1020015
+    field public static final int textAssist = 16908353; // 0x1020041
     field public static final int title = 16908310; // 0x1020016
     field public static final int toggle = 16908311; // 0x1020017
     field public static final int undo = 16908338; // 0x1020032
@@ -2994,8 +3006,9 @@
   public class AccountManager {
     method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
-    method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, int[]);
+    method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, java.util.Map<java.lang.Integer, java.lang.Integer>);
     method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
+    method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[]);
     method public java.lang.String blockingGetAuthToken(android.accounts.Account, java.lang.String, boolean) throws android.accounts.AuthenticatorException, java.io.IOException, android.accounts.OperationCanceledException;
     method public void clearPassword(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
@@ -3003,7 +3016,9 @@
     method public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> finishSessionAsUser(android.os.Bundle, android.app.Activity, android.os.UserHandle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public static android.accounts.AccountManager get(android.content.Context);
+    method public int getAccountVisibility(android.accounts.Account, int);
     method public android.accounts.Account[] getAccounts();
+    method public java.util.Map<android.accounts.Account, java.lang.Integer> getAccountsAndVisibilityForPackage(java.lang.String, java.lang.String);
     method public android.accounts.Account[] getAccountsByType(java.lang.String);
     method public android.accounts.AccountManagerFuture<android.accounts.Account[]> getAccountsByTypeAndFeatures(java.lang.String, java.lang.String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler);
     method public android.accounts.Account[] getAccountsByTypeForPackage(java.lang.String, java.lang.String);
@@ -3014,13 +3029,11 @@
     method public android.accounts.AuthenticatorDescription[] getAuthenticatorTypes();
     method public java.lang.String getPassword(android.accounts.Account);
     method public java.lang.String getPreviousName(android.accounts.Account);
-    method public int[] getRequestingUidsForType(java.lang.String);
+    method public java.util.Map<java.lang.Integer, java.lang.Integer> getUidsAndVisibilityForAccount(android.accounts.Account);
     method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public void invalidateAuthToken(java.lang.String, java.lang.String);
-    method public boolean isAccountVisible(android.accounts.Account, int);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
-    method public boolean makeAccountVisible(android.accounts.Account, int);
     method public static deprecated android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], boolean, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
     method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.List<android.accounts.Account>, java.lang.String[], java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
     method public boolean notifyAccountAuthenticated(android.accounts.Account);
@@ -3028,9 +3041,9 @@
     method public deprecated android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean removeAccountExplicitly(android.accounts.Account);
-    method public boolean removeAccountVisibility(android.accounts.Account, int);
     method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
     method public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
+    method public boolean setAccountVisibility(android.accounts.Account, int, int);
     method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
     method public void setPassword(android.accounts.Account, java.lang.String);
     method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
@@ -3069,7 +3082,14 @@
     field public static final java.lang.String KEY_LAST_AUTHENTICATED_TIME = "lastAuthenticatedTime";
     field public static final java.lang.String KEY_PASSWORD = "password";
     field public static final java.lang.String KEY_USERDATA = "userdata";
-    field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+    field public static final deprecated java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+    field public static final int UID_KEY_DEFAULT_LEGACY_VISIBILITY = -3; // 0xfffffffd
+    field public static final int UID_KEY_DEFAULT_VISIBILITY = -2; // 0xfffffffe
+    field public static final int VISIBILITY_NOT_VISIBLE = 3; // 0x3
+    field public static final int VISIBILITY_UNDEFINED = 0; // 0x0
+    field public static final int VISIBILITY_USER_MANAGED_NOT_VISIBLE = 4; // 0x4
+    field public static final int VISIBILITY_USER_MANAGED_VISIBLE = 2; // 0x2
+    field public static final int VISIBILITY_VISIBLE = 1; // 0x1
   }
 
   public abstract interface AccountManagerCallback<V> {
@@ -3168,8 +3188,10 @@
 
   public static abstract interface Animator.AnimatorListener {
     method public abstract void onAnimationCancel(android.animation.Animator);
+    method public default void onAnimationEnd(android.animation.Animator, boolean);
     method public abstract void onAnimationEnd(android.animation.Animator);
     method public abstract void onAnimationRepeat(android.animation.Animator);
+    method public default void onAnimationStart(android.animation.Animator, boolean);
     method public abstract void onAnimationStart(android.animation.Animator);
   }
 
@@ -3205,6 +3227,8 @@
     method public void playSequentially(java.util.List<android.animation.Animator>);
     method public void playTogether(android.animation.Animator...);
     method public void playTogether(java.util.Collection<android.animation.Animator>);
+    method public void reverse();
+    method public void setCurrentPlayTime(long);
     method public android.animation.AnimatorSet setDuration(long);
     method public void setInterpolator(android.animation.TimeInterpolator);
     method public void setStartDelay(long);
@@ -3609,8 +3633,7 @@
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
-    method public void enterPictureInPictureMode(float);
-    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
+    method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -3643,7 +3666,6 @@
     method public int getRequestedOrientation();
     method public final android.view.SearchEvent getSearchEvent();
     method public int getTaskId();
-    method public android.text.TextAssistant getTextAssistant();
     method public final java.lang.CharSequence getTitle();
     method public final int getTitleColor();
     method public android.app.VoiceInteractor getVoiceInteractor();
@@ -3785,8 +3807,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
-    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
-    method public void setPictureInPictureAspectRatio(float);
+    method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
     method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -3796,7 +3817,6 @@
     method public final void setResult(int, android.content.Intent);
     method public final deprecated void setSecondaryProgress(int);
     method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
-    method public void setTextAssistant(android.text.TextAssistant);
     method public void setTitle(java.lang.CharSequence);
     method public void setTitle(int);
     method public deprecated void setTitleColor(int);
@@ -4748,6 +4768,7 @@
 
   public abstract class FragmentContainer {
     ctor public FragmentContainer();
+    method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
     method public abstract android.view.View onFindViewById(int);
     method public abstract boolean onHasView();
   }
@@ -4983,11 +5004,13 @@
   public static class Instrumentation.ActivityMonitor {
     ctor public Instrumentation.ActivityMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean);
     ctor public Instrumentation.ActivityMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean);
+    ctor public Instrumentation.ActivityMonitor();
     method public final android.content.IntentFilter getFilter();
     method public final int getHits();
     method public final android.app.Activity getLastActivity();
     method public final android.app.Instrumentation.ActivityResult getResult();
     method public final boolean isBlocking();
+    method public android.app.Instrumentation.ActivityResult onMatchIntent(android.content.Intent);
     method public final android.app.Activity waitForActivity();
     method public final android.app.Activity waitForActivityWithTimeout(long);
   }
@@ -5160,6 +5183,7 @@
     method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
+    method public long getTimeout();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
     field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -5183,11 +5207,14 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+    field public static final java.lang.String EXTRA_COLORIZED = "android.colorized";
     field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final java.lang.String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
     field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
@@ -5268,6 +5295,7 @@
     method public android.app.Notification.Action clone();
     method public int describeContents();
     method public boolean getAllowGeneratedReplies();
+    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
     method public android.os.Bundle getExtras();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
@@ -5332,7 +5360,8 @@
   }
 
   public static class Notification.Builder {
-    ctor public Notification.Builder(android.content.Context);
+    ctor public Notification.Builder(android.content.Context, java.lang.String);
+    ctor public deprecated Notification.Builder(android.content.Context);
     method public deprecated android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
     method public android.app.Notification.Builder addAction(android.app.Notification.Action);
     method public android.app.Notification.Builder addExtras(android.os.Bundle);
@@ -5351,6 +5380,7 @@
     method public android.app.Notification.Builder setChannel(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
     method public android.app.Notification.Builder setColor(int);
+    method public android.app.Notification.Builder setColorized(boolean);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
@@ -5388,6 +5418,7 @@
     method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
     method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
     method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public android.app.Notification.Builder setTimeout(long);
     method public android.app.Notification.Builder setUsesChronometer(boolean);
     method public android.app.Notification.Builder setVibrate(long[]);
     method public android.app.Notification.Builder setVisibility(int);
@@ -5454,9 +5485,11 @@
 
   public static class Notification.MessagingStyle extends android.app.Notification.Style {
     ctor public Notification.MessagingStyle(java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addHistoricMessage(android.app.Notification.MessagingStyle.Message);
     method public android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
     method public android.app.Notification.MessagingStyle addMessage(android.app.Notification.MessagingStyle.Message);
     method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.app.Notification.MessagingStyle.Message> getHistoricMessages();
     method public java.util.List<android.app.Notification.MessagingStyle.Message> getMessages();
     method public java.lang.CharSequence getUserDisplayName();
     method public android.app.Notification.MessagingStyle setConversationTitle(java.lang.CharSequence);
@@ -5484,6 +5517,19 @@
     field protected android.app.Notification.Builder mBuilder;
   }
 
+  public static final class Notification.TvExtender implements android.app.Notification.Extender {
+    ctor public Notification.TvExtender();
+    ctor public Notification.TvExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public java.lang.String getChannel();
+    method public android.app.PendingIntent getContentIntent();
+    method public android.app.PendingIntent getDeleteIntent();
+    method public boolean isAvailableOnTv();
+    method public android.app.Notification.TvExtender setChannel(java.lang.String);
+    method public android.app.Notification.TvExtender setContentIntent(android.app.PendingIntent);
+    method public android.app.Notification.TvExtender setDeleteIntent(android.app.PendingIntent);
+  }
+
   public static final class Notification.WearableExtender implements android.app.Notification.Extender {
     ctor public Notification.WearableExtender();
     ctor public Notification.WearableExtender(android.app.Notification);
@@ -5551,6 +5597,7 @@
     method public boolean canShowBadge();
     method public int describeContents();
     method public void enableVibration(boolean);
+    method public java.lang.String getGroup();
     method public java.lang.String getId();
     method public int getImportance();
     method public int getLockscreenVisibility();
@@ -5558,10 +5605,12 @@
     method public android.net.Uri getSound();
     method public int getUserLockedFields();
     method public long[] getVibrationPattern();
-    method public boolean isAllowed();
+    method public boolean isDeleted();
     method public void lockFields(int);
     method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
     method public void setBypassDnd(boolean);
+    method public void setDeleted(boolean);
+    method public void setGroup(java.lang.String);
     method public void setImportance(int);
     method public void setLights(boolean);
     method public void setLockscreenVisibility(int);
@@ -5586,6 +5635,20 @@
     field public static final int USER_LOCKED_VISIBILITY = 2; // 0x2
   }
 
+  public final class NotificationChannelGroup implements android.os.Parcelable {
+    ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
+    ctor protected NotificationChannelGroup(android.os.Parcel);
+    method public void addChannel(android.app.NotificationChannel);
+    method public int describeContents();
+    method public java.util.List<android.app.NotificationChannel> getChannels();
+    method public java.lang.String getId();
+    method public java.lang.CharSequence getName();
+    method public org.json.JSONObject toJson() throws org.json.JSONException;
+    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
+  }
+
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
     method public boolean areNotificationsEnabled();
@@ -5593,6 +5656,8 @@
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
     method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+    method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup>);
     method public void createNotificationChannels(java.util.List<android.app.NotificationChannel>);
     method public void deleteNotificationChannel(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
@@ -5609,6 +5674,7 @@
     method public boolean removeAutomaticZenRule(java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -5694,6 +5760,17 @@
     method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
   }
 
+  public final class PictureInPictureArgs implements android.os.Parcelable {
+    ctor public PictureInPictureArgs();
+    ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
+    method public android.app.PictureInPictureArgs clone();
+    method public int describeContents();
+    method public void setActions(java.util.List<android.app.RemoteAction>);
+    method public void setAspectRatio(float);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
+  }
+
   public class Presentation extends android.app.Dialog {
     ctor public Presentation(android.content.Context, android.view.Display);
     ctor public Presentation(android.content.Context, android.view.Display, int);
@@ -5730,6 +5807,18 @@
     field public static final int STYLE_SPINNER = 0; // 0x0
   }
 
+  public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
+    ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
+    method public int describeContents();
+    method public android.app.PendingIntent getUserAction();
+    method public java.lang.CharSequence getUserActionTitle();
+    method public java.lang.CharSequence getUserMessage();
+    method public void showAsDialog(android.app.Activity);
+    method public void showAsNotification(android.content.Context);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR;
+  }
+
   public final class RemoteAction implements android.os.Parcelable {
     ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
     method public android.app.RemoteAction clone();
@@ -5747,14 +5836,18 @@
   }
 
   public final class RemoteInput implements android.os.Parcelable {
+    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
     method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
     method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
     method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getResultKey();
     method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
     field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5766,6 +5859,7 @@
     method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
     method public android.app.RemoteInput build();
     method public android.os.Bundle getExtras();
+    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
     method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
     method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
     method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
@@ -6247,6 +6341,7 @@
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearProfileOwner(android.content.ComponentName);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+    method public android.content.Intent createAdminSupportIntent(java.lang.String);
     method public android.os.UserHandle createAndManageUser(android.content.ComponentName, java.lang.String, android.content.ComponentName, android.os.PersistableBundle, int);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
@@ -6255,16 +6350,18 @@
     method public java.util.List<java.lang.String> getAffiliationIds(android.content.ComponentName);
     method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
-    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
+    method public deprecated java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(android.content.ComponentName);
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
-    method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+    method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
     method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
+    method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String);
+    method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
     method public deprecated java.lang.String getDeviceInitializerApp();
     method public deprecated android.content.ComponentName getDeviceInitializerComponent();
     method public java.lang.String getDeviceOwner();
@@ -6292,6 +6389,7 @@
     method public int getPasswordMinimumSymbols(android.content.ComponentName);
     method public int getPasswordMinimumUpperCase(android.content.ComponentName);
     method public int getPasswordQuality(android.content.ComponentName);
+    method public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(android.content.ComponentName);
     method public int getPermissionGrantState(android.content.ComponentName, java.lang.String, java.lang.String);
     method public int getPermissionPolicy(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
@@ -6318,7 +6416,8 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
-    method public boolean isCallerApplicationRestrictionsManagingPackage();
+    method public boolean isBackupServiceEnabled(android.content.ComponentName);
+    method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceManaged();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isDeviceProvisioned();
@@ -6335,6 +6434,8 @@
     method public void lockNow();
     method public void lockNow(int);
     method public void notifyPendingSystemUpdate(long);
+    method public void notifyPendingSystemUpdate(long, boolean);
+    method public boolean packageHasActiveAdmins(java.lang.String);
     method public void reboot(android.content.ComponentName);
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
@@ -6351,13 +6452,15 @@
     method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
-    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public deprecated void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
+    method public void setBackupServiceEnabled(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
-    method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
+    method public deprecated void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
     method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
+    method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
     method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
     method public void setDeviceProvisioningConfigApplied();
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
@@ -6406,6 +6509,7 @@
     method public void uninstallCaCert(android.content.ComponentName, byte[]);
     method public void wipeData(int);
     field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
+    field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
     field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
@@ -6418,6 +6522,12 @@
     field public static final java.lang.String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+    field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
+    field public static final java.lang.String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall";
+    field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
+    field public static final java.lang.String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
+    field public static final java.lang.String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
+    field public static final java.lang.String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
     field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
@@ -6425,6 +6535,7 @@
     field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
     field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
     field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
+    field public static final java.lang.String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
     field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
     field public static final java.lang.String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
     field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
@@ -6488,6 +6599,8 @@
     field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
     field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
     field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
+    field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
+    field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -6537,6 +6650,17 @@
     field public static final android.os.Parcelable.Creator<android.app.admin.SecurityLog.SecurityEvent> CREATOR;
   }
 
+  public final class SystemUpdateInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getReceivedTime();
+    method public int getSecurityPatchState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdateInfo> CREATOR;
+    field public static final int SECURITY_PATCH_STATE_FALSE = 1; // 0x1
+    field public static final int SECURITY_PATCH_STATE_TRUE = 2; // 0x2
+    field public static final int SECURITY_PATCH_STATE_UNKNOWN = 0; // 0x0
+  }
+
   public class SystemUpdatePolicy implements android.os.Parcelable {
     method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy();
     method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy();
@@ -6710,16 +6834,22 @@
     method public boolean isBackupEnabled();
     method public java.lang.String[] listAllTransports();
     method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver);
+    method public int requestBackup(java.lang.String[], android.app.backup.BackupObserver, int);
     method public int requestRestore(android.app.backup.RestoreObserver);
-    method public java.lang.String selectBackupTransport(java.lang.String);
+    method public deprecated java.lang.String selectBackupTransport(java.lang.String);
+    method public void selectBackupTransport(android.content.ComponentName, android.app.backup.SelectBackupTransportCallback);
     method public void setAutoRestore(boolean);
     method public void setBackupEnabled(boolean);
     field public static final int ERROR_AGENT_FAILURE = -1003; // 0xfffffc15
     field public static final int ERROR_BACKUP_NOT_ALLOWED = -2001; // 0xfffff82f
     field public static final int ERROR_PACKAGE_NOT_FOUND = -2002; // 0xfffff82e
     field public static final int ERROR_TRANSPORT_ABORTED = -1000; // 0xfffffc18
+    field public static final int ERROR_TRANSPORT_INVALID = -2; // 0xfffffffe
     field public static final int ERROR_TRANSPORT_PACKAGE_REJECTED = -1002; // 0xfffffc16
     field public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED = -1005; // 0xfffffc13
+    field public static final int ERROR_TRANSPORT_UNAVAILABLE = -1; // 0xffffffff
+    field public static final int FLAG_NON_INCREMENTAL_BACKUP = 1; // 0x1
+    field public static final java.lang.String PACKAGE_MANAGER_SENTINEL = "@pm@";
     field public static final int SUCCESS = 0; // 0x0
   }
 
@@ -6832,6 +6962,12 @@
     field public long token;
   }
 
+  public abstract class SelectBackupTransportCallback {
+    ctor public SelectBackupTransportCallback();
+    method public void onFailure(int);
+    method public void onSuccess(java.lang.String);
+  }
+
   public class SharedPreferencesBackupHelper extends android.app.backup.FileBackupHelperBase implements android.app.backup.BackupHelper {
     ctor public SharedPreferencesBackupHelper(android.content.Context, java.lang.String...);
     method public void performBackup(android.os.ParcelFileDescriptor, android.app.backup.BackupDataOutput, android.os.ParcelFileDescriptor);
@@ -8261,6 +8397,7 @@
     ctor public ClipData(android.content.ClipDescription, android.content.ClipData.Item);
     ctor public ClipData(android.content.ClipData);
     method public void addItem(android.content.ClipData.Item);
+    method public void addItem(android.content.ClipData.Item, android.content.ContentResolver);
     method public int describeContents();
     method public android.content.ClipDescription getDescription();
     method public android.content.ClipData.Item getItemAt(int);
@@ -8647,6 +8784,7 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public abstract android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.Context createCredentialProtectedStorageContext();
     method public abstract android.content.Context createDeviceProtectedStorageContext();
     method public abstract android.content.Context createDisplayContext(android.view.Display);
@@ -8787,6 +8925,7 @@
     field public static final java.lang.String DOWNLOAD_SERVICE = "download";
     field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
     field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
+    field public static final java.lang.String FONT_SERVICE = "font";
     field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
     field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
     field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
@@ -8856,6 +8995,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.Context createCredentialProtectedStorageContext();
     method public android.content.Context createDeviceProtectedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
@@ -9169,6 +9309,7 @@
     field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
     field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
     field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
+    field public static final java.lang.String ACTION_CLEAR_PACKAGE = "android.intent.action.CLEAR_PACKAGE";
     field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
     field public static final java.lang.String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
@@ -9381,10 +9522,10 @@
     field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
     field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER";
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
-    field public static final java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
-    field public static final java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
     field public static final java.lang.String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
     field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
@@ -9898,6 +10039,10 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     method public final int getThemeResource();
+    field public static final int COLOR_MODE_DEFAULT = 0; // 0x0
+    field public static final int COLOR_MODE_HDR = 2; // 0x2
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT = 1; // 0x1
+    field public static final int CONFIG_COLOR_MODE = 16384; // 0x4000
     field public static final int CONFIG_DENSITY = 4096; // 0x1000
     field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
     field public static final int CONFIG_KEYBOARD = 16; // 0x10
@@ -9958,6 +10103,7 @@
     field public static final int SCREEN_ORIENTATION_USER_LANDSCAPE = 11; // 0xb
     field public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12; // 0xc
     field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1
+    field public int colorMode;
     field public int configChanges;
     field public int documentLaunchMode;
     field public int flags;
@@ -10055,6 +10201,7 @@
     field public int requiresSmallestWidthDp;
     field public java.lang.String[] sharedLibraryFiles;
     field public java.lang.String sourceDir;
+    field public java.lang.String[] splitNames;
     field public java.lang.String[] splitPublicSourceDirs;
     field public java.lang.String[] splitSourceDirs;
     field public int targetSdkVersion;
@@ -10083,6 +10230,7 @@
     field public boolean enabled;
     field public boolean exported;
     field public java.lang.String processName;
+    field public java.lang.String splitName;
   }
 
   public class ConfigurationInfo implements android.os.Parcelable {
@@ -10113,7 +10261,8 @@
 
   public final class EphemeralResolveInfo implements android.os.Parcelable {
     ctor public deprecated EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
-    ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
+    ctor public deprecated EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
+    ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>, int);
     ctor public EphemeralResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
     method public int describeContents();
     method public byte[] getDigestBytes();
@@ -10121,6 +10270,7 @@
     method public deprecated java.util.List<android.content.IntentFilter> getFilters();
     method public java.util.List<android.content.pm.EphemeralIntentFilter> getIntentFilters();
     method public java.lang.String getPackageName();
+    method public int getVersionCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo> CREATOR;
     field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
@@ -10169,11 +10319,21 @@
     field public boolean handleProfiling;
     field public java.lang.String publicSourceDir;
     field public java.lang.String sourceDir;
+    field public java.lang.String[] splitNames;
     field public java.lang.String[] splitPublicSourceDirs;
     field public java.lang.String[] splitSourceDirs;
     field public java.lang.String targetPackage;
   }
 
+  public final class IntentFilterVerificationInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.Set<java.lang.String> getDomains();
+    method public java.lang.String getPackageName();
+    method public int getStatus();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
+  }
+
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -10203,7 +10363,10 @@
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
+    method public java.util.List<android.os.UserHandle> getProfiles();
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+    method public android.content.IntentSender getShortcutConfigActivityIntent(android.content.pm.LauncherActivityInfo);
+    method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(java.lang.String, android.os.UserHandle);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
@@ -10238,7 +10401,7 @@
     method public boolean accept(android.os.Bundle);
     method public boolean accept();
     method public int describeContents();
-    method public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo();
+    method public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo(android.content.Context);
     method public int getRequestType();
     method public android.content.pm.ShortcutInfo getShortcutInfo();
     method public boolean isValid();
@@ -10307,6 +10470,7 @@
     method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
     method public void uninstall(java.lang.String, android.content.IntentSender);
+    method public void uninstall(android.content.pm.VersionedPackage, android.content.IntentSender);
     method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void updateSessionAppIcon(int, android.graphics.Bitmap);
     method public void updateSessionAppLabel(int, java.lang.CharSequence);
@@ -10421,6 +10585,7 @@
     method public abstract boolean addPermission(android.content.pm.PermissionInfo);
     method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo);
     method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
+    method public abstract boolean canRequestPackageInstalls();
     method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]);
     method public abstract int checkPermission(java.lang.String, java.lang.String);
     method public abstract int checkSignatures(java.lang.String, java.lang.String);
@@ -10435,6 +10600,7 @@
     method public abstract android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.graphics.drawable.Drawable getActivityLogo(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.graphics.drawable.Drawable getActivityLogo(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
     method public abstract java.util.List<android.content.pm.PermissionGroupInfo> getAllPermissionGroups(int);
     method public abstract android.graphics.drawable.Drawable getApplicationBanner(android.content.pm.ApplicationInfo);
     method public abstract android.graphics.drawable.Drawable getApplicationBanner(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -10447,12 +10613,15 @@
     method public abstract android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
+    method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
+    method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -10460,6 +10629,7 @@
     method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInstaller getPackageInstaller();
     method public abstract int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] getPackagesForUid(int);
@@ -10475,6 +10645,7 @@
     method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
     method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public abstract java.lang.String[] getSystemSharedLibraryNames();
     method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -10506,7 +10677,9 @@
     method public abstract void setApplicationCategoryHint(java.lang.String, int);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
+    method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
     method public abstract void verifyPendingInstall(int, int);
@@ -10542,6 +10715,7 @@
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
+    field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
     field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
@@ -10585,6 +10759,7 @@
     field public static final java.lang.String FEATURE_SIP = "android.software.sip";
     field public static final java.lang.String FEATURE_SIP_VOIP = "android.software.sip.voip";
     field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
+    field public static final java.lang.String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
     field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
     field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
     field public static final deprecated java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
@@ -10666,6 +10841,11 @@
     field public static final int INSTALL_REASON_POLICY = 1; // 0x1
     field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
     field public static final int INSTALL_SUCCEEDED = 1; // 0x1
+    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2
+    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4
+    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1; // 0x1
+    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3; // 0x3
+    field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0; // 0x0
     field public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff
     field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
     field public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
@@ -10689,6 +10869,7 @@
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
+    field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -10837,6 +11018,20 @@
     field public java.lang.String permission;
   }
 
+  public final class SharedLibraryInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.pm.VersionedPackage getDeclaringPackage();
+    method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
+    method public java.lang.String getName();
+    method public int getVersion();
+    method public boolean isBuiltin();
+    method public boolean isDynamic();
+    method public boolean isStatic();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+    field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
+  }
+
   public final class ShortcutInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivity();
@@ -10880,6 +11075,7 @@
 
   public class ShortcutManager {
     method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public android.content.Intent createShortcutResultIntent(android.content.pm.ShortcutInfo);
     method public void disableShortcuts(java.util.List<java.lang.String>);
     method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
     method public void enableShortcuts(java.util.List<java.lang.String>);
@@ -10911,6 +11107,15 @@
     field public static final android.os.Parcelable.Creator<android.content.pm.Signature> CREATOR;
   }
 
+  public final class VersionedPackage implements android.os.Parcelable {
+    ctor public VersionedPackage(java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getPackageName();
+    method public long getVersionCode();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.VersionedPackage> CREATOR;
+  }
+
 }
 
 package android.content.pm.permission {
@@ -11010,7 +11215,9 @@
     method public int getLayoutDirection();
     method public android.os.LocaleList getLocales();
     method public boolean isLayoutSizeAtLeast(int);
+    method public boolean isScreenHdr();
     method public boolean isScreenRound();
+    method public boolean isScreenWideColorGamut();
     method public static boolean needNewResources(int, int);
     method public void readFromParcel(android.os.Parcel);
     method public void setLayoutDirection(java.util.Locale);
@@ -11020,6 +11227,16 @@
     method public void setToDefaults();
     method public int updateFrom(android.content.res.Configuration);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int COLOR_MODE_HDR_MASK = 12; // 0xc
+    field public static final int COLOR_MODE_HDR_NO = 4; // 0x4
+    field public static final int COLOR_MODE_HDR_SHIFT = 2; // 0x2
+    field public static final int COLOR_MODE_HDR_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_HDR_YES = 8; // 0x8
+    field public static final int COLOR_MODE_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 3; // 0x3
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 1; // 0x1
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 2; // 0x2
     field public static final android.os.Parcelable.Creator<android.content.res.Configuration> CREATOR;
     field public static final int DENSITY_DPI_UNDEFINED = 0; // 0x0
     field public static final int HARDKEYBOARDHIDDEN_NO = 1; // 0x1
@@ -11085,6 +11302,7 @@
     field public static final int UI_MODE_TYPE_UNDEFINED = 0; // 0x0
     field public static final int UI_MODE_TYPE_VR_HEADSET = 7; // 0x7
     field public static final int UI_MODE_TYPE_WATCH = 6; // 0x6
+    field public int colorMode;
     field public int densityDpi;
     field public float fontScale;
     field public int hardKeyboardHidden;
@@ -11139,6 +11357,7 @@
     method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
     method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme);
+    method public android.graphics.Typeface getFont(int) throws android.content.res.Resources.NotFoundException;
     method public float getFraction(int, int, int);
     method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String);
     method public int[] getIntArray(int) throws android.content.res.Resources.NotFoundException;
@@ -12625,15 +12844,57 @@
     method public static int HSVToColor(float[]);
     method public static int HSVToColor(int, float[]);
     method public static void RGBToHSV(int, int, int, float[]);
+    method public float alpha();
+    method public static float alpha(long);
     method public static int alpha(int);
     method public static int argb(int, int, int, int);
+    method public static int argb(float, float, float, float);
+    method public float blue();
+    method public static float blue(long);
     method public static int blue(int);
+    method public static android.graphics.ColorSpace colorSpace(long);
     method public static void colorToHSV(int, float[]);
+    method public android.graphics.Color convert(android.graphics.ColorSpace);
+    method public static long convert(int, android.graphics.ColorSpace);
+    method public static long convert(long, android.graphics.ColorSpace);
+    method public static long convert(float, float, float, float, android.graphics.ColorSpace, android.graphics.ColorSpace);
+    method public static long convert(long, android.graphics.ColorSpace.Connector);
+    method public static long convert(float, float, float, float, android.graphics.ColorSpace.Connector);
+    method public android.graphics.ColorSpace getColorSpace();
+    method public float getComponent(int);
+    method public int getComponentCount();
+    method public float[] getComponents();
+    method public android.graphics.ColorSpace.Model getModel();
+    method public float green();
+    method public static float green(long);
     method public static int green(int);
+    method public static boolean isInColorSpace(long, android.graphics.ColorSpace);
+    method public boolean isSrgb();
+    method public static boolean isSrgb(long);
+    method public boolean isWideGamut();
+    method public static boolean isWideGamut(long);
+    method public float luminance();
+    method public static float luminance(long);
     method public static float luminance(int);
+    method public long pack();
+    method public static long pack(int);
+    method public static long pack(float, float, float);
+    method public static long pack(float, float, float, float);
+    method public static long pack(float, float, float, float, android.graphics.ColorSpace);
     method public static int parseColor(java.lang.String);
+    method public float red();
+    method public static float red(long);
     method public static int red(int);
     method public static int rgb(int, int, int);
+    method public static int rgb(float, float, float);
+    method public int toArgb();
+    method public static int toArgb(long);
+    method public static android.graphics.Color valueOf(int);
+    method public static android.graphics.Color valueOf(long);
+    method public static android.graphics.Color valueOf(float, float, float);
+    method public static android.graphics.Color valueOf(float, float, float, float);
+    method public static android.graphics.Color valueOf(float, float, float, float, android.graphics.ColorSpace);
+    method public static android.graphics.Color valueOf(float[], android.graphics.ColorSpace);
     field public static final int BLACK = -16777216; // 0xff000000
     field public static final int BLUE = -16776961; // 0xff0000ff
     field public static final int CYAN = -16711681; // 0xff00ffff
@@ -12705,7 +12966,7 @@
     field public static final float[] ILLUMINANT_D65;
     field public static final float[] ILLUMINANT_D75;
     field public static final float[] ILLUMINANT_E;
-    field public static final int MAX_ID = 64; // 0x40
+    field public static final int MAX_ID = 63; // 0x3f
     field public static final int MIN_ID = -1; // 0xffffffff
   }
 
@@ -13028,7 +13289,6 @@
     method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt);
     method public android.graphics.Paint.FontMetricsInt getFontMetricsInt();
     method public float getFontSpacing();
-    method public java.lang.String getFontVariationSettings();
     method public int getHinting();
     method public float getLetterSpacing();
     method public android.graphics.MaskFilter getMaskFilter();
@@ -13086,7 +13346,6 @@
     method public void setFilterBitmap(boolean);
     method public void setFlags(int);
     method public void setFontFeatureSettings(java.lang.String);
-    method public void setFontVariationSettings(java.lang.String);
     method public void setHinting(int);
     method public void setLetterSpacing(float);
     method public void setLinearText(boolean);
@@ -13584,6 +13843,7 @@
   }
 
   public class Typeface {
+    method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
     method public static android.graphics.Typeface create(java.lang.String, int);
     method public static android.graphics.Typeface create(android.graphics.Typeface, int);
     method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
@@ -13604,6 +13864,14 @@
     field public static final android.graphics.Typeface SERIF;
   }
 
+  public static abstract interface Typeface.FontRequestCallback {
+    method public abstract void onTypefaceRequestFailed(int);
+    method public abstract void onTypefaceRetrieved(android.graphics.Typeface);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0
+  }
+
   public class Xfermode {
     ctor public Xfermode();
   }
@@ -14010,6 +14278,23 @@
     method public void addLevel(int, int, android.graphics.drawable.Drawable);
   }
 
+  public class MaskableIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public MaskableIconDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.drawable.Drawable getBackground();
+    method public android.graphics.drawable.Drawable getForeground();
+    method public android.graphics.Path getIconMask();
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setOpacity(int);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+    field public static final float DEFAULT_VIEW_PORT_SCALE = 0.6666667f;
+    field public static final float MASK_SIZE = 100.0f;
+  }
+
   public class NinePatchDrawable extends android.graphics.drawable.Drawable {
     ctor public deprecated NinePatchDrawable(android.graphics.Bitmap, byte[], android.graphics.Rect, java.lang.String);
     ctor public NinePatchDrawable(android.content.res.Resources, android.graphics.Bitmap, byte[], android.graphics.Rect, java.lang.String);
@@ -14158,6 +14443,19 @@
 
 }
 
+package android.graphics.fonts {
+
+  public final class FontRequest implements android.os.Parcelable {
+    ctor public FontRequest(java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public java.lang.String getProviderAuthority();
+    method public java.lang.String getQuery();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
+  }
+
+}
+
 package android.graphics.pdf {
 
   public class PdfDocument {
@@ -14485,9 +14783,42 @@
     method public float getZ();
   }
 
+  public final class HardwareBuffer implements android.os.Parcelable {
+    method public static android.hardware.HardwareBuffer create(int, int, int, int, long);
+    method public int describeContents();
+    method public void destroy();
+    method public int getFormat();
+    method public int getHeight();
+    method public int getLayers();
+    method public long getUsage();
+    method public int getWidth();
+    method public boolean isDestroyed();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BLOB = 33; // 0x21
+    field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR;
+    field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_FP16 = 22; // 0x16
+    field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGB_565 = 4; // 0x4
+    field public static final int RGB_888 = 3; // 0x3
+    field public static final long USAGE0_CPU_READ = 2L; // 0x2L
+    field public static final long USAGE0_CPU_READ_OFTEN = 6L; // 0x6L
+    field public static final long USAGE0_CPU_WRITE = 32L; // 0x20L
+    field public static final long USAGE0_CPU_WRITE_OFTEN = 96L; // 0x60L
+    field public static final long USAGE0_GPU_COLOR_OUTPUT = 2048L; // 0x800L
+    field public static final long USAGE0_GPU_CUBEMAP = 8192L; // 0x2000L
+    field public static final long USAGE0_GPU_DATA_BUFFER = 16384L; // 0x4000L
+    field public static final long USAGE0_GPU_SAMPLED_IMAGE = 1024L; // 0x400L
+    field public static final long USAGE0_GPU_STORAGE_IMAGE = 3072L; // 0xc00L
+    field public static final long USAGE0_PROTECTED_CONTENT = 262144L; // 0x40000L
+    field public static final long USAGE0_SENSOR_DIRECT_DATA = 536870912L; // 0x20000000L
+    field public static final long USAGE0_VIDEO_ENCODE = 2097152L; // 0x200000L
+  }
+
   public final class Sensor {
     method public int getFifoMaxEventCount();
     method public int getFifoReservedEventCount();
+    method public int getHighestDirectReportRateLevel();
     method public int getId();
     method public int getMaxDelay();
     method public float getMaximumRange();
@@ -14503,6 +14834,7 @@
     method public int getVersion();
     method public boolean isAdditionalInfoSupported();
     method public boolean isDataInjectionSupported();
+    method public boolean isDirectChannelTypeSupported(int);
     method public boolean isDynamicSensor();
     method public boolean isWakeUpSensor();
     field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
@@ -14584,6 +14916,17 @@
     field public final int type;
   }
 
+  public final class SensorDirectChannel implements java.lang.AutoCloseable {
+    method public void close();
+    method public boolean isValid();
+    field public static final int RATE_FAST = 2; // 0x2
+    field public static final int RATE_NORMAL = 1; // 0x1
+    field public static final int RATE_STOP = 0; // 0x0
+    field public static final int RATE_VERY_FAST = 3; // 0x3
+    field public static final int TYPE_ASHMEM = 1; // 0x1
+    field public static final int TYPE_HARDWARE_BUFFER = 2; // 0x2
+  }
+
   public class SensorEvent {
     field public int accuracy;
     field public android.hardware.Sensor sensor;
@@ -14615,6 +14958,9 @@
 
   public abstract class SensorManager {
     method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+    method public int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
+    method public android.hardware.SensorDirectChannel createDirectChannel(android.os.MemoryFile);
+    method public android.hardware.SensorDirectChannel createDirectChannel(android.hardware.HardwareBuffer);
     method public boolean flush(android.hardware.SensorEventListener);
     method public static float getAltitude(float, float);
     method public static void getAngleChange(float[], float[], float[]);
@@ -14746,7 +15092,7 @@
     method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close();
-    method public abstract void finishDeferredConfiguration(java.util.List<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void finalizeOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CameraDevice getDevice();
     method public abstract android.view.Surface getInputSurface();
     method public abstract boolean isReprocessable();
@@ -15161,6 +15507,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_CAPTURE_INTENT;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> CONTROL_ENABLE_ZSL;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SCENE_MODE;
@@ -15240,6 +15587,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AWB_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_CAPTURE_INTENT;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_ENABLE_ZSL;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE;
@@ -15389,11 +15737,13 @@
     ctor public OutputConfiguration(android.view.Surface, int);
     ctor public OutputConfiguration(int, android.view.Surface, int);
     ctor public OutputConfiguration(android.util.Size, java.lang.Class<T>);
+    method public void addSurface(android.view.Surface);
     method public int describeContents();
+    method public void enableSurfaceSharing();
     method public int getRotation();
     method public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
-    method public void setDeferredSurface(android.view.Surface);
+    method public java.util.List<android.view.Surface> getSurfaces();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int ROTATION_0 = 0; // 0x0
@@ -18654,6 +19004,15 @@
     method public boolean isTransitionalDifferent();
   }
 
+  public final class ListFormatter {
+    method public java.lang.String format(java.lang.Object...);
+    method public java.lang.String format(java.util.Collection<?>);
+    method public static android.icu.text.ListFormatter getInstance(android.icu.util.ULocale);
+    method public static android.icu.text.ListFormatter getInstance(java.util.Locale);
+    method public static android.icu.text.ListFormatter getInstance();
+    method public java.lang.String getPatternForNumItems(int);
+  }
+
   public abstract class LocaleDisplayNames {
     method public abstract android.icu.text.DisplayContext getContext(android.icu.text.DisplayContext.Type);
     method public abstract android.icu.text.LocaleDisplayNames.DialectHandling getDialectHandling();
@@ -18663,6 +19022,8 @@
     method public static android.icu.text.LocaleDisplayNames getInstance(android.icu.util.ULocale, android.icu.text.DisplayContext...);
     method public static android.icu.text.LocaleDisplayNames getInstance(java.util.Locale, android.icu.text.DisplayContext...);
     method public abstract android.icu.util.ULocale getLocale();
+    method public java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiList(java.util.Set<android.icu.util.ULocale>, boolean, java.util.Comparator<java.lang.Object>);
+    method public abstract java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiListCompareWholeItems(java.util.Set<android.icu.util.ULocale>, java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem>);
     method public abstract java.lang.String keyDisplayName(java.lang.String);
     method public abstract java.lang.String keyValueDisplayName(java.lang.String, java.lang.String);
     method public abstract java.lang.String languageDisplayName(java.lang.String);
@@ -18682,9 +19043,19 @@
     enum_constant public static final android.icu.text.LocaleDisplayNames.DialectHandling STANDARD_NAMES;
   }
 
+  public static class LocaleDisplayNames.UiListItem {
+    ctor public LocaleDisplayNames.UiListItem(android.icu.util.ULocale, android.icu.util.ULocale, java.lang.String, java.lang.String);
+    method public static java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem> getComparator(java.util.Comparator<java.lang.Object>, boolean);
+    field public final android.icu.util.ULocale minimized;
+    field public final android.icu.util.ULocale modified;
+    field public final java.lang.String nameInDisplayLocale;
+    field public final java.lang.String nameInSelf;
+  }
+
   public class MeasureFormat extends android.icu.text.UFormat {
     method public final boolean equals(java.lang.Object);
     method public java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
+    method public java.lang.StringBuilder formatMeasurePerUnit(android.icu.util.Measure, android.icu.util.MeasureUnit, java.lang.StringBuilder, java.text.FieldPosition);
     method public final java.lang.String formatMeasures(android.icu.util.Measure...);
     method public java.lang.StringBuilder formatMeasures(java.lang.StringBuilder, java.text.FieldPosition, android.icu.util.Measure...);
     method public static android.icu.text.MeasureFormat getCurrencyFormat(android.icu.util.ULocale);
@@ -19153,6 +19524,14 @@
     method public void setUpperCaseFirst(boolean);
   }
 
+  public final class ScientificNumberFormatter {
+    method public java.lang.String format(java.lang.Object);
+    method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.util.ULocale, java.lang.String, java.lang.String);
+    method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.text.DecimalFormat, java.lang.String, java.lang.String);
+    method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.util.ULocale);
+    method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.text.DecimalFormat);
+  }
+
   public abstract class SearchIterator {
     ctor protected SearchIterator(java.text.CharacterIterator, android.icu.text.BreakIterator);
     method public final int first();
@@ -19928,6 +20307,34 @@
     method public long getToDate();
   }
 
+  public final class EthiopicCalendar extends android.icu.util.CECalendar {
+    ctor public EthiopicCalendar();
+    ctor public EthiopicCalendar(android.icu.util.TimeZone);
+    ctor public EthiopicCalendar(java.util.Locale);
+    ctor public EthiopicCalendar(android.icu.util.ULocale);
+    ctor public EthiopicCalendar(android.icu.util.TimeZone, java.util.Locale);
+    ctor public EthiopicCalendar(android.icu.util.TimeZone, android.icu.util.ULocale);
+    ctor public EthiopicCalendar(int, int, int);
+    ctor public EthiopicCalendar(java.util.Date);
+    ctor public EthiopicCalendar(int, int, int, int, int, int);
+    method protected deprecated int handleGetExtendedYear();
+    method public boolean isAmeteAlemEra();
+    method public void setAmeteAlemEra(boolean);
+    field public static final int GENBOT = 8; // 0x8
+    field public static final int HAMLE = 10; // 0xa
+    field public static final int HEDAR = 2; // 0x2
+    field public static final int MEGABIT = 6; // 0x6
+    field public static final int MESKEREM = 0; // 0x0
+    field public static final int MIAZIA = 7; // 0x7
+    field public static final int NEHASSE = 11; // 0xb
+    field public static final int PAGUMEN = 12; // 0xc
+    field public static final int SENE = 9; // 0x9
+    field public static final int TAHSAS = 3; // 0x3
+    field public static final int TEKEMT = 1; // 0x1
+    field public static final int TER = 4; // 0x4
+    field public static final int YEKATIT = 5; // 0x5
+  }
+
   public abstract interface Freezable<T> implements java.lang.Cloneable {
     method public abstract T cloneAsThawed();
     method public abstract T freeze();
@@ -20456,6 +20863,35 @@
     enum_constant public static final android.icu.util.ULocale.Category FORMAT;
   }
 
+  public final class UniversalTimeScale {
+    method public static android.icu.math.BigDecimal bigDecimalFrom(double, int);
+    method public static android.icu.math.BigDecimal bigDecimalFrom(long, int);
+    method public static android.icu.math.BigDecimal bigDecimalFrom(android.icu.math.BigDecimal, int);
+    method public static long from(long, int);
+    method public static long getTimeScaleValue(int, int);
+    method public static android.icu.math.BigDecimal toBigDecimal(long, int);
+    method public static android.icu.math.BigDecimal toBigDecimal(android.icu.math.BigDecimal, int);
+    method public static long toLong(long, int);
+    field public static final int DB2_TIME = 8; // 0x8
+    field public static final int DOTNET_DATE_TIME = 4; // 0x4
+    field public static final int EPOCH_OFFSET_PLUS_1_VALUE = 6; // 0x6
+    field public static final int EPOCH_OFFSET_VALUE = 1; // 0x1
+    field public static final int EXCEL_TIME = 7; // 0x7
+    field public static final int FROM_MAX_VALUE = 3; // 0x3
+    field public static final int FROM_MIN_VALUE = 2; // 0x2
+    field public static final int ICU4C_TIME = 2; // 0x2
+    field public static final int JAVA_TIME = 0; // 0x0
+    field public static final int MAC_OLD_TIME = 5; // 0x5
+    field public static final int MAC_TIME = 6; // 0x6
+    field public static final int MAX_SCALE = 10; // 0xa
+    field public static final int TO_MAX_VALUE = 5; // 0x5
+    field public static final int TO_MIN_VALUE = 4; // 0x4
+    field public static final int UNITS_VALUE = 0; // 0x0
+    field public static final int UNIX_MICROSECONDS_TIME = 9; // 0x9
+    field public static final int UNIX_TIME = 1; // 0x1
+    field public static final int WINDOWS_FILE_TIME = 3; // 0x3
+  }
+
   public abstract interface ValueIterator {
     method public abstract boolean next(android.icu.util.ValueIterator.Element);
     method public abstract void reset();
@@ -20912,6 +21348,7 @@
     method public double getAccumulatedDeltaRangeMeters();
     method public int getAccumulatedDeltaRangeState();
     method public double getAccumulatedDeltaRangeUncertaintyMeters();
+    method public double getAutomaticGainControlLevelInDb();
     method public long getCarrierCycles();
     method public float getCarrierFrequencyHz();
     method public double getCarrierPhase();
@@ -20927,6 +21364,7 @@
     method public int getState();
     method public int getSvid();
     method public double getTimeOffsetNanos();
+    method public boolean hasAutomaticGainControlLevelInDb();
     method public boolean hasCarrierCycles();
     method public boolean hasCarrierFrequencyHz();
     method public boolean hasCarrierPhase();
@@ -20950,11 +21388,13 @@
     field public static final int STATE_GAL_E1C_2ND_CODE_LOCK = 2048; // 0x800
     field public static final int STATE_GLO_STRING_SYNC = 64; // 0x40
     field public static final int STATE_GLO_TOD_DECODED = 128; // 0x80
+    field public static final int STATE_GLO_TOD_KNOWN = 32768; // 0x8000
     field public static final int STATE_MSEC_AMBIGUOUS = 16; // 0x10
     field public static final int STATE_SBAS_SYNC = 8192; // 0x2000
     field public static final int STATE_SUBFRAME_SYNC = 4; // 0x4
     field public static final int STATE_SYMBOL_SYNC = 32; // 0x20
     field public static final int STATE_TOW_DECODED = 8; // 0x8
+    field public static final int STATE_TOW_KNOWN = 16384; // 0x4000
     field public static final int STATE_UNKNOWN = 0; // 0x0
   }
 
@@ -21011,12 +21451,14 @@
 
   public final class GnssStatus {
     method public float getAzimuthDegrees(int);
+    method public float getCarrierFrequencyHz(int);
     method public float getCn0DbHz(int);
     method public int getConstellationType(int);
     method public float getElevationDegrees(int);
     method public int getSatelliteCount();
     method public int getSvid(int);
     method public boolean hasAlmanacData(int);
+    method public boolean hasCarrierFrequency(int);
     method public boolean hasEphemerisData(int);
     method public boolean usedInFix(int);
     field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
@@ -21302,29 +21744,39 @@
     method public float getAccuracy();
     method public double getAltitude();
     method public float getBearing();
+    method public float getBearingAccuracyDegrees();
     method public long getElapsedRealtimeNanos();
     method public android.os.Bundle getExtras();
     method public double getLatitude();
     method public double getLongitude();
     method public java.lang.String getProvider();
     method public float getSpeed();
+    method public float getSpeedAccuracyMetersPerSecond();
     method public long getTime();
+    method public float getVerticalAccuracyMeters();
     method public boolean hasAccuracy();
     method public boolean hasAltitude();
     method public boolean hasBearing();
+    method public boolean hasBearingAccuracy();
     method public boolean hasSpeed();
+    method public boolean hasSpeedAccuracy();
+    method public boolean hasVerticalAccuracy();
     method public boolean isComplete();
     method public boolean isFromMockProvider();
     method public void makeComplete();
     method public void removeAccuracy();
     method public void removeAltitude();
     method public void removeBearing();
+    method public void removeBearingAccuracy();
     method public void removeSpeed();
+    method public void removeSpeedAccuracy();
+    method public void removeVerticalAccuracy();
     method public void reset();
     method public void set(android.location.Location);
     method public void setAccuracy(float);
     method public void setAltitude(double);
     method public void setBearing(float);
+    method public void setBearingAccuracyDegrees(float);
     method public void setElapsedRealtimeNanos(long);
     method public void setExtras(android.os.Bundle);
     method public void setIsFromMockProvider(boolean);
@@ -21332,7 +21784,9 @@
     method public void setLongitude(double);
     method public void setProvider(java.lang.String);
     method public void setSpeed(float);
+    method public void setSpeedAccuracyMetersPerSecond(float);
     method public void setTime(long);
+    method public void setVerticalAccuracyMeters(float);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.Location> CREATOR;
     field public static final int FORMAT_DEGREES = 0; // 0x0
@@ -21511,7 +21965,7 @@
     field public static final int FLAG_BYPASS_MUTE = 128; // 0x80
     field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
     field public static final int FLAG_HW_HOTWORD = 32; // 0x20
-    field public static final int FLAG_LOW_LATENCY = 256; // 0x100
+    field public static final deprecated int FLAG_LOW_LATENCY = 256; // 0x100
     field public static final int USAGE_ALARM = 4; // 0x4
     field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
     field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
@@ -21854,6 +22308,7 @@
     method public int getClientPid();
     method public int getClientUid();
     method public int getPlayerInterfaceId();
+    method public android.media.PlayerProxy getPlayerProxy();
     method public int getPlayerState();
     method public int getPlayerType();
     method public void writeToParcel(android.os.Parcel, int);
@@ -21996,6 +22451,7 @@
     method protected deprecated int getNativeFrameCount();
     method public static int getNativeOutputSampleRate(int);
     method public int getNotificationMarkerPosition();
+    method public int getPerformanceMode();
     method public int getPlayState();
     method public int getPlaybackHeadPosition();
     method public android.media.PlaybackParams getPlaybackParams();
@@ -22042,6 +22498,9 @@
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
+    field public static final int PERFORMANCE_MODE_LOW_LATENCY = 1; // 0x1
+    field public static final int PERFORMANCE_MODE_NONE = 0; // 0x0
+    field public static final int PERFORMANCE_MODE_POWER_SAVING = 2; // 0x2
     field public static final int PLAYSTATE_PAUSED = 2; // 0x2
     field public static final int PLAYSTATE_PLAYING = 3; // 0x3
     field public static final int PLAYSTATE_STOPPED = 1; // 0x1
@@ -22059,6 +22518,7 @@
     method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioTrack.Builder setPerformanceMode(int);
     method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
   }
@@ -22073,6 +22533,40 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public final class BufferingParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getInitialBufferingMode();
+    method public int getInitialBufferingWatermarkKB();
+    method public int getInitialBufferingWatermarkMs();
+    method public int getRebufferingMode();
+    method public int getRebufferingWatermarkHighKB();
+    method public int getRebufferingWatermarkHighMs();
+    method public int getRebufferingWatermarkLowKB();
+    method public int getRebufferingWatermarkLowMs();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUFFERING_MODE_NONE = 0; // 0x0
+    field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2
+    field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1
+    field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
+  }
+
+  public static class BufferingParams.Builder {
+    ctor public BufferingParams.Builder();
+    ctor public BufferingParams.Builder(android.media.BufferingParams);
+    method public android.media.BufferingParams build();
+    method public android.media.BufferingParams.Builder setInitialBufferingMode(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingMode(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -23217,6 +23711,7 @@
 
   public final class MediaMuxer {
     ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
+    ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
     method public int addTrack(android.media.MediaFormat);
     method public void release();
     method public void setLocation(float, float);
@@ -23245,7 +23740,9 @@
     method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
     method public void deselectTrack(int) throws java.lang.IllegalStateException;
     method public int getAudioSessionId();
+    method public android.media.BufferingParams getBufferingParams();
     method public int getCurrentPosition();
+    method public android.media.BufferingParams getDefaultBufferingParams();
     method public int getDuration();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -23268,6 +23765,7 @@
     method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public deprecated void setAudioStreamType(int);
     method public void setAuxEffectSendLevel(float);
+    method public void setBufferingParams(android.media.BufferingParams);
     method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
@@ -23393,6 +23891,8 @@
     method public void setLocation(float, float);
     method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
     method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
+    method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
+    method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
     method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
     method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
     method public void setOrientationHint(int);
@@ -23411,7 +23911,9 @@
     field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
     field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
+    field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322
     field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321
+    field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323
     field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1
   }
 
@@ -23682,6 +24184,13 @@
     field public static final android.os.Parcelable.Creator<android.media.PlaybackParams> CREATOR;
   }
 
+  public class PlayerProxy {
+    method public void pause() throws java.lang.IllegalStateException;
+    method public void setVolume(float) throws java.lang.IllegalStateException;
+    method public void start() throws java.lang.IllegalStateException;
+    method public void stop() throws java.lang.IllegalStateException;
+  }
+
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -24819,12 +25328,22 @@
     method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
     method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
     method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
+    method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
+    method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
   }
 
   public static abstract interface MediaSessionManager.OnActiveSessionsChangedListener {
     method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
   }
 
+  public static abstract interface MediaSessionManager.OnMediaKeyListener {
+    method public abstract boolean onMediaKey(android.view.KeyEvent);
+  }
+
+  public static abstract interface MediaSessionManager.OnVolumeKeyLongPressListener {
+    method public abstract void onVolumeKeyLongPress(android.view.KeyEvent);
+  }
+
   public final class PlaybackState implements android.os.Parcelable {
     method public int describeContents();
     method public long getActions();
@@ -25020,6 +25539,7 @@
     field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
     field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
     field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
     field public static final java.lang.String COLUMN_TYPE = "type";
     field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
@@ -25051,6 +25571,7 @@
     field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
     field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
     field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_PREVIEW = "TYPE_PREVIEW";
     field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
     field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
     field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
@@ -25078,6 +25599,7 @@
 
   public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns {
     field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
     field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
     field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
     field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
@@ -25086,14 +25608,24 @@
     field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
     field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
     field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_DURATION = "preview_duration";
+    field public static final java.lang.String COLUMN_PREVIEW_INTENT_URI = "preview_intent_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_LAST_PLAYBACK_POSITION = "preview_last_playback_position";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_WEIGHT = "preview_weight";
     field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
     field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
     field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -25102,12 +25634,23 @@
     field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
     field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
     field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_TRANSIENT = "transient";
     field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
     field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
     field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS";
+    field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS";
+    field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES";
+    field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS";
+    field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS";
+    field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS";
+    field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS";
+    field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE";
+    field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS";
+    field public static final java.lang.String REVIEW_RATING_STYLE_THUMPS_UP_DOWN = "REVIEW_RATING_STYLE_THUMPS_UP_DOWN";
   }
 
   public static final class TvContract.Programs.Genres {
@@ -25298,9 +25841,13 @@
     method public void unregisterCallback(android.media.tv.TvInputManager.TvInputCallback);
     method public void updateTvInputInfo(android.media.tv.TvInputInfo);
     field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
+    field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE";
     field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
     field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
     field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
+    field public static final java.lang.String ACTION_VIEW_RECORDING_SCHEDULES = "android.media.tv.action.VIEW_RECORDING_SCHEDULES";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
     field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -25556,6 +26103,47 @@
 
 }
 
+package android.metrics {
+
+  public class LogMaker {
+    ctor public LogMaker(int);
+    ctor public LogMaker(java.lang.Object[]);
+    method public android.metrics.LogMaker addTaggedData(int, java.lang.Object);
+    method public void deserialize(java.lang.Object[]);
+    method public int getCategory();
+    method public long getCounterBucket();
+    method public java.lang.String getCounterName();
+    method public int getCounterValue();
+    method public java.lang.String getPackageName();
+    method public int getSubtype();
+    method public java.lang.Object getTaggedData(int);
+    method public long getTimestamp();
+    method public int getType();
+    method public boolean isLongCounterBucket();
+    method public boolean isValidValue(java.lang.Object);
+    method public java.lang.Object[] serialize();
+    method public android.metrics.LogMaker setCategory(int);
+    method public android.metrics.LogMaker setCounterBucket(int);
+    method public android.metrics.LogMaker setCounterBucket(long);
+    method public android.metrics.LogMaker setCounterName(java.lang.String);
+    method public android.metrics.LogMaker setCounterValue(int);
+    method public android.metrics.LogMaker setPackageName(java.lang.String);
+    method public android.metrics.LogMaker setSubtype(int);
+    method public android.metrics.LogMaker setTimestamp(long);
+    method public android.metrics.LogMaker setType(int);
+  }
+
+  public class MetricsReader {
+    ctor public MetricsReader();
+    method public void checkpoint();
+    method public boolean hasNext();
+    method public android.metrics.LogMaker next();
+    method public void read(long);
+    method public void reset();
+  }
+
+}
+
 package android.mtp {
 
   public final class MtpConstants {
@@ -26072,6 +26660,10 @@
     field public static final android.os.Parcelable.Creator<android.net.Network> CREATOR;
   }
 
+  public class NetworkBadging {
+    method public static android.graphics.drawable.Drawable getWifiIcon(int, int, android.content.res.Resources.Theme);
+  }
+
   public final class NetworkCapabilities implements android.os.Parcelable {
     ctor public NetworkCapabilities(android.net.NetworkCapabilities);
     method public int describeContents();
@@ -26241,9 +26833,14 @@
   public final class RecommendationRequest implements android.os.Parcelable {
     ctor protected RecommendationRequest(android.os.Parcel);
     method public int describeContents();
-    method public android.net.wifi.WifiConfiguration getCurrentSelectedConfig();
-    method public android.net.NetworkCapabilities getRequiredCapabilities();
+    method public android.net.wifi.WifiConfiguration[] getConnectableConfigs();
+    method public android.net.wifi.WifiConfiguration getConnectedConfig();
+    method public android.net.wifi.WifiConfiguration getDefaultWifiConfig();
+    method public int getLastSelectedNetworkId();
+    method public long getLastSelectedNetworkTimestamp();
     method public android.net.wifi.ScanResult[] getScanResults();
+    method public void setConnectableConfigs(android.net.wifi.WifiConfiguration[]);
+    method public void setConnectedConfig(android.net.wifi.WifiConfiguration);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.RecommendationRequest> CREATOR;
   }
@@ -26251,8 +26848,10 @@
   public static final class RecommendationRequest.Builder {
     ctor public RecommendationRequest.Builder();
     method public android.net.RecommendationRequest build();
-    method public android.net.RecommendationRequest.Builder setCurrentRecommendedWifiConfig(android.net.wifi.WifiConfiguration);
-    method public android.net.RecommendationRequest.Builder setNetworkCapabilities(android.net.NetworkCapabilities);
+    method public android.net.RecommendationRequest.Builder setConnectableConfigs(android.net.wifi.WifiConfiguration[]);
+    method public android.net.RecommendationRequest.Builder setConnectedWifiConfig(android.net.wifi.WifiConfiguration);
+    method public android.net.RecommendationRequest.Builder setDefaultWifiConfig(android.net.wifi.WifiConfiguration);
+    method public android.net.RecommendationRequest.Builder setLastSelectedNetwork(int, long);
     method public android.net.RecommendationRequest.Builder setScanResults(android.net.wifi.ScanResult[]);
   }
 
@@ -26344,6 +26943,7 @@
     ctor public TrafficStats();
     method public static void clearThreadStatsTag();
     method public static void clearThreadStatsUid();
+    method public static int getAndSetThreadStatsTag(int);
     method public static long getMobileRxBytes();
     method public static long getMobileRxPackets();
     method public static long getMobileTxBytes();
@@ -27348,9 +27948,12 @@
   public class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
+    method public android.net.ProxyInfo getHttpProxy();
     method public boolean hasNoInternetAccess();
+    method public boolean isEphemeral();
     method public boolean isNoInternetAccessExpected();
     method public boolean isPasspoint();
+    method public void setHttpProxy(android.net.ProxyInfo);
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
     field public java.lang.String FQDN;
@@ -32200,6 +32803,7 @@
     method public final android.util.SizeF readSizeF();
     method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
     method public final android.util.SparseBooleanArray readSparseBooleanArray();
+    method public final android.util.SparseIntArray readSparseIntArray();
     method public final java.lang.String readString();
     method public final void readStringArray(java.lang.String[]);
     method public final void readStringList(java.util.List<java.lang.String>);
@@ -32244,6 +32848,7 @@
     method public final void writeSizeF(android.util.SizeF);
     method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
     method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
+    method public final void writeSparseIntArray(android.util.SparseIntArray);
     method public final void writeString(java.lang.String);
     method public final void writeStringArray(java.lang.String[]);
     method public final void writeStringList(java.util.List<java.lang.String>);
@@ -32446,6 +33051,15 @@
     field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
   }
 
+  public abstract class ProxyFileDescriptorCallback {
+    ctor public ProxyFileDescriptorCallback();
+    method public void onFsync() throws android.system.ErrnoException;
+    method public long onGetSize() throws android.system.ErrnoException;
+    method public int onRead(long, int, byte[]) throws android.system.ErrnoException;
+    method public abstract void onRelease();
+    method public int onWrite(long, int, byte[]) throws android.system.ErrnoException;
+  }
+
   public class RecoverySystem {
     method public static void cancelScheduledUpdate(android.content.Context) throws java.io.IOException;
     method public static void installPackage(android.content.Context, java.io.File) throws java.io.IOException;
@@ -32579,6 +33193,7 @@
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
+    method public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
@@ -32693,7 +33308,8 @@
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
     method public java.util.List<android.os.UserHandle> getUserProfiles();
-    method public int getUserRestrictionSource(java.lang.String, android.os.UserHandle);
+    method public deprecated int getUserRestrictionSource(java.lang.String, android.os.UserHandle);
+    method public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(java.lang.String, android.os.UserHandle);
     method public android.os.Bundle getUserRestrictions();
     method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean hasUserRestriction(java.lang.String);
@@ -32759,6 +33375,14 @@
     field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
   }
 
+  public static final class UserManager.EnforcingUser implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.UserHandle getUserHandle();
+    method public int getUserRestrictionSource();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.os.UserManager.EnforcingUser> CREATOR;
+  }
+
   public static abstract class UserManager.UserRestrictionSource implements java.lang.annotation.Annotation {
   }
 
@@ -32942,13 +33566,22 @@
   }
 
   public class StorageManager {
+    method public long getCacheQuotaBytes();
+    method public long getCacheSizeBytes();
+    method public long getExternalCacheQuotaBytes();
+    method public long getExternalCacheSizeBytes();
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
+    method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
+    method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
+    method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
+    method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
     field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
   }
@@ -33089,6 +33722,7 @@
     method public android.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
     method public android.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
     method public int getOrder();
+    method public android.preference.PreferenceGroup getParent();
     method protected boolean getPersistedBoolean(boolean);
     method protected float getPersistedFloat(float);
     method protected int getPersistedInt(int);
@@ -35085,7 +35719,7 @@
     ctor public ContactsContract.Intents();
     field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS";
     field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
-    field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
+    field public static final deprecated java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
     field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
     field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
@@ -35225,8 +35859,10 @@
   public static final class ContactsContract.ProviderStatus {
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/provider_status";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DATABASE_CREATION_TIMESTAMP = "database_creation_timestamp";
     field public static final java.lang.String STATUS = "status";
     field public static final int STATUS_BUSY = 1; // 0x1
+    field public static final android.net.Uri STATUS_CHANGE_NOTIFICATION_CONTENT_URI;
     field public static final int STATUS_EMPTY = 2; // 0x2
     field public static final int STATUS_NORMAL = 0; // 0x0
   }
@@ -35372,6 +36008,7 @@
     method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
     method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
+    method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle);
     method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
     method public static java.util.List<java.lang.String> findDocumentPath(android.content.ContentResolver, android.net.Uri);
     method public static java.lang.String getDocumentId(android.net.Uri);
@@ -35416,6 +36053,7 @@
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
     field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
+    field public static final int FLAG_WEB_LINKABLE = 4096; // 0x1000
     field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
   }
 
@@ -35449,6 +36087,7 @@
     ctor public DocumentsProvider();
     method public java.lang.String copyDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public java.lang.String createDocument(java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+    method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException;
     method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
@@ -35468,7 +36107,6 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public android.content.res.AssetFileDescriptor openTypedDocument(java.lang.String, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
-    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public abstract android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
     method public android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], android.os.Bundle) throws java.io.FileNotFoundException;
@@ -35482,6 +36120,16 @@
     method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
+  public class FontsContract {
+  }
+
+  public static final class FontsContract.Columns implements android.provider.BaseColumns {
+    ctor public FontsContract.Columns();
+    field public static final java.lang.String STYLE = "font_style";
+    field public static final java.lang.String TTC_INDEX = "font_ttc_index";
+    field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
+  }
+
   public final deprecated class LiveFolders implements android.provider.BaseColumns {
     field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
     field public static final java.lang.String DESCRIPTION = "description";
@@ -35949,6 +36597,7 @@
     field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
     field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
     field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
+    field public static final java.lang.String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
     field public static final java.lang.String ACTION_CONFIGURE_WIFI_SETTINGS = "android.settings.CONFIGURE_WIFI_SETTINGS";
     field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
     field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
@@ -35968,6 +36617,7 @@
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
+    field public static final java.lang.String ACTION_MANAGE_EXTERNAL_SOURCES = "android.settings.action.MANAGE_EXTERNAL_SOURCES";
     field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
     field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
     field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -36004,8 +36654,10 @@
     field public static final java.lang.String AUTHORITY = "settings";
     field public static final java.lang.String EXTRA_ACCOUNT_TYPES = "account_types";
     field public static final java.lang.String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled";
+    field public static final java.lang.String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
     field public static final java.lang.String EXTRA_AUTHORITIES = "authorities";
     field public static final java.lang.String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
     field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
     field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
     field public static final java.lang.String EXTRA_INPUT_METHOD_ID = "input_method_id";
@@ -36040,6 +36692,7 @@
     field public static final java.lang.String BOOT_COUNT = "boot_count";
     field public static final java.lang.String CONTACT_METADATA_SYNC_ENABLED = "contact_metadata_sync_enabled";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String CURATE_SAVED_OPEN_NETWORKS = "curate_saved_open_networks";
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
     field public static final java.lang.String DEBUG_APP = "debug_app";
     field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
@@ -36123,7 +36776,7 @@
     field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
     field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
     field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
-    field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+    field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String LOCATION_MODE = "location_mode";
     field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
     field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
@@ -36678,6 +37331,8 @@
 
   public static final class VoicemailContract.Voicemails implements android.provider.BaseColumns android.provider.OpenableColumns {
     method public static android.net.Uri buildSourceUri(java.lang.String);
+    field public static final java.lang.String ARCHIVED = "archived";
+    field public static final java.lang.String BACKED_UP = "backed_up";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATE = "date";
     field public static final java.lang.String DELETED = "deleted";
@@ -36685,6 +37340,7 @@
     field public static final java.lang.String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String HAS_CONTENT = "has_content";
+    field public static final java.lang.String IS_OMTP_VOICEMAIL = "is_omtp_voicemail";
     field public static final java.lang.String IS_READ = "is_read";
     field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
     field public static final java.lang.String LAST_MODIFIED = "last_modified";
@@ -36692,6 +37348,7 @@
     field public static final java.lang.String NUMBER = "number";
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
     field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
+    field public static final java.lang.String RESTORED = "restored";
     field public static final java.lang.String SOURCE_DATA = "source_data";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String TRANSCRIPTION = "transcription";
@@ -37896,6 +38553,18 @@
 
 package android.security.keystore {
 
+  public abstract class AttestationUtils {
+    method public static java.security.cert.X509Certificate[] attestDeviceIds(android.content.Context, int[], byte[]) throws android.security.keystore.DeviceIdAttestationException;
+    field public static final int ID_TYPE_IMEI = 2; // 0x2
+    field public static final int ID_TYPE_MEID = 3; // 0x3
+    field public static final int ID_TYPE_SERIAL = 1; // 0x1
+  }
+
+  public class DeviceIdAttestationException extends java.lang.Exception {
+    ctor public DeviceIdAttestationException(java.lang.String);
+    ctor public DeviceIdAttestationException(java.lang.String, java.lang.Throwable);
+  }
+
   public class KeyExpiredException extends java.security.InvalidKeyException {
     ctor public KeyExpiredException();
     ctor public KeyExpiredException(java.lang.String);
@@ -38015,6 +38684,7 @@
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
     field public static final int PURPOSE_VERIFY = 8; // 0x8
+    field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
   }
@@ -38068,16 +38738,25 @@
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
+    method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
     method public void onDisconnected();
     method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
+    field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
+    field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
+    field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
+    field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
+    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
   public final class FillCallback {
+    method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
     method public void onFailure(java.lang.CharSequence);
+    method public void onFillResponseAuthentication(int);
     method public void onSuccess(android.view.autofill.FillResponse);
   }
 
@@ -38279,7 +38958,6 @@
     field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
     field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
     field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-    field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
   }
 
   public class MediaBrowserService.Result<T> {
@@ -38363,6 +39041,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
+    method public final void unsnoozeNotification(java.lang.String);
     method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
@@ -38380,6 +39059,7 @@
     method public final int getCurrentInterruptionFilter();
     method public final int getCurrentListenerHints();
     method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
+    method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
@@ -38400,9 +39080,7 @@
     method public final void setOnNotificationPostedTrim(int);
     method public final void snoozeNotification(java.lang.String, java.lang.String);
     method public final void snoozeNotification(java.lang.String, long);
-    method public final void snoozeNotification(java.lang.String);
     method public void unregisterAsSystemService() throws android.os.RemoteException;
-    method public final void unsnoozeNotification(java.lang.String);
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
@@ -38427,9 +39105,9 @@
     field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
     field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
     field public static final int REASON_SNOOZED = 18; // 0x12
+    field public static final int REASON_TIMEOUT = 19; // 0x13
     field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10
     field public static final int REASON_USER_STOPPED = 6; // 0x6
-    field public static final int REASON_USER_SWITCH = 19; // 0x13
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
     field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2
@@ -38439,6 +39117,7 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public boolean canShowBadge();
     method public java.util.List<java.lang.String> getAdditionalPeople();
     method public android.app.NotificationChannel getChannel();
     method public int getImportance();
@@ -38480,7 +39159,6 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
-    method public android.app.NotificationChannel getNotificationChannel();
     method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
@@ -38603,6 +39281,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public boolean onConfigure(java.util.List<android.os.PersistableBundle>);
     method public void onDeviceLocked();
+    method public void onDeviceUnlockLockout(long);
     method public void onDeviceUnlocked();
     method public void onTrustTimeout();
     method public void onUnlockAttempt(boolean);
@@ -38940,6 +39619,7 @@
     method public abstract int getMaxBufferSize();
     method public abstract boolean hasFinished();
     method public abstract boolean hasStarted();
+    method public default void rangeStart(int, int, int);
     method public abstract int start(int, int, int);
   }
 
@@ -39092,6 +39772,7 @@
     method public void onError(java.lang.String, int);
     method public abstract void onStart(java.lang.String);
     method public void onStop(java.lang.String, boolean);
+    method public void onUtteranceRangeStart(java.lang.String, int, int);
   }
 
   public class Voice implements android.os.Parcelable {
@@ -40036,6 +40717,7 @@
     method public void onReject();
     method public void onReject(java.lang.String);
     method public void onSeparate();
+    method public void onShowIncomingCallUi();
     method public void onStateChanged(int);
     method public void onStopDtmfTone();
     method public void onUnhold();
@@ -40047,6 +40729,7 @@
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
     method public final void setAudioModeIsVoip(boolean);
+    method public final void setAudioRoute(int);
     method public final void setCallerDisplayName(java.lang.String, int);
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -40095,6 +40778,7 @@
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+    field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
     field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -40162,7 +40846,9 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
     method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
     method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -40399,6 +41085,7 @@
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
     field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
     field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
+    field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
     field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -40637,6 +41324,8 @@
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
+    method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+    method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isRinging();
     method public boolean isTtySupported();
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
@@ -40649,7 +41338,7 @@
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
     field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
-    field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+    field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
     field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
@@ -40759,7 +41448,8 @@
     field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
     field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
     field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
-    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+    field public static final deprecated java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY = "carrier_vvm_package_name_string_array";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
     field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
     field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
@@ -40770,6 +41460,7 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
+    field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
@@ -40851,9 +41542,13 @@
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
     field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL = "vvm_cellular_data_required_bool";
+    field public static final java.lang.String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
     field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
+    field public static final java.lang.String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY = "vvm_disabled_capabilities_string_array";
+    field public static final java.lang.String KEY_VVM_LEGACY_MODE_ENABLED_BOOL = "vvm_legacy_mode_enabled_bool";
     field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
     field public static final java.lang.String KEY_VVM_PREFETCH_BOOL = "vvm_prefetch_bool";
+    field public static final java.lang.String KEY_VVM_SSL_ENABLED_BOOL = "vvm_ssl_enabled_bool";
     field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
     field public static final java.lang.String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
   }
@@ -41379,6 +42074,7 @@
     method public void enableVideoCalling(boolean);
     method public boolean endCall();
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+    method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
     method public int getCallState();
     method public android.os.PersistableBundle getCarrierConfig();
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
@@ -41422,6 +42118,7 @@
     method public int getSimState(int);
     method public java.lang.String getSubscriberId();
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
+    method public java.lang.String getVisualVoicemailPackageName(android.telecom.PhoneAccountHandle);
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
     method public int getVoiceNetworkType();
@@ -41435,6 +42132,7 @@
     method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String);
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
+    method public boolean isConcurrentVoiceAndDataAllowed();
     method public boolean isDataConnectivityPossible();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isIdle();
@@ -41454,6 +42152,7 @@
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler);
     method public void sendUssdRequest(java.lang.String, int, android.telephony.TelephonyManager.OnReceiveUssdResponseCallback, android.os.Handler);
+    method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method public void setDataEnabled(boolean);
     method public void setDataEnabled(int, boolean);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
@@ -41498,6 +42197,7 @@
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+    field public static final java.lang.String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
     field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
     field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
     field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
@@ -41506,6 +42206,7 @@
     field public static final java.lang.String EXTRA_STATE_OFFHOOK;
     field public static final java.lang.String EXTRA_STATE_RINGING;
     field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
+    field public static final java.lang.String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
     field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
     field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -41712,6 +42413,15 @@
 
 }
 
+package android.telephony.ims {
+
+  public class ImsServiceBase extends android.app.Service {
+    ctor public ImsServiceBase();
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+}
+
 package android.test {
 
   public abstract deprecated class ActivityInstrumentationTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase {
@@ -42056,6 +42766,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.Context createCredentialProtectedStorageContext();
     method public android.content.Context createDeviceProtectedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
@@ -42145,6 +42856,7 @@
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public android.content.ComponentName startService(android.content.Intent);
+    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -42208,6 +42920,7 @@
     method public boolean addPermission(android.content.pm.PermissionInfo);
     method public boolean addPermissionAsync(android.content.pm.PermissionInfo);
     method public void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
+    method public boolean canRequestPackageInstalls();
     method public java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]);
     method public int checkPermission(java.lang.String, java.lang.String);
     method public int checkSignatures(java.lang.String, java.lang.String);
@@ -42235,18 +42948,22 @@
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
+    method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
+    method public int getIntentVerificationStatusAsUser(java.lang.String, int);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInstaller getPackageInstaller();
     method public int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] getPackagesForUid(int);
@@ -42262,6 +42979,7 @@
     method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo);
     method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
     method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public java.lang.String[] getSystemSharedLibraryNames();
     method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -42292,7 +43010,9 @@
     method public void setApplicationCategoryHint(java.lang.String, int);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
+    method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
     method public void verifyPendingInstall(int, int);
@@ -42496,6 +43216,65 @@
     method public android.text.Editable newEditable(java.lang.CharSequence);
   }
 
+  public final class FontConfig implements android.os.Parcelable {
+    ctor public FontConfig();
+    ctor public FontConfig(android.text.FontConfig);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Alias> getAliases();
+    method public java.util.List<android.text.FontConfig.Family> getFamilies();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
+  }
+
+  public static final class FontConfig.Alias implements android.os.Parcelable {
+    ctor public FontConfig.Alias(java.lang.String, java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getName();
+    method public java.lang.String getToName();
+    method public int getWeight();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Alias> CREATOR;
+  }
+
+  public static final class FontConfig.Axis implements android.os.Parcelable {
+    ctor public FontConfig.Axis(int, float);
+    method public int describeContents();
+    method public float getStyleValue();
+    method public int getTag();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Axis> CREATOR;
+  }
+
+  public static final class FontConfig.Family implements android.os.Parcelable {
+    ctor public FontConfig.Family(java.lang.String, java.util.List<android.text.FontConfig.Font>, java.lang.String, java.lang.String);
+    ctor public FontConfig.Family(android.text.FontConfig.Family);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Font> getFonts();
+    method public java.lang.String getLanguage();
+    method public java.lang.String getName();
+    method public java.lang.String getVariant();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR;
+  }
+
+  public static final class FontConfig.Font implements android.os.Parcelable {
+    ctor public FontConfig.Font(java.lang.String, int, java.util.List<android.text.FontConfig.Axis>, int, boolean);
+    ctor public FontConfig.Font(android.text.FontConfig.Font);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Axis> getAxes();
+    method public android.os.ParcelFileDescriptor getFd();
+    method public java.lang.String getFontName();
+    method public int getTtcIndex();
+    method public int getWeight();
+    method public boolean isItalic();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Font> CREATOR;
+  }
+
+  public final class FontManager {
+    method public android.text.FontConfig getSystemFonts();
+  }
+
   public abstract interface GetChars implements java.lang.CharSequence {
     method public abstract void getChars(int, int, char[], int);
   }
@@ -42851,22 +43630,6 @@
     method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
   }
 
-  public abstract interface TextAssistant {
-    method public abstract void addLinks(android.text.Spannable, int);
-    method public abstract android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int);
-  }
-
-  public class TextClassification {
-    ctor public TextClassification();
-    method public java.util.Map<java.lang.String, java.lang.Float> getTypeConfidence();
-  }
-
-  public final class TextClassificationManager implements android.text.TextAssistant {
-    method public void addLinks(android.text.Spannable, int);
-    method public java.util.List<android.text.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int);
-  }
-
   public abstract interface TextDirectionHeuristic {
     method public abstract boolean isRtl(char[], int, int);
     method public abstract boolean isRtl(java.lang.CharSequence, int, int);
@@ -42882,13 +43645,6 @@
     field public static final android.text.TextDirectionHeuristic RTL;
   }
 
-  public final class TextLanguage {
-    ctor public TextLanguage(int, int, java.util.Map<java.lang.String, java.lang.Float>);
-    method public int getEndIndex();
-    method public java.util.Map<java.lang.String, java.lang.Float> getLanguageConfidence();
-    method public int getStartIndex();
-  }
-
   public class TextPaint extends android.graphics.Paint {
     ctor public TextPaint();
     ctor public TextPaint(int);
@@ -42901,13 +43657,6 @@
     field public int linkColor;
   }
 
-  public class TextSelection {
-    ctor public TextSelection();
-    method public int getSelectionEndIndex();
-    method public int getSelectionStartIndex();
-    method public android.text.TextClassification getTextClassification();
-  }
-
   public class TextUtils {
     method public static deprecated java.lang.CharSequence commaEllipsize(java.lang.CharSequence, android.text.TextPaint, float, java.lang.String, java.lang.String);
     method public static java.lang.CharSequence concat(java.lang.CharSequence...);
@@ -44461,6 +45210,7 @@
     method public static int getTagCode(java.lang.String);
     method public static java.lang.String getTagName(int);
     method public static void readEvents(int[], java.util.Collection<android.util.EventLog.Event>) throws java.io.IOException;
+    method public static void readEventsOnWrapping(int[], long, java.util.Collection<android.util.EventLog.Event>) throws java.io.IOException;
     method public static int writeEvent(int, int);
     method public static int writeEvent(int, long);
     method public static int writeEvent(int, float);
@@ -45161,7 +45911,9 @@
     method public android.view.Display.Mode[] getSupportedModes();
     method public deprecated float[] getSupportedRefreshRates();
     method public deprecated int getWidth();
+    method public boolean isHdr();
     method public boolean isValid();
+    method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
@@ -45230,7 +45982,7 @@
     method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
     method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
     method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int);
-    method public android.view.View findNextKeyboardNavigationGroup(int, android.view.View, android.view.View, int);
+    method public android.view.View findNextKeyboardNavigationCluster(android.view.View, android.view.View, int);
     method public static android.view.FocusFinder getInstance();
   }
 
@@ -45413,6 +46165,7 @@
     field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
     field public static final int SOURCE_KEYBOARD = 257; // 0x101
     field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_MOUSE_RELATIVE = 131076; // 0x20004
     field public static final int SOURCE_STYLUS = 16386; // 0x4002
     field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
     field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
@@ -46015,7 +46768,7 @@
     method public abstract android.view.SubMenu getSubMenu();
     method public abstract java.lang.CharSequence getTitle();
     method public abstract java.lang.CharSequence getTitleCondensed();
-    method public default java.lang.CharSequence getTooltip();
+    method public default java.lang.CharSequence getTooltipText();
     method public abstract boolean hasSubMenu();
     method public abstract boolean isActionViewExpanded();
     method public abstract boolean isCheckable();
@@ -46042,7 +46795,7 @@
     method public abstract android.view.MenuItem setTitle(java.lang.CharSequence);
     method public abstract android.view.MenuItem setTitle(int);
     method public abstract android.view.MenuItem setTitleCondensed(java.lang.CharSequence);
-    method public default android.view.MenuItem setTooltip(java.lang.CharSequence);
+    method public default android.view.MenuItem setTooltipText(java.lang.CharSequence);
     method public abstract android.view.MenuItem setVisible(boolean);
     field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
     field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
@@ -46528,7 +47281,7 @@
     method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
-    method public void addKeyboardNavigationGroups(int, java.util.Collection<android.view.View>, int);
+    method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
     method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
     method public void addTouchables(java.util.ArrayList<android.view.View>);
@@ -46567,6 +47320,7 @@
     method public void createContextMenu(android.view.ContextMenu);
     method public void destroyDrawingCache();
     method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
+    method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
     method public void dispatchDisplayHint(int);
     method public boolean dispatchDragEvent(android.view.DragEvent);
@@ -46585,6 +47339,7 @@
     method public boolean dispatchNestedPrePerformAccessibilityAction(int, android.os.Bundle);
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public void dispatchPointerCaptureChanged(boolean);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int);
     method public void dispatchProvideStructure(android.view.ViewStructure);
@@ -46650,6 +47405,7 @@
     method public float getElevation();
     method public boolean getFilterTouchesWhenObscured();
     method public boolean getFitsSystemWindows();
+    method public int getFocusable();
     method public java.util.ArrayList<android.view.View> getFocusables(int);
     method public void getFocusedRect(android.graphics.Rect);
     method public android.graphics.drawable.Drawable getForeground();
@@ -46692,7 +47448,6 @@
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
-    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -46736,7 +47491,7 @@
     method public java.lang.Object getTag(int);
     method public int getTextAlignment();
     method public int getTextDirection();
-    method public final java.lang.CharSequence getTooltip();
+    method public final java.lang.CharSequence getTooltipText();
     method public final int getTop();
     method protected float getTopFadingEdgeStrength();
     method protected int getTopPaddingOffset();
@@ -46766,6 +47521,7 @@
     method public boolean hasNestedScrollingParent();
     method public boolean hasOnClickListeners();
     method public boolean hasOverlappingRendering();
+    method public boolean hasPointerCapture();
     method public boolean hasTransientState();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
@@ -46797,7 +47553,6 @@
     method public boolean isInLayout();
     method public boolean isInTouchMode();
     method public final boolean isKeyboardNavigationCluster();
-    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -46820,7 +47575,7 @@
     method public boolean isVerticalFadingEdgeEnabled();
     method public boolean isVerticalScrollBarEnabled();
     method public void jumpDrawablesToCurrentState();
-    method public android.view.View keyboardNavigationGroupSearch(int, android.view.View, int);
+    method public android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public void layout(int, int, int, int);
     method public final void measure(int, int);
     method protected static int[] mergeDrawableStates(int[], int[]);
@@ -46831,6 +47586,7 @@
     method public android.view.WindowInsets onApplyWindowInsets(android.view.WindowInsets);
     method protected void onAttachedToWindow();
     method public void onCancelPendingInputEvents();
+    method public boolean onCapturedPointerEvent(android.view.MotionEvent);
     method public boolean onCheckIsTextEditor();
     method protected void onConfigurationChanged(android.content.res.Configuration);
     method protected void onCreateContextMenu(android.view.ContextMenu);
@@ -46860,6 +47616,7 @@
     method protected void onLayout(boolean, int, int, int, int);
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
+    method public void onPointerCaptureChange(boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void onProvideAutoFillStructure(android.view.ViewStructure, int);
     method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int);
@@ -46902,6 +47659,7 @@
     method public void postOnAnimation(java.lang.Runnable);
     method public void postOnAnimationDelayed(java.lang.Runnable, long);
     method public void refreshDrawableState();
+    method public void releasePointerCapture();
     method public boolean removeCallbacks(java.lang.Runnable);
     method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -46912,6 +47670,7 @@
     method public boolean requestFocus(int, android.graphics.Rect);
     method public final boolean requestFocusFromTouch();
     method public void requestLayout();
+    method public void requestPointerCapture();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
     method public final void requestUnbufferedDispatch(android.view.MotionEvent);
@@ -46955,6 +47714,7 @@
     method public void setFilterTouchesWhenObscured(boolean);
     method public void setFitsSystemWindows(boolean);
     method public void setFocusable(boolean);
+    method public void setFocusable(int);
     method public void setFocusableInTouchMode(boolean);
     method public void setFocusedByDefault(boolean);
     method public void setForeground(android.graphics.drawable.Drawable);
@@ -46970,7 +47730,6 @@
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
     method public void setKeyboardNavigationCluster(boolean);
-    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -46988,8 +47747,8 @@
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
-    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
+    method public void setOnCapturedPointerListener(android.view.View.OnCapturedPointerListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
     method public void setOnCreateContextMenuListener(android.view.View.OnCreateContextMenuListener);
@@ -47037,7 +47796,7 @@
     method public void setTag(int, java.lang.Object);
     method public void setTextAlignment(int);
     method public void setTextDirection(int);
-    method public final void setTooltip(java.lang.CharSequence);
+    method public final void setTooltipText(java.lang.CharSequence);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
     method public final void setTransitionName(java.lang.String);
@@ -47094,8 +47853,10 @@
     field protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET;
     field public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2; // 0x2
     field public static final int FIND_VIEWS_WITH_TEXT = 1; // 0x1
+    field public static final int FOCUSABLE = 1; // 0x1
     field public static final int FOCUSABLES_ALL = 0; // 0x0
     field public static final int FOCUSABLES_TOUCH_MODE = 1; // 0x1
+    field public static final int FOCUSABLE_AUTO = 16; // 0x10
     field protected static final int[] FOCUSED_SELECTED_STATE_SET;
     field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_STATE_SET;
@@ -47114,8 +47875,6 @@
     field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
     field public static final int INVISIBLE = 4; // 0x4
     field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
-    field public static final int KEYBOARD_NAVIGATION_GROUP_CLUSTER = 1; // 0x1
-    field public static final int KEYBOARD_NAVIGATION_GROUP_SECTION = 2; // 0x2
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
     field public static final int LAYER_TYPE_NONE = 0; // 0x0
     field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
@@ -47127,6 +47886,7 @@
     field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
     field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
     field public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field public static final int NOT_FOCUSABLE = 0; // 0x0
     field public static final int NO_ID = -1; // 0xffffffff
     field public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
     field public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
@@ -47261,6 +48021,10 @@
     method public abstract void onViewDetachedFromWindow(android.view.View);
   }
 
+  public static abstract interface View.OnCapturedPointerListener {
+    method public abstract boolean onCapturedPointer(android.view.View, android.view.MotionEvent);
+  }
+
   public static abstract interface View.OnClickListener {
     method public abstract void onClick(android.view.View);
   }
@@ -47634,7 +48398,7 @@
     method public abstract boolean isLayoutRequested();
     method public abstract boolean isTextAlignmentResolved();
     method public abstract boolean isTextDirectionResolved();
-    method public abstract android.view.View keyboardNavigationGroupSearch(int, android.view.View, int);
+    method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
     method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
     method public abstract boolean onNestedPreFling(android.view.View, float, float);
@@ -47831,6 +48595,7 @@
     method public boolean getAllowReturnTransitionOverlap();
     method public final android.view.WindowManager.LayoutParams getAttributes();
     method public final android.view.Window.Callback getCallback();
+    method public int getColorMode();
     method public final android.view.Window getContainer();
     method public android.transition.Scene getContentScene();
     method public final android.content.Context getContext();
@@ -47887,6 +48652,7 @@
     method public abstract void setChildDrawable(int, android.graphics.drawable.Drawable);
     method public abstract void setChildInt(int, int);
     method public void setClipToOutline(boolean);
+    method public void setColorMode(int);
     method public void setContainer(android.view.Window);
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
@@ -47992,6 +48758,7 @@
     method public abstract boolean onMenuItemSelected(int, android.view.MenuItem);
     method public abstract boolean onMenuOpened(int, android.view.Menu);
     method public abstract void onPanelClosed(int, android.view.Menu);
+    method public default void onPointerCaptureChanged(boolean);
     method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
     method public abstract boolean onSearchRequested();
@@ -48086,9 +48853,11 @@
     method public final int copyFrom(android.view.WindowManager.LayoutParams);
     method public java.lang.String debug(java.lang.String);
     method public int describeContents();
+    method public int getColorMode();
     method public final java.lang.CharSequence getTitle();
     method public final long getUserActivityTimeout();
     method public static boolean mayUseInputMethod(int);
+    method public void setColorMode(int);
     method public final void setTitle(java.lang.CharSequence);
     method public final void setUserActivityTimeout(long);
     method public void writeToParcel(android.os.Parcel, int);
@@ -48958,6 +49727,13 @@
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
   }
 
+  public final class AutoFillManager {
+    method public void updateAutoFillInput(android.view.View, int);
+    method public void updateAutoFillInput(android.view.View, int, android.graphics.Rect, int);
+    field public static final int FLAG_UPDATE_UI_HIDE = 2; // 0x2
+    field public static final int FLAG_UPDATE_UI_SHOW = 1; // 0x1
+  }
+
   public final class AutoFillType implements android.os.Parcelable {
     method public int describeContents();
     method public static android.view.autofill.AutoFillType forList();
@@ -48992,6 +49768,8 @@
   public static final class Dataset.Builder {
     ctor public Dataset.Builder(java.lang.CharSequence);
     method public android.view.autofill.Dataset build();
+    method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
+    method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
     method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
     method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
@@ -49007,6 +49785,8 @@
     method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
     method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
     method public android.view.autofill.FillResponse build();
+    method public android.view.autofill.FillResponse.Builder requiresCustomAuthentication(android.os.Bundle, int);
+    method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
     method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
@@ -49017,7 +49797,7 @@
 
   public static abstract class VirtualViewDelegate.Callback {
     ctor public VirtualViewDelegate.Callback();
-    method public void onFocusChanged(int, boolean);
+    method public void onAutoFillInputUpdated(int, android.graphics.Rect, int);
     method public void onNodeRemoved(int...);
     method public void onValueChanged(int);
   }
@@ -49424,6 +50204,83 @@
 
 }
 
+package android.view.textclassifier {
+
+  public abstract interface LinksInfo {
+    method public abstract boolean apply(java.lang.CharSequence);
+  }
+
+  public final class TextClassificationManager {
+    method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
+    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+  }
+
+  public final class TextClassificationResult {
+    method public float getConfidenceScore(java.lang.String);
+    method public java.lang.String getEntity(int);
+    method public int getEntityCount();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public android.content.Intent getIntent();
+    method public java.lang.CharSequence getLabel();
+    method public android.view.View.OnClickListener getOnClickListener();
+    method public java.lang.String getText();
+  }
+
+  public static final class TextClassificationResult.Builder {
+    ctor public TextClassificationResult.Builder();
+    method public android.view.textclassifier.TextClassificationResult build();
+    method public android.view.textclassifier.TextClassificationResult.Builder setEntityType(java.lang.String, float);
+    method public android.view.textclassifier.TextClassificationResult.Builder setIcon(android.graphics.drawable.Drawable);
+    method public android.view.textclassifier.TextClassificationResult.Builder setIntent(android.content.Intent);
+    method public android.view.textclassifier.TextClassificationResult.Builder setLabel(java.lang.String);
+    method public android.view.textclassifier.TextClassificationResult.Builder setOnClickListener(android.view.View.OnClickListener);
+    method public android.view.textclassifier.TextClassificationResult.Builder setText(java.lang.String);
+  }
+
+  public abstract interface TextClassifier {
+    method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
+    method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
+    method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+    field public static final android.view.textclassifier.TextClassifier NO_OP;
+    field public static final java.lang.String TYPE_ADDRESS = "address";
+    field public static final java.lang.String TYPE_EMAIL = "email";
+    field public static final java.lang.String TYPE_OTHER = "other";
+    field public static final java.lang.String TYPE_PHONE = "phone";
+  }
+
+  public static abstract class TextClassifier.EntityType implements java.lang.annotation.Annotation {
+  }
+
+  public final class TextLanguage {
+    method public float getConfidenceScore(java.util.Locale);
+    method public int getEndIndex();
+    method public java.util.Locale getLanguage(int);
+    method public int getLanguageCount();
+    method public int getStartIndex();
+  }
+
+  public static final class TextLanguage.Builder {
+    ctor public TextLanguage.Builder(int, int);
+    method public android.view.textclassifier.TextLanguage build();
+    method public android.view.textclassifier.TextLanguage.Builder setLanguage(java.util.Locale, float);
+  }
+
+  public final class TextSelection {
+    method public float getConfidenceScore(java.lang.String);
+    method public java.lang.String getEntity(int);
+    method public int getEntityCount();
+    method public int getSelectionEndIndex();
+    method public int getSelectionStartIndex();
+  }
+
+  public static final class TextSelection.Builder {
+    ctor public TextSelection.Builder(int, int);
+    method public android.view.textclassifier.TextSelection build();
+    method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
+  }
+
+}
+
 package android.view.textservice {
 
   public final class SentenceSuggestionsInfo implements android.os.Parcelable {
@@ -49708,6 +50565,7 @@
   public abstract class RenderProcessGoneDetail {
     ctor public RenderProcessGoneDetail();
     method public abstract boolean didCrash();
+    method public abstract int rendererPriorityAtExit();
   }
 
   public class ServiceWorkerClient {
@@ -50163,6 +51021,8 @@
     method public deprecated java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String);
     method public java.lang.String getOriginalUrl();
     method public int getProgress();
+    method public boolean getRendererPriorityWaivedWhenNotVisible();
+    method public int getRendererRequestedPriority();
     method public deprecated float getScale();
     method public android.webkit.WebSettings getSettings();
     method public java.lang.String getTitle();
@@ -50211,6 +51071,7 @@
     method public deprecated void setMapTrackballToArrowKeys(boolean);
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
+    method public void setRendererPriorityPolicy(int, boolean);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
@@ -50221,6 +51082,9 @@
     method public boolean zoomIn();
     method public boolean zoomOut();
     field public static final java.lang.String DATA_REDUCTION_PROXY_SETTING_CHANGED = "android.webkit.DATA_REDUCTION_PROXY_SETTING_CHANGED";
+    field public static final int RENDERER_PRIORITY_BOUND = 1; // 0x1
+    field public static final int RENDERER_PRIORITY_IMPORTANT = 2; // 0x2
+    field public static final int RENDERER_PRIORITY_WAIVED = 0; // 0x0
     field public static final java.lang.String SCHEME_GEO = "geo:0,0?q=";
     field public static final java.lang.String SCHEME_MAILTO = "mailto:";
     field public static final java.lang.String SCHEME_TEL = "tel:";
@@ -50354,6 +51218,7 @@
     method public java.lang.String getErrorString(android.content.Context, int);
     method public int getPackageId(android.content.res.Resources, java.lang.String);
     method public void invokeDrawGlFunctor(android.view.View, long, boolean);
+    method public boolean isMultiProcessEnabled();
     method public boolean isTraceTagEnabled();
     method public void setOnTraceEnabledChangeListener(android.webkit.WebViewDelegate.OnTraceEnabledChangeListener);
   }
@@ -50442,6 +51307,8 @@
     method public abstract java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String);
     method public abstract java.lang.String getOriginalUrl();
     method public abstract int getProgress();
+    method public abstract boolean getRendererPriorityWaivedWhenNotVisible();
+    method public abstract int getRendererRequestedPriority();
     method public abstract float getScale();
     method public abstract android.webkit.WebViewProvider.ScrollDelegate getScrollDelegate();
     method public abstract android.webkit.WebSettings getSettings();
@@ -50496,6 +51363,7 @@
     method public abstract void setMapTrackballToArrowKeys(boolean);
     method public abstract void setNetworkAvailable(boolean);
     method public abstract void setPictureListener(android.webkit.WebView.PictureListener);
+    method public abstract void setRendererPriorityPolicy(int, boolean);
     method public abstract void setVerticalScrollbarOverlay(boolean);
     method public abstract void setWebChromeClient(android.webkit.WebChromeClient);
     method public abstract void setWebViewClient(android.webkit.WebViewClient);
@@ -52788,6 +53656,10 @@
     method public void endBatchEdit();
     method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText);
     method public final int getAutoLinkMask();
+    method public int getAutoSizeMaxTextSize();
+    method public int getAutoSizeMinTextSize();
+    method public int getAutoSizeStepGranularity();
+    method public int getAutoSizeTextType();
     method public int getBreakStrategy();
     method public int getCompoundDrawablePadding();
     method public android.content.res.ColorStateList getCompoundDrawableTintList();
@@ -52813,7 +53685,6 @@
     method public int getExtendedPaddingTop();
     method public android.text.InputFilter[] getFilters();
     method public java.lang.String getFontFeatureSettings();
-    method public java.lang.String getFontVariationSettings();
     method public boolean getFreezesText();
     method public int getGravity();
     method public int getHighlightColor();
@@ -52860,7 +53731,7 @@
     method public float getShadowRadius();
     method public final boolean getShowSoftInputOnFocus();
     method public java.lang.CharSequence getText();
-    method public android.text.TextAssistant getTextAssistant();
+    method public android.view.textclassifier.TextClassifier getTextClassifier();
     method public final android.content.res.ColorStateList getTextColors();
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
@@ -52897,6 +53768,10 @@
     method public void removeTextChangedListener(android.text.TextWatcher);
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
+    method public void setAutoSizeMaxTextSize(int, float);
+    method public void setAutoSizeMinTextSize(int, float);
+    method public void setAutoSizeStepGranularity(int, float);
+    method public void setAutoSizeTextType(int);
     method public void setBreakStrategy(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
@@ -52919,7 +53794,6 @@
     method public void setExtractedText(android.view.inputmethod.ExtractedText);
     method public void setFilters(android.text.InputFilter[]);
     method public void setFontFeatureSettings(java.lang.String);
-    method public void setFontVariationSettings(java.lang.String);
     method protected boolean setFrame(int, int, int, int);
     method public void setFreezesText(boolean);
     method public void setGravity(int);
@@ -52973,7 +53847,7 @@
     method public final void setText(int, android.widget.TextView.BufferType);
     method public void setTextAppearance(int);
     method public deprecated void setTextAppearance(android.content.Context, int);
-    method public void setTextAssistant(android.text.TextAssistant);
+    method public void setTextClassifier(android.view.textclassifier.TextClassifier);
     method public void setTextColor(int);
     method public void setTextColor(android.content.res.ColorStateList);
     method public void setTextIsSelectable(boolean);
@@ -52988,8 +53862,8 @@
     method public void setTypeface(android.graphics.Typeface, int);
     method public void setTypeface(android.graphics.Typeface);
     method public void setWidth(int);
-    field public static final int AUTO_SIZE_TYPE_NONE = 0; // 0x0
-    field public static final int AUTO_SIZE_TYPE_XY = 1; // 0x1
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_XY = 1; // 0x1
   }
 
   public static final class TextView.BufferType extends java.lang.Enum {
@@ -53608,6 +54482,10 @@
     method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
   }
 
+  public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+    ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
+  }
+
   public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
     ctor public PathClassLoader(java.lang.String, java.lang.ClassLoader);
     ctor public PathClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader);
@@ -56706,6 +57584,133 @@
 
 }
 
+package java.lang.invoke {
+
+  public class LambdaConversionException extends java.lang.Exception {
+    ctor public LambdaConversionException();
+    ctor public LambdaConversionException(java.lang.String);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable, boolean, boolean);
+  }
+
+  public abstract class MethodHandle {
+    method public java.lang.invoke.MethodHandle asFixedArity();
+    method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
+    method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
+    method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
+    method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
+    method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+    method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
+    method public boolean isVarargsCollector();
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public abstract interface MethodHandleInfo {
+    method public abstract java.lang.Class<?> getDeclaringClass();
+    method public abstract java.lang.invoke.MethodType getMethodType();
+    method public abstract int getModifiers();
+    method public abstract java.lang.String getName();
+    method public abstract int getReferenceKind();
+    method public default boolean isVarArgs();
+    method public static boolean refKindIsField(int);
+    method public static boolean refKindIsValid(int);
+    method public static java.lang.String refKindName(int);
+    method public static java.lang.String referenceKindToString(int);
+    method public abstract <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandles.Lookup);
+    method public static java.lang.String toString(int, java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType);
+    field public static final int REF_getField = 1; // 0x1
+    field public static final int REF_getStatic = 2; // 0x2
+    field public static final int REF_invokeInterface = 9; // 0x9
+    field public static final int REF_invokeSpecial = 7; // 0x7
+    field public static final int REF_invokeStatic = 6; // 0x6
+    field public static final int REF_invokeVirtual = 5; // 0x5
+    field public static final int REF_newInvokeSpecial = 8; // 0x8
+    field public static final int REF_putField = 3; // 0x3
+    field public static final int REF_putStatic = 4; // 0x4
+  }
+
+  public class MethodHandles {
+    method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandles.Lookup lookup();
+    method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
+    method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+    method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
+  }
+
+  public static final class MethodHandles.Lookup {
+    method public java.lang.invoke.MethodHandle bind(java.lang.Object, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findConstructor(java.lang.Class<?>, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSpecial(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStatic(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStaticGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findStaticSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findVirtual(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
+    method public java.lang.Class<?> lookupClass();
+    method public int lookupModes();
+    method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSpecial(java.lang.reflect.Method, java.lang.Class<?>) throws java.lang.IllegalAccessException;
+    field public static final int PACKAGE = 8; // 0x8
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+    field public static final int PUBLIC = 1; // 0x1
+  }
+
+  public final class MethodType implements java.io.Serializable {
+    method public java.lang.invoke.MethodType appendParameterTypes(java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType appendParameterTypes(java.util.List<java.lang.Class<?>>);
+    method public java.lang.invoke.MethodType changeParameterType(int, java.lang.Class<?>);
+    method public java.lang.invoke.MethodType changeReturnType(java.lang.Class<?>);
+    method public java.lang.invoke.MethodType dropParameterTypes(int, int);
+    method public java.lang.invoke.MethodType erase();
+    method public static java.lang.invoke.MethodType fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) throws java.lang.IllegalArgumentException, java.lang.TypeNotPresentException;
+    method public java.lang.invoke.MethodType generic();
+    method public static java.lang.invoke.MethodType genericMethodType(int, boolean);
+    method public static java.lang.invoke.MethodType genericMethodType(int);
+    method public boolean hasPrimitives();
+    method public boolean hasWrappers();
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>[]);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.invoke.MethodType);
+    method public java.lang.Class<?>[] parameterArray();
+    method public int parameterCount();
+    method public java.util.List<java.lang.Class<?>> parameterList();
+    method public java.lang.Class<?> parameterType(int);
+    method public java.lang.Class<?> returnType();
+    method public java.lang.String toMethodDescriptorString();
+    method public java.lang.invoke.MethodType unwrap();
+    method public java.lang.invoke.MethodType wrap();
+  }
+
+  public class WrongMethodTypeException extends java.lang.RuntimeException {
+    ctor public WrongMethodTypeException();
+    ctor public WrongMethodTypeException(java.lang.String);
+  }
+
+}
+
 package java.lang.ref {
 
   public class PhantomReference<T> extends java.lang.ref.Reference {
@@ -64985,6 +65990,9 @@
     method public static <E> java.util.Collection<E> checkedCollection(java.util.Collection<E>, java.lang.Class<E>);
     method public static <E> java.util.List<E> checkedList(java.util.List<E>, java.lang.Class<E>);
     method public static <K, V> java.util.Map<K, V> checkedMap(java.util.Map<K, V>, java.lang.Class<K>, java.lang.Class<V>);
+    method public static <K, V> java.util.NavigableMap<K, V> checkedNavigableMap(java.util.NavigableMap<K, V>, java.lang.Class<K>, java.lang.Class<V>);
+    method public static <E> java.util.NavigableSet<E> checkedNavigableSet(java.util.NavigableSet<E>, java.lang.Class<E>);
+    method public static <E> java.util.Queue<E> checkedQueue(java.util.Queue<E>, java.lang.Class<E>);
     method public static <E> java.util.Set<E> checkedSet(java.util.Set<E>, java.lang.Class<E>);
     method public static <K, V> java.util.SortedMap<K, V> checkedSortedMap(java.util.SortedMap<K, V>, java.lang.Class<K>, java.lang.Class<V>);
     method public static <E> java.util.SortedSet<E> checkedSortedSet(java.util.SortedSet<E>, java.lang.Class<E>);
@@ -64995,7 +66003,11 @@
     method public static final <T> java.util.List<T> emptyList();
     method public static <T> java.util.ListIterator<T> emptyListIterator();
     method public static final <K, V> java.util.Map<K, V> emptyMap();
+    method public static final <K, V> java.util.NavigableMap<K, V> emptyNavigableMap();
+    method public static <E> java.util.NavigableSet<E> emptyNavigableSet();
     method public static final <T> java.util.Set<T> emptySet();
+    method public static final <K, V> java.util.SortedMap<K, V> emptySortedMap();
+    method public static <E> java.util.SortedSet<E> emptySortedSet();
     method public static <T> java.util.Enumeration<T> enumeration(java.util.Collection<T>);
     method public static <T> void fill(java.util.List<? super T>, T);
     method public static int frequency(java.util.Collection<?>, java.lang.Object);
@@ -65024,12 +66036,16 @@
     method public static <T> java.util.Collection<T> synchronizedCollection(java.util.Collection<T>);
     method public static <T> java.util.List<T> synchronizedList(java.util.List<T>);
     method public static <K, V> java.util.Map<K, V> synchronizedMap(java.util.Map<K, V>);
+    method public static <K, V> java.util.NavigableMap<K, V> synchronizedNavigableMap(java.util.NavigableMap<K, V>);
+    method public static <T> java.util.NavigableSet<T> synchronizedNavigableSet(java.util.NavigableSet<T>);
     method public static <T> java.util.Set<T> synchronizedSet(java.util.Set<T>);
     method public static <K, V> java.util.SortedMap<K, V> synchronizedSortedMap(java.util.SortedMap<K, V>);
     method public static <T> java.util.SortedSet<T> synchronizedSortedSet(java.util.SortedSet<T>);
     method public static <T> java.util.Collection<T> unmodifiableCollection(java.util.Collection<? extends T>);
     method public static <T> java.util.List<T> unmodifiableList(java.util.List<? extends T>);
     method public static <K, V> java.util.Map<K, V> unmodifiableMap(java.util.Map<? extends K, ? extends V>);
+    method public static <K, V> java.util.NavigableMap<K, V> unmodifiableNavigableMap(java.util.NavigableMap<K, ? extends V>);
+    method public static <T> java.util.NavigableSet<T> unmodifiableNavigableSet(java.util.NavigableSet<T>);
     method public static <T> java.util.Set<T> unmodifiableSet(java.util.Set<? extends T>);
     method public static <K, V> java.util.SortedMap<K, V> unmodifiableSortedMap(java.util.SortedMap<K, ? extends V>);
     method public static <T> java.util.SortedSet<T> unmodifiableSortedSet(java.util.SortedSet<T>);
@@ -68474,14 +69490,14 @@
     method public java.util.logging.ErrorManager getErrorManager();
     method public java.util.logging.Filter getFilter();
     method public java.util.logging.Formatter getFormatter();
-    method public synchronized java.util.logging.Level getLevel();
+    method public java.util.logging.Level getLevel();
     method public boolean isLoggable(java.util.logging.LogRecord);
     method public abstract void publish(java.util.logging.LogRecord);
     method protected void reportError(java.lang.String, java.lang.Exception, int);
-    method public void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
-    method public void setErrorManager(java.util.logging.ErrorManager);
-    method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
-    method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+    method public synchronized void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
+    method public synchronized void setErrorManager(java.util.logging.ErrorManager);
+    method public synchronized void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+    method public synchronized void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
     method public synchronized void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
   }
 
@@ -68508,7 +69524,7 @@
   public class LogManager {
     ctor protected LogManager();
     method public boolean addLogger(java.util.logging.Logger);
-    method public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+    method public deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
     method public void checkAccess() throws java.lang.SecurityException;
     method public static java.util.logging.LogManager getLogManager();
     method public java.util.logging.Logger getLogger(java.lang.String);
@@ -68517,7 +69533,7 @@
     method public java.lang.String getProperty(java.lang.String);
     method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
     method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
-    method public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+    method public deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
     method public void reset() throws java.lang.SecurityException;
     field public static final java.lang.String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
   }
@@ -68554,14 +69570,18 @@
     ctor protected Logger(java.lang.String, java.lang.String);
     method public void addHandler(java.util.logging.Handler) throws java.lang.SecurityException;
     method public void config(java.lang.String);
+    method public void config(java.util.function.Supplier<java.lang.String>);
     method public void entering(java.lang.String, java.lang.String);
     method public void entering(java.lang.String, java.lang.String, java.lang.Object);
     method public void entering(java.lang.String, java.lang.String, java.lang.Object[]);
     method public void exiting(java.lang.String, java.lang.String);
     method public void exiting(java.lang.String, java.lang.String, java.lang.Object);
     method public void fine(java.lang.String);
+    method public void fine(java.util.function.Supplier<java.lang.String>);
     method public void finer(java.lang.String);
+    method public void finer(java.util.function.Supplier<java.lang.String>);
     method public void finest(java.lang.String);
+    method public void finest(java.util.function.Supplier<java.lang.String>);
     method public static java.util.logging.Logger getAnonymousLogger();
     method public static java.util.logging.Logger getAnonymousLogger(java.lang.String);
     method public java.util.logging.Filter getFilter();
@@ -68576,28 +69596,38 @@
     method public java.lang.String getResourceBundleName();
     method public boolean getUseParentHandlers();
     method public void info(java.lang.String);
+    method public void info(java.util.function.Supplier<java.lang.String>);
     method public boolean isLoggable(java.util.logging.Level);
     method public void log(java.util.logging.LogRecord);
     method public void log(java.util.logging.Level, java.lang.String);
+    method public void log(java.util.logging.Level, java.util.function.Supplier<java.lang.String>);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Object);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Object[]);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Throwable);
+    method public void log(java.util.logging.Level, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String);
+    method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.util.function.Supplier<java.lang.String>);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+    method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
+    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Object...);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Throwable);
     method public void removeHandler(java.util.logging.Handler) throws java.lang.SecurityException;
     method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
     method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
     method public void setParent(java.util.logging.Logger);
+    method public void setResourceBundle(java.util.ResourceBundle);
     method public void setUseParentHandlers(boolean);
     method public void severe(java.lang.String);
+    method public void severe(java.util.function.Supplier<java.lang.String>);
     method public void throwing(java.lang.String, java.lang.String, java.lang.Throwable);
     method public void warning(java.lang.String);
+    method public void warning(java.util.function.Supplier<java.lang.String>);
     field public static final java.lang.String GLOBAL_LOGGER_NAME = "global";
     field public static final deprecated java.util.logging.Logger global;
   }
@@ -68618,10 +69648,10 @@
     ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
     method public void close() throws java.lang.SecurityException;
     method public void flush();
-    method public synchronized java.util.logging.Level getPushLevel();
+    method public java.util.logging.Level getPushLevel();
     method public synchronized void publish(java.util.logging.LogRecord);
     method public synchronized void push();
-    method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
+    method public synchronized void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
   }
 
   public class SimpleFormatter extends java.util.logging.Formatter {
diff --git a/api/test-current.txt b/api/test-current.txt
index 48c7009..556bd41 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -83,6 +83,7 @@
     field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
     field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+    field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
     field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
     field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
     field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -113,6 +114,7 @@
     field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
     field public static final java.lang.String RECORD_AUDIO = "android.permission.RECORD_AUDIO";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
+    field public static final java.lang.String REQUEST_DELETE_PACKAGES = "android.permission.REQUEST_DELETE_PACKAGES";
     field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
@@ -201,6 +203,8 @@
 
   public static final class R.attr {
     ctor public R.attr();
+    field public static final int __removed0 = 16844097; // 0x1010541
+    field public static final int __removed1 = 16844099; // 0x1010543
     field public static final int absListViewStyle = 16842858; // 0x101006a
     field public static final int accessibilityEventTypes = 16843648; // 0x1010380
     field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -361,6 +365,7 @@
     field public static final int centerMedium = 16842959; // 0x10100cf
     field public static final int centerX = 16843170; // 0x10101a2
     field public static final int centerY = 16843171; // 0x10101a3
+    field public static final int certDigest = 16844106; // 0x101054a
     field public static final int checkBoxPreferenceStyle = 16842895; // 0x101008f
     field public static final int checkMark = 16843016; // 0x1010108
     field public static final int checkMarkTint = 16843943; // 0x10104a7
@@ -403,6 +408,7 @@
     field public static final int colorForeground = 16842800; // 0x1010030
     field public static final int colorForegroundInverse = 16843270; // 0x1010206
     field public static final int colorLongPressedHighlight = 16843662; // 0x101038e
+    field public static final int colorMode = 16844108; // 0x101054c
     field public static final int colorMultiSelectHighlight = 16843665; // 0x1010391
     field public static final int colorPressedHighlight = 16843661; // 0x101038d
     field public static final int colorPrimary = 16843827; // 0x1010433
@@ -733,6 +739,7 @@
     field public static final int isScrollContainer = 16843342; // 0x101024e
     field public static final int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
+    field public static final int isolatedSplits = 16844109; // 0x101054d
     field public static final int itemBackground = 16843056; // 0x1010130
     field public static final int itemIconDisabledAlpha = 16843057; // 0x1010131
     field public static final int itemPadding = 16843565; // 0x101032d
@@ -755,7 +762,6 @@
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
     field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
-    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -905,7 +911,6 @@
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
-    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -989,6 +994,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
+    field public static final int primaryContentAlpha = 16843367; // 0x1010267
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -1052,6 +1058,7 @@
     field public static final int resizeable = 16843405; // 0x101028d
     field public static final int resizeableActivity = 16844022; // 0x10104f6
     field public static final int resource = 16842789; // 0x1010025
+    field public static final int restartOnConfigChanges = 16844105; // 0x1010549
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
     field public static final int restrictedAccountType = 16843733; // 0x10103d5
@@ -1164,6 +1171,7 @@
     field public static final int spinnerStyle = 16842881; // 0x1010081
     field public static final int spinnersShown = 16843595; // 0x101034b
     field public static final int splitMotionEvents = 16843503; // 0x10102ef
+    field public static final int splitName = 16844107; // 0x101054b
     field public static final int splitTrack = 16843852; // 0x101044c
     field public static final int spotShadowAlpha = 16843967; // 0x10104bf
     field public static final int src = 16843033; // 0x1010119
@@ -1376,7 +1384,7 @@
     field public static final int toYDelta = 16843209; // 0x10101c9
     field public static final int toYScale = 16843205; // 0x10101c5
     field public static final int toolbarStyle = 16843946; // 0x10104aa
-    field public static final int tooltip = 16844084; // 0x1010534
+    field public static final int tooltipText = 16844084; // 0x1010534
     field public static final int top = 16843182; // 0x10101ae
     field public static final int topBright = 16842955; // 0x10100cb
     field public static final int topDark = 16842951; // 0x10100c7
@@ -1435,7 +1443,7 @@
     field public static final int viewportWidth = 16843778; // 0x1010402
     field public static final int visibility = 16842972; // 0x10100dc
     field public static final int visible = 16843156; // 0x1010194
-    field public static final int visibleToEphemeral = 16844095; // 0x101053f
+    field public static final int visibleToInstantApps = 16844095; // 0x101053f
     field public static final int vmSafeMode = 16843448; // 0x10102b8
     field public static final int voiceIcon = 16843908; // 0x1010484
     field public static final int voiceLanguage = 16843349; // 0x1010255
@@ -1812,6 +1820,7 @@
     field public static final int tabs = 16908307; // 0x1020013
     field public static final int text1 = 16908308; // 0x1020014
     field public static final int text2 = 16908309; // 0x1020015
+    field public static final int textAssist = 16908353; // 0x1020041
     field public static final int title = 16908310; // 0x1020016
     field public static final int toggle = 16908311; // 0x1020017
     field public static final int undo = 16908338; // 0x1020032
@@ -2878,15 +2887,18 @@
   public class AccountManager {
     method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
-    method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, int[]);
+    method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, java.util.Map<java.lang.Integer, java.lang.Integer>);
     method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
+    method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[]);
     method public java.lang.String blockingGetAuthToken(android.accounts.Account, java.lang.String, boolean) throws android.accounts.AuthenticatorException, java.io.IOException, android.accounts.OperationCanceledException;
     method public void clearPassword(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(java.lang.String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public static android.accounts.AccountManager get(android.content.Context);
+    method public int getAccountVisibility(android.accounts.Account, int);
     method public android.accounts.Account[] getAccounts();
+    method public java.util.Map<android.accounts.Account, java.lang.Integer> getAccountsAndVisibilityForPackage(java.lang.String, java.lang.String);
     method public android.accounts.Account[] getAccountsByType(java.lang.String);
     method public android.accounts.AccountManagerFuture<android.accounts.Account[]> getAccountsByTypeAndFeatures(java.lang.String, java.lang.String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler);
     method public android.accounts.Account[] getAccountsByTypeForPackage(java.lang.String, java.lang.String);
@@ -2897,13 +2909,11 @@
     method public android.accounts.AuthenticatorDescription[] getAuthenticatorTypes();
     method public java.lang.String getPassword(android.accounts.Account);
     method public java.lang.String getPreviousName(android.accounts.Account);
-    method public int[] getRequestingUidsForType(java.lang.String);
+    method public java.util.Map<java.lang.Integer, java.lang.Integer> getUidsAndVisibilityForAccount(android.accounts.Account);
     method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public void invalidateAuthToken(java.lang.String, java.lang.String);
-    method public boolean isAccountVisible(android.accounts.Account, int);
     method public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
-    method public boolean makeAccountVisible(android.accounts.Account, int);
     method public static deprecated android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], boolean, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
     method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.List<android.accounts.Account>, java.lang.String[], java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
     method public boolean notifyAccountAuthenticated(android.accounts.Account);
@@ -2911,9 +2921,9 @@
     method public deprecated android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean removeAccountExplicitly(android.accounts.Account);
-    method public boolean removeAccountVisibility(android.accounts.Account, int);
     method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
     method public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
+    method public boolean setAccountVisibility(android.accounts.Account, int, int);
     method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
     method public void setPassword(android.accounts.Account, java.lang.String);
     method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
@@ -2952,7 +2962,14 @@
     field public static final java.lang.String KEY_LAST_AUTHENTICATED_TIME = "lastAuthenticatedTime";
     field public static final java.lang.String KEY_PASSWORD = "password";
     field public static final java.lang.String KEY_USERDATA = "userdata";
-    field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+    field public static final deprecated java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+    field public static final int UID_KEY_DEFAULT_LEGACY_VISIBILITY = -3; // 0xfffffffd
+    field public static final int UID_KEY_DEFAULT_VISIBILITY = -2; // 0xfffffffe
+    field public static final int VISIBILITY_NOT_VISIBLE = 3; // 0x3
+    field public static final int VISIBILITY_UNDEFINED = 0; // 0x0
+    field public static final int VISIBILITY_USER_MANAGED_NOT_VISIBLE = 4; // 0x4
+    field public static final int VISIBILITY_USER_MANAGED_VISIBLE = 2; // 0x2
+    field public static final int VISIBILITY_VISIBLE = 1; // 0x1
   }
 
   public abstract interface AccountManagerCallback<V> {
@@ -3051,8 +3068,10 @@
 
   public static abstract interface Animator.AnimatorListener {
     method public abstract void onAnimationCancel(android.animation.Animator);
+    method public default void onAnimationEnd(android.animation.Animator, boolean);
     method public abstract void onAnimationEnd(android.animation.Animator);
     method public abstract void onAnimationRepeat(android.animation.Animator);
+    method public default void onAnimationStart(android.animation.Animator, boolean);
     method public abstract void onAnimationStart(android.animation.Animator);
   }
 
@@ -3088,6 +3107,8 @@
     method public void playSequentially(java.util.List<android.animation.Animator>);
     method public void playTogether(android.animation.Animator...);
     method public void playTogether(java.util.Collection<android.animation.Animator>);
+    method public void reverse();
+    method public void setCurrentPlayTime(long);
     method public android.animation.AnimatorSet setDuration(long);
     method public void setInterpolator(android.animation.TimeInterpolator);
     method public void setStartDelay(long);
@@ -3492,8 +3513,7 @@
     method public boolean dispatchTrackballEvent(android.view.MotionEvent);
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public void enterPictureInPictureMode();
-    method public void enterPictureInPictureMode(float);
-    method public void enterPictureInPictureModeOnMoveToBackground(boolean);
+    method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
     method public android.view.View findViewById(int);
     method public void finish();
     method public void finishActivity(int);
@@ -3526,7 +3546,6 @@
     method public int getRequestedOrientation();
     method public final android.view.SearchEvent getSearchEvent();
     method public int getTaskId();
-    method public android.text.TextAssistant getTextAssistant();
     method public final java.lang.CharSequence getTitle();
     method public final int getTitleColor();
     method public android.app.VoiceInteractor getVoiceInteractor();
@@ -3666,8 +3685,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
-    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
-    method public void setPictureInPictureAspectRatio(float);
+    method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
     method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -3677,7 +3695,6 @@
     method public final void setResult(int, android.content.Intent);
     method public final deprecated void setSecondaryProgress(int);
     method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
-    method public void setTextAssistant(android.text.TextAssistant);
     method public void setTitle(java.lang.CharSequence);
     method public void setTitle(int);
     method public deprecated void setTitleColor(int);
@@ -4601,6 +4618,7 @@
 
   public abstract class FragmentContainer {
     ctor public FragmentContainer();
+    method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
     method public abstract android.view.View onFindViewById(int);
     method public abstract boolean onHasView();
   }
@@ -4836,11 +4854,13 @@
   public static class Instrumentation.ActivityMonitor {
     ctor public Instrumentation.ActivityMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean);
     ctor public Instrumentation.ActivityMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean);
+    ctor public Instrumentation.ActivityMonitor();
     method public final android.content.IntentFilter getFilter();
     method public final int getHits();
     method public final android.app.Activity getLastActivity();
     method public final android.app.Instrumentation.ActivityResult getResult();
     method public final boolean isBlocking();
+    method public android.app.Instrumentation.ActivityResult onMatchIntent(android.content.Intent);
     method public final android.app.Activity waitForActivity();
     method public final android.app.Activity waitForActivityWithTimeout(long);
   }
@@ -5012,6 +5032,7 @@
     method public android.graphics.drawable.Icon getLargeIcon();
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
+    method public long getTimeout();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
     field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -5035,11 +5056,14 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+    field public static final java.lang.String EXTRA_COLORIZED = "android.colorized";
     field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
     field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final java.lang.String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
     field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
@@ -5118,6 +5142,7 @@
     method public android.app.Notification.Action clone();
     method public int describeContents();
     method public boolean getAllowGeneratedReplies();
+    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
     method public android.os.Bundle getExtras();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
@@ -5182,7 +5207,8 @@
   }
 
   public static class Notification.Builder {
-    ctor public Notification.Builder(android.content.Context);
+    ctor public Notification.Builder(android.content.Context, java.lang.String);
+    ctor public deprecated Notification.Builder(android.content.Context);
     method public deprecated android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
     method public android.app.Notification.Builder addAction(android.app.Notification.Action);
     method public android.app.Notification.Builder addExtras(android.os.Bundle);
@@ -5201,6 +5227,7 @@
     method public android.app.Notification.Builder setChannel(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
     method public android.app.Notification.Builder setColor(int);
+    method public android.app.Notification.Builder setColorized(boolean);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
@@ -5238,6 +5265,7 @@
     method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
     method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
     method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
+    method public android.app.Notification.Builder setTimeout(long);
     method public android.app.Notification.Builder setUsesChronometer(boolean);
     method public android.app.Notification.Builder setVibrate(long[]);
     method public android.app.Notification.Builder setVisibility(int);
@@ -5304,9 +5332,11 @@
 
   public static class Notification.MessagingStyle extends android.app.Notification.Style {
     ctor public Notification.MessagingStyle(java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addHistoricMessage(android.app.Notification.MessagingStyle.Message);
     method public android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
     method public android.app.Notification.MessagingStyle addMessage(android.app.Notification.MessagingStyle.Message);
     method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.app.Notification.MessagingStyle.Message> getHistoricMessages();
     method public java.util.List<android.app.Notification.MessagingStyle.Message> getMessages();
     method public java.lang.CharSequence getUserDisplayName();
     method public android.app.Notification.MessagingStyle setConversationTitle(java.lang.CharSequence);
@@ -5401,14 +5431,15 @@
     method public boolean canShowBadge();
     method public int describeContents();
     method public void enableVibration(boolean);
+    method public java.lang.String getGroup();
     method public java.lang.String getId();
     method public int getImportance();
     method public int getLockscreenVisibility();
     method public java.lang.CharSequence getName();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
-    method public boolean isAllowed();
     method public void setBypassDnd(boolean);
+    method public void setGroup(java.lang.String);
     method public void setImportance(int);
     method public void setLights(boolean);
     method public void setLockscreenVisibility(int);
@@ -5422,6 +5453,17 @@
     field public static final java.lang.String DEFAULT_CHANNEL_ID = "miscellaneous";
   }
 
+  public final class NotificationChannelGroup implements android.os.Parcelable {
+    ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
+    ctor protected NotificationChannelGroup(android.os.Parcel);
+    method public int describeContents();
+    method public java.util.List<android.app.NotificationChannel> getChannels();
+    method public java.lang.String getId();
+    method public java.lang.CharSequence getName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
+  }
+
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
     method public boolean areNotificationsEnabled();
@@ -5429,6 +5471,8 @@
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
     method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+    method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup>);
     method public void createNotificationChannels(java.util.List<android.app.NotificationChannel>);
     method public void deleteNotificationChannel(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
@@ -5446,6 +5490,7 @@
     method public boolean removeAutomaticZenRule(java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -5531,6 +5576,17 @@
     method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
   }
 
+  public final class PictureInPictureArgs implements android.os.Parcelable {
+    ctor public PictureInPictureArgs();
+    ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
+    method public android.app.PictureInPictureArgs clone();
+    method public int describeContents();
+    method public void setActions(java.util.List<android.app.RemoteAction>);
+    method public void setAspectRatio(float);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
+  }
+
   public class Presentation extends android.app.Dialog {
     ctor public Presentation(android.content.Context, android.view.Display);
     ctor public Presentation(android.content.Context, android.view.Display, int);
@@ -5567,6 +5623,18 @@
     field public static final int STYLE_SPINNER = 0; // 0x0
   }
 
+  public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
+    ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
+    method public int describeContents();
+    method public android.app.PendingIntent getUserAction();
+    method public java.lang.CharSequence getUserActionTitle();
+    method public java.lang.CharSequence getUserMessage();
+    method public void showAsDialog(android.app.Activity);
+    method public void showAsNotification(android.content.Context);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR;
+  }
+
   public final class RemoteAction implements android.os.Parcelable {
     ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
     method public android.app.RemoteAction clone();
@@ -5584,14 +5652,18 @@
   }
 
   public final class RemoteInput implements android.os.Parcelable {
+    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
     method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
     method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
     method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getResultKey();
     method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
     field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5603,6 +5675,7 @@
     method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
     method public android.app.RemoteInput build();
     method public android.os.Bundle getExtras();
+    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
     method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
     method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
     method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
@@ -6085,6 +6158,7 @@
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearProfileOwner(android.content.ComponentName);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+    method public android.content.Intent createAdminSupportIntent(java.lang.String);
     method public android.os.UserHandle createAndManageUser(android.content.ComponentName, java.lang.String, android.content.ComponentName, android.os.PersistableBundle, int);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
@@ -6093,16 +6167,18 @@
     method public java.util.List<java.lang.String> getAffiliationIds(android.content.ComponentName);
     method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
-    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
+    method public deprecated java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(android.content.ComponentName);
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
-    method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+    method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
     method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
+    method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String);
+    method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
     method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
     method public java.lang.CharSequence getDeviceOwnerOrganizationName();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
@@ -6128,6 +6204,7 @@
     method public int getPasswordMinimumSymbols(android.content.ComponentName);
     method public int getPasswordMinimumUpperCase(android.content.ComponentName);
     method public int getPasswordQuality(android.content.ComponentName);
+    method public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(android.content.ComponentName);
     method public int getPermissionGrantState(android.content.ComponentName, java.lang.String, java.lang.String);
     method public int getPermissionPolicy(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
@@ -6149,7 +6226,8 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
-    method public boolean isCallerApplicationRestrictionsManagingPackage();
+    method public boolean isBackupServiceEnabled(android.content.ComponentName);
+    method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceManaged();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
@@ -6178,13 +6256,15 @@
     method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String, boolean) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
-    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public deprecated void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
+    method public void setBackupServiceEnabled(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
-    method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
+    method public deprecated void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
     method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
+    method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
     method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -6232,6 +6312,7 @@
     method public void uninstallCaCert(android.content.ComponentName, byte[]);
     method public void wipeData(int);
     field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
+    field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
     field public static final java.lang.String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final java.lang.String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL";
@@ -6241,6 +6322,12 @@
     field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+    field public static final java.lang.String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
+    field public static final java.lang.String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall";
+    field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
+    field public static final java.lang.String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
+    field public static final java.lang.String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
+    field public static final java.lang.String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
     field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
@@ -6248,6 +6335,7 @@
     field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
     field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
     field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
+    field public static final java.lang.String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
     field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
     field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
     field public static final java.lang.String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
@@ -6279,6 +6367,7 @@
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_PORT = "android.app.extra.PROVISIONING_WIFI_PROXY_PORT";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID";
+    field public static final java.lang.String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
     field public static final int FLAG_EVICT_CE_KEY = 1; // 0x1
     field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
     field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
@@ -6306,6 +6395,8 @@
     field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
     field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
     field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
+    field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
+    field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -6350,6 +6441,17 @@
     field public static final android.os.Parcelable.Creator<android.app.admin.SecurityLog.SecurityEvent> CREATOR;
   }
 
+  public final class SystemUpdateInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getReceivedTime();
+    method public int getSecurityPatchState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdateInfo> CREATOR;
+    field public static final int SECURITY_PATCH_STATE_FALSE = 1; // 0x1
+    field public static final int SECURITY_PATCH_STATE_TRUE = 2; // 0x2
+    field public static final int SECURITY_PATCH_STATE_UNKNOWN = 0; // 0x0
+  }
+
   public class SystemUpdatePolicy implements android.os.Parcelable {
     method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy();
     method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy();
@@ -7933,6 +8035,7 @@
     ctor public ClipData(android.content.ClipDescription, android.content.ClipData.Item);
     ctor public ClipData(android.content.ClipData);
     method public void addItem(android.content.ClipData.Item);
+    method public void addItem(android.content.ClipData.Item, android.content.ContentResolver);
     method public int describeContents();
     method public android.content.ClipDescription getDescription();
     method public android.content.ClipData.Item getItemAt(int);
@@ -8319,6 +8422,7 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public abstract android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.Context createDeviceProtectedStorageContext();
     method public abstract android.content.Context createDisplayContext(android.view.Display);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -8453,6 +8557,7 @@
     field public static final java.lang.String DOWNLOAD_SERVICE = "download";
     field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
     field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
+    field public static final java.lang.String FONT_SERVICE = "font";
     field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
     field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
     field public static final java.lang.String INPUT_SERVICE = "input";
@@ -8517,6 +8622,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.Context createDeviceProtectedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -8826,6 +8932,7 @@
     field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
     field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
     field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
+    field public static final java.lang.String ACTION_CLEAR_PACKAGE = "android.intent.action.CLEAR_PACKAGE";
     field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
     field public static final java.lang.String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
@@ -9024,10 +9131,10 @@
     field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
     field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER";
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
-    field public static final java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
-    field public static final java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
     field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
     field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
@@ -9540,6 +9647,10 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     method public final int getThemeResource();
+    field public static final int COLOR_MODE_DEFAULT = 0; // 0x0
+    field public static final int COLOR_MODE_HDR = 2; // 0x2
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT = 1; // 0x1
+    field public static final int CONFIG_COLOR_MODE = 16384; // 0x4000
     field public static final int CONFIG_DENSITY = 4096; // 0x1000
     field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
     field public static final int CONFIG_KEYBOARD = 16; // 0x10
@@ -9600,6 +9711,7 @@
     field public static final int SCREEN_ORIENTATION_USER_LANDSCAPE = 11; // 0xb
     field public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12; // 0xc
     field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1
+    field public int colorMode;
     field public int configChanges;
     field public int documentLaunchMode;
     field public int flags;
@@ -9698,6 +9810,7 @@
     field public int requiresSmallestWidthDp;
     field public java.lang.String[] sharedLibraryFiles;
     field public java.lang.String sourceDir;
+    field public java.lang.String[] splitNames;
     field public java.lang.String[] splitPublicSourceDirs;
     field public java.lang.String[] splitSourceDirs;
     field public int targetSdkVersion;
@@ -9726,6 +9839,7 @@
     field public boolean enabled;
     field public boolean exported;
     field public java.lang.String processName;
+    field public java.lang.String splitName;
   }
 
   public class ConfigurationInfo implements android.os.Parcelable {
@@ -9779,6 +9893,7 @@
     field public boolean handleProfiling;
     field public java.lang.String publicSourceDir;
     field public java.lang.String sourceDir;
+    field public java.lang.String[] splitNames;
     field public java.lang.String[] splitPublicSourceDirs;
     field public java.lang.String[] splitSourceDirs;
     field public java.lang.String targetPackage;
@@ -9814,7 +9929,10 @@
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
+    method public java.util.List<android.os.UserHandle> getProfiles();
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+    method public android.content.IntentSender getShortcutConfigActivityIntent(android.content.pm.LauncherActivityInfo);
+    method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(java.lang.String, android.os.UserHandle);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
@@ -9849,7 +9967,7 @@
     method public boolean accept(android.os.Bundle);
     method public boolean accept();
     method public int describeContents();
-    method public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo();
+    method public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo(android.content.Context);
     method public int getRequestType();
     method public android.content.pm.ShortcutInfo getShortcutInfo();
     method public boolean isValid();
@@ -9918,6 +10036,7 @@
     method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
     method public void uninstall(java.lang.String, android.content.IntentSender);
+    method public void uninstall(android.content.pm.VersionedPackage, android.content.IntentSender);
     method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void updateSessionAppIcon(int, android.graphics.Bitmap);
     method public void updateSessionAppLabel(int, java.lang.CharSequence);
@@ -10026,6 +10145,7 @@
     method public abstract boolean addPermission(android.content.pm.PermissionInfo);
     method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo);
     method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
+    method public abstract boolean canRequestPackageInstalls();
     method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]);
     method public abstract int checkPermission(java.lang.String, java.lang.String);
     method public abstract int checkSignatures(java.lang.String, java.lang.String);
@@ -10066,6 +10186,7 @@
     method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInstaller getPackageInstaller();
     method public abstract int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] getPackagesForUid(int);
@@ -10080,6 +10201,7 @@
     method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
     method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public abstract java.lang.String[] getSystemSharedLibraryNames();
     method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -10138,6 +10260,7 @@
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
+    field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
     field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
@@ -10238,6 +10361,7 @@
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
+    field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -10378,6 +10502,20 @@
     field public java.lang.String permission;
   }
 
+  public final class SharedLibraryInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.pm.VersionedPackage getDeclaringPackage();
+    method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
+    method public java.lang.String getName();
+    method public int getVersion();
+    method public boolean isBuiltin();
+    method public boolean isDynamic();
+    method public boolean isStatic();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+    field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
+  }
+
   public final class ShortcutInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivity();
@@ -10422,6 +10560,7 @@
   public class ShortcutManager {
     ctor public ShortcutManager(android.content.Context);
     method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public android.content.Intent createShortcutResultIntent(android.content.pm.ShortcutInfo);
     method public void disableShortcuts(java.util.List<java.lang.String>);
     method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
     method public void enableShortcuts(java.util.List<java.lang.String>);
@@ -10453,6 +10592,15 @@
     field public static final android.os.Parcelable.Creator<android.content.pm.Signature> CREATOR;
   }
 
+  public final class VersionedPackage implements android.os.Parcelable {
+    ctor public VersionedPackage(java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getPackageName();
+    method public long getVersionCode();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.VersionedPackage> CREATOR;
+  }
+
 }
 
 package android.content.res {
@@ -10538,7 +10686,9 @@
     method public int getLayoutDirection();
     method public android.os.LocaleList getLocales();
     method public boolean isLayoutSizeAtLeast(int);
+    method public boolean isScreenHdr();
     method public boolean isScreenRound();
+    method public boolean isScreenWideColorGamut();
     method public static boolean needNewResources(int, int);
     method public void readFromParcel(android.os.Parcel);
     method public void setLayoutDirection(java.util.Locale);
@@ -10548,6 +10698,16 @@
     method public void setToDefaults();
     method public int updateFrom(android.content.res.Configuration);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int COLOR_MODE_HDR_MASK = 12; // 0xc
+    field public static final int COLOR_MODE_HDR_NO = 4; // 0x4
+    field public static final int COLOR_MODE_HDR_SHIFT = 2; // 0x2
+    field public static final int COLOR_MODE_HDR_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_HDR_YES = 8; // 0x8
+    field public static final int COLOR_MODE_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 3; // 0x3
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 1; // 0x1
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0; // 0x0
+    field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 2; // 0x2
     field public static final android.os.Parcelable.Creator<android.content.res.Configuration> CREATOR;
     field public static final int DENSITY_DPI_UNDEFINED = 0; // 0x0
     field public static final int HARDKEYBOARDHIDDEN_NO = 1; // 0x1
@@ -10613,6 +10773,7 @@
     field public static final int UI_MODE_TYPE_UNDEFINED = 0; // 0x0
     field public static final int UI_MODE_TYPE_VR_HEADSET = 7; // 0x7
     field public static final int UI_MODE_TYPE_WATCH = 6; // 0x6
+    field public int colorMode;
     field public int densityDpi;
     field public float fontScale;
     field public int hardKeyboardHidden;
@@ -10667,6 +10828,7 @@
     method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
     method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException;
     method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme);
+    method public android.graphics.Typeface getFont(int) throws android.content.res.Resources.NotFoundException;
     method public float getFraction(int, int, int);
     method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String);
     method public int[] getIntArray(int) throws android.content.res.Resources.NotFoundException;
@@ -12153,15 +12315,57 @@
     method public static int HSVToColor(float[]);
     method public static int HSVToColor(int, float[]);
     method public static void RGBToHSV(int, int, int, float[]);
+    method public float alpha();
+    method public static float alpha(long);
     method public static int alpha(int);
     method public static int argb(int, int, int, int);
+    method public static int argb(float, float, float, float);
+    method public float blue();
+    method public static float blue(long);
     method public static int blue(int);
+    method public static android.graphics.ColorSpace colorSpace(long);
     method public static void colorToHSV(int, float[]);
+    method public android.graphics.Color convert(android.graphics.ColorSpace);
+    method public static long convert(int, android.graphics.ColorSpace);
+    method public static long convert(long, android.graphics.ColorSpace);
+    method public static long convert(float, float, float, float, android.graphics.ColorSpace, android.graphics.ColorSpace);
+    method public static long convert(long, android.graphics.ColorSpace.Connector);
+    method public static long convert(float, float, float, float, android.graphics.ColorSpace.Connector);
+    method public android.graphics.ColorSpace getColorSpace();
+    method public float getComponent(int);
+    method public int getComponentCount();
+    method public float[] getComponents();
+    method public android.graphics.ColorSpace.Model getModel();
+    method public float green();
+    method public static float green(long);
     method public static int green(int);
+    method public static boolean isInColorSpace(long, android.graphics.ColorSpace);
+    method public boolean isSrgb();
+    method public static boolean isSrgb(long);
+    method public boolean isWideGamut();
+    method public static boolean isWideGamut(long);
+    method public float luminance();
+    method public static float luminance(long);
     method public static float luminance(int);
+    method public long pack();
+    method public static long pack(int);
+    method public static long pack(float, float, float);
+    method public static long pack(float, float, float, float);
+    method public static long pack(float, float, float, float, android.graphics.ColorSpace);
     method public static int parseColor(java.lang.String);
+    method public float red();
+    method public static float red(long);
     method public static int red(int);
     method public static int rgb(int, int, int);
+    method public static int rgb(float, float, float);
+    method public int toArgb();
+    method public static int toArgb(long);
+    method public static android.graphics.Color valueOf(int);
+    method public static android.graphics.Color valueOf(long);
+    method public static android.graphics.Color valueOf(float, float, float);
+    method public static android.graphics.Color valueOf(float, float, float, float);
+    method public static android.graphics.Color valueOf(float, float, float, float, android.graphics.ColorSpace);
+    method public static android.graphics.Color valueOf(float[], android.graphics.ColorSpace);
     field public static final int BLACK = -16777216; // 0xff000000
     field public static final int BLUE = -16776961; // 0xff0000ff
     field public static final int CYAN = -16711681; // 0xff00ffff
@@ -12233,7 +12437,7 @@
     field public static final float[] ILLUMINANT_D65;
     field public static final float[] ILLUMINANT_D75;
     field public static final float[] ILLUMINANT_E;
-    field public static final int MAX_ID = 64; // 0x40
+    field public static final int MAX_ID = 63; // 0x3f
     field public static final int MIN_ID = -1; // 0xffffffff
   }
 
@@ -12556,7 +12760,6 @@
     method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt);
     method public android.graphics.Paint.FontMetricsInt getFontMetricsInt();
     method public float getFontSpacing();
-    method public java.lang.String getFontVariationSettings();
     method public int getHinting();
     method public float getLetterSpacing();
     method public android.graphics.MaskFilter getMaskFilter();
@@ -12614,7 +12817,6 @@
     method public void setFilterBitmap(boolean);
     method public void setFlags(int);
     method public void setFontFeatureSettings(java.lang.String);
-    method public void setFontVariationSettings(java.lang.String);
     method public void setHinting(int);
     method public void setLetterSpacing(float);
     method public void setLinearText(boolean);
@@ -13112,6 +13314,7 @@
   }
 
   public class Typeface {
+    method public static void create(android.graphics.fonts.FontRequest, android.graphics.Typeface.FontRequestCallback);
     method public static android.graphics.Typeface create(java.lang.String, int);
     method public static android.graphics.Typeface create(android.graphics.Typeface, int);
     method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
@@ -13132,6 +13335,14 @@
     field public static final android.graphics.Typeface SERIF;
   }
 
+  public static abstract interface Typeface.FontRequestCallback {
+    method public abstract void onTypefaceRequestFailed(int);
+    method public abstract void onTypefaceRetrieved(android.graphics.Typeface);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 2; // 0x2
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = 0; // 0x0
+  }
+
   public class Xfermode {
     ctor public Xfermode();
   }
@@ -13538,6 +13749,23 @@
     method public void addLevel(int, int, android.graphics.drawable.Drawable);
   }
 
+  public class MaskableIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+    ctor public MaskableIconDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public android.graphics.drawable.Drawable getBackground();
+    method public android.graphics.drawable.Drawable getForeground();
+    method public android.graphics.Path getIconMask();
+    method public int getOpacity();
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setOpacity(int);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+    field public static final float DEFAULT_VIEW_PORT_SCALE = 0.6666667f;
+    field public static final float MASK_SIZE = 100.0f;
+  }
+
   public class NinePatchDrawable extends android.graphics.drawable.Drawable {
     ctor public deprecated NinePatchDrawable(android.graphics.Bitmap, byte[], android.graphics.Rect, java.lang.String);
     ctor public NinePatchDrawable(android.content.res.Resources, android.graphics.Bitmap, byte[], android.graphics.Rect, java.lang.String);
@@ -13686,6 +13914,19 @@
 
 }
 
+package android.graphics.fonts {
+
+  public final class FontRequest implements android.os.Parcelable {
+    ctor public FontRequest(java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public java.lang.String getProviderAuthority();
+    method public java.lang.String getQuery();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
+  }
+
+}
+
 package android.graphics.pdf {
 
   public class PdfDocument {
@@ -14013,9 +14254,42 @@
     method public float getZ();
   }
 
+  public final class HardwareBuffer implements android.os.Parcelable {
+    method public static android.hardware.HardwareBuffer create(int, int, int, int, long);
+    method public int describeContents();
+    method public void destroy();
+    method public int getFormat();
+    method public int getHeight();
+    method public int getLayers();
+    method public long getUsage();
+    method public int getWidth();
+    method public boolean isDestroyed();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BLOB = 33; // 0x21
+    field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR;
+    field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_FP16 = 22; // 0x16
+    field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGB_565 = 4; // 0x4
+    field public static final int RGB_888 = 3; // 0x3
+    field public static final long USAGE0_CPU_READ = 2L; // 0x2L
+    field public static final long USAGE0_CPU_READ_OFTEN = 6L; // 0x6L
+    field public static final long USAGE0_CPU_WRITE = 32L; // 0x20L
+    field public static final long USAGE0_CPU_WRITE_OFTEN = 96L; // 0x60L
+    field public static final long USAGE0_GPU_COLOR_OUTPUT = 2048L; // 0x800L
+    field public static final long USAGE0_GPU_CUBEMAP = 8192L; // 0x2000L
+    field public static final long USAGE0_GPU_DATA_BUFFER = 16384L; // 0x4000L
+    field public static final long USAGE0_GPU_SAMPLED_IMAGE = 1024L; // 0x400L
+    field public static final long USAGE0_GPU_STORAGE_IMAGE = 3072L; // 0xc00L
+    field public static final long USAGE0_PROTECTED_CONTENT = 262144L; // 0x40000L
+    field public static final long USAGE0_SENSOR_DIRECT_DATA = 536870912L; // 0x20000000L
+    field public static final long USAGE0_VIDEO_ENCODE = 2097152L; // 0x200000L
+  }
+
   public final class Sensor {
     method public int getFifoMaxEventCount();
     method public int getFifoReservedEventCount();
+    method public int getHighestDirectReportRateLevel();
     method public int getId();
     method public int getMaxDelay();
     method public float getMaximumRange();
@@ -14029,6 +14303,7 @@
     method public java.lang.String getVendor();
     method public int getVersion();
     method public boolean isAdditionalInfoSupported();
+    method public boolean isDirectChannelTypeSupported(int);
     method public boolean isDynamicSensor();
     method public boolean isWakeUpSensor();
     field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
@@ -14106,6 +14381,17 @@
     field public final int type;
   }
 
+  public final class SensorDirectChannel implements java.lang.AutoCloseable {
+    method public void close();
+    method public boolean isValid();
+    field public static final int RATE_FAST = 2; // 0x2
+    field public static final int RATE_NORMAL = 1; // 0x1
+    field public static final int RATE_STOP = 0; // 0x0
+    field public static final int RATE_VERY_FAST = 3; // 0x3
+    field public static final int TYPE_ASHMEM = 1; // 0x1
+    field public static final int TYPE_HARDWARE_BUFFER = 2; // 0x2
+  }
+
   public class SensorEvent {
     field public int accuracy;
     field public android.hardware.Sensor sensor;
@@ -14137,6 +14423,9 @@
 
   public abstract class SensorManager {
     method public boolean cancelTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
+    method public int configureDirectChannel(android.hardware.SensorDirectChannel, android.hardware.Sensor, int);
+    method public android.hardware.SensorDirectChannel createDirectChannel(android.os.MemoryFile);
+    method public android.hardware.SensorDirectChannel createDirectChannel(android.hardware.HardwareBuffer);
     method public boolean flush(android.hardware.SensorEventListener);
     method public static float getAltitude(float, float);
     method public static void getAngleChange(float[], float[], float[]);
@@ -14266,7 +14555,7 @@
     method public abstract int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void close();
-    method public abstract void finishDeferredConfiguration(java.util.List<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void finalizeOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CameraDevice getDevice();
     method public abstract android.view.Surface getInputSurface();
     method public abstract boolean isReprocessable();
@@ -14681,6 +14970,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_CAPTURE_INTENT;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> CONTROL_ENABLE_ZSL;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SCENE_MODE;
@@ -14760,6 +15050,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AWB_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_CAPTURE_INTENT;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_EFFECT_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_ENABLE_ZSL;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE;
@@ -14907,10 +15198,12 @@
     ctor public OutputConfiguration(android.view.Surface);
     ctor public OutputConfiguration(int, android.view.Surface);
     ctor public OutputConfiguration(android.util.Size, java.lang.Class<T>);
+    method public void addSurface(android.view.Surface);
     method public int describeContents();
+    method public void enableSurfaceSharing();
     method public android.view.Surface getSurface();
     method public int getSurfaceGroupId();
-    method public void setDeferredSurface(android.view.Surface);
+    method public java.util.List<android.view.Surface> getSurfaces();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -17460,6 +17753,15 @@
     method public boolean isTransitionalDifferent();
   }
 
+  public final class ListFormatter {
+    method public java.lang.String format(java.lang.Object...);
+    method public java.lang.String format(java.util.Collection<?>);
+    method public static android.icu.text.ListFormatter getInstance(android.icu.util.ULocale);
+    method public static android.icu.text.ListFormatter getInstance(java.util.Locale);
+    method public static android.icu.text.ListFormatter getInstance();
+    method public java.lang.String getPatternForNumItems(int);
+  }
+
   public abstract class LocaleDisplayNames {
     method public abstract android.icu.text.DisplayContext getContext(android.icu.text.DisplayContext.Type);
     method public abstract android.icu.text.LocaleDisplayNames.DialectHandling getDialectHandling();
@@ -17469,6 +17771,8 @@
     method public static android.icu.text.LocaleDisplayNames getInstance(android.icu.util.ULocale, android.icu.text.DisplayContext...);
     method public static android.icu.text.LocaleDisplayNames getInstance(java.util.Locale, android.icu.text.DisplayContext...);
     method public abstract android.icu.util.ULocale getLocale();
+    method public java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiList(java.util.Set<android.icu.util.ULocale>, boolean, java.util.Comparator<java.lang.Object>);
+    method public abstract java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiListCompareWholeItems(java.util.Set<android.icu.util.ULocale>, java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem>);
     method public abstract java.lang.String keyDisplayName(java.lang.String);
     method public abstract java.lang.String keyValueDisplayName(java.lang.String, java.lang.String);
     method public abstract java.lang.String languageDisplayName(java.lang.String);
@@ -17488,9 +17792,19 @@
     enum_constant public static final android.icu.text.LocaleDisplayNames.DialectHandling STANDARD_NAMES;
   }
 
+  public static class LocaleDisplayNames.UiListItem {
+    ctor public LocaleDisplayNames.UiListItem(android.icu.util.ULocale, android.icu.util.ULocale, java.lang.String, java.lang.String);
+    method public static java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem> getComparator(java.util.Comparator<java.lang.Object>, boolean);
+    field public final android.icu.util.ULocale minimized;
+    field public final android.icu.util.ULocale modified;
+    field public final java.lang.String nameInDisplayLocale;
+    field public final java.lang.String nameInSelf;
+  }
+
   public class MeasureFormat extends android.icu.text.UFormat {
     method public final boolean equals(java.lang.Object);
     method public java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
+    method public java.lang.StringBuilder formatMeasurePerUnit(android.icu.util.Measure, android.icu.util.MeasureUnit, java.lang.StringBuilder, java.text.FieldPosition);
     method public final java.lang.String formatMeasures(android.icu.util.Measure...);
     method public java.lang.StringBuilder formatMeasures(java.lang.StringBuilder, java.text.FieldPosition, android.icu.util.Measure...);
     method public static android.icu.text.MeasureFormat getCurrencyFormat(android.icu.util.ULocale);
@@ -17959,6 +18273,14 @@
     method public void setUpperCaseFirst(boolean);
   }
 
+  public final class ScientificNumberFormatter {
+    method public java.lang.String format(java.lang.Object);
+    method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.util.ULocale, java.lang.String, java.lang.String);
+    method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.text.DecimalFormat, java.lang.String, java.lang.String);
+    method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.util.ULocale);
+    method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.text.DecimalFormat);
+  }
+
   public abstract class SearchIterator {
     ctor protected SearchIterator(java.text.CharacterIterator, android.icu.text.BreakIterator);
     method public final int first();
@@ -18734,6 +19056,34 @@
     method public long getToDate();
   }
 
+  public final class EthiopicCalendar extends android.icu.util.CECalendar {
+    ctor public EthiopicCalendar();
+    ctor public EthiopicCalendar(android.icu.util.TimeZone);
+    ctor public EthiopicCalendar(java.util.Locale);
+    ctor public EthiopicCalendar(android.icu.util.ULocale);
+    ctor public EthiopicCalendar(android.icu.util.TimeZone, java.util.Locale);
+    ctor public EthiopicCalendar(android.icu.util.TimeZone, android.icu.util.ULocale);
+    ctor public EthiopicCalendar(int, int, int);
+    ctor public EthiopicCalendar(java.util.Date);
+    ctor public EthiopicCalendar(int, int, int, int, int, int);
+    method protected deprecated int handleGetExtendedYear();
+    method public boolean isAmeteAlemEra();
+    method public void setAmeteAlemEra(boolean);
+    field public static final int GENBOT = 8; // 0x8
+    field public static final int HAMLE = 10; // 0xa
+    field public static final int HEDAR = 2; // 0x2
+    field public static final int MEGABIT = 6; // 0x6
+    field public static final int MESKEREM = 0; // 0x0
+    field public static final int MIAZIA = 7; // 0x7
+    field public static final int NEHASSE = 11; // 0xb
+    field public static final int PAGUMEN = 12; // 0xc
+    field public static final int SENE = 9; // 0x9
+    field public static final int TAHSAS = 3; // 0x3
+    field public static final int TEKEMT = 1; // 0x1
+    field public static final int TER = 4; // 0x4
+    field public static final int YEKATIT = 5; // 0x5
+  }
+
   public abstract interface Freezable<T> implements java.lang.Cloneable {
     method public abstract T cloneAsThawed();
     method public abstract T freeze();
@@ -19262,6 +19612,35 @@
     enum_constant public static final android.icu.util.ULocale.Category FORMAT;
   }
 
+  public final class UniversalTimeScale {
+    method public static android.icu.math.BigDecimal bigDecimalFrom(double, int);
+    method public static android.icu.math.BigDecimal bigDecimalFrom(long, int);
+    method public static android.icu.math.BigDecimal bigDecimalFrom(android.icu.math.BigDecimal, int);
+    method public static long from(long, int);
+    method public static long getTimeScaleValue(int, int);
+    method public static android.icu.math.BigDecimal toBigDecimal(long, int);
+    method public static android.icu.math.BigDecimal toBigDecimal(android.icu.math.BigDecimal, int);
+    method public static long toLong(long, int);
+    field public static final int DB2_TIME = 8; // 0x8
+    field public static final int DOTNET_DATE_TIME = 4; // 0x4
+    field public static final int EPOCH_OFFSET_PLUS_1_VALUE = 6; // 0x6
+    field public static final int EPOCH_OFFSET_VALUE = 1; // 0x1
+    field public static final int EXCEL_TIME = 7; // 0x7
+    field public static final int FROM_MAX_VALUE = 3; // 0x3
+    field public static final int FROM_MIN_VALUE = 2; // 0x2
+    field public static final int ICU4C_TIME = 2; // 0x2
+    field public static final int JAVA_TIME = 0; // 0x0
+    field public static final int MAC_OLD_TIME = 5; // 0x5
+    field public static final int MAC_TIME = 6; // 0x6
+    field public static final int MAX_SCALE = 10; // 0xa
+    field public static final int TO_MAX_VALUE = 5; // 0x5
+    field public static final int TO_MIN_VALUE = 4; // 0x4
+    field public static final int UNITS_VALUE = 0; // 0x0
+    field public static final int UNIX_MICROSECONDS_TIME = 9; // 0x9
+    field public static final int UNIX_TIME = 1; // 0x1
+    field public static final int WINDOWS_FILE_TIME = 3; // 0x3
+  }
+
   public abstract interface ValueIterator {
     method public abstract boolean next(android.icu.util.ValueIterator.Element);
     method public abstract void reset();
@@ -19738,6 +20117,7 @@
     method public double getAccumulatedDeltaRangeMeters();
     method public int getAccumulatedDeltaRangeState();
     method public double getAccumulatedDeltaRangeUncertaintyMeters();
+    method public double getAutomaticGainControlLevelInDb();
     method public long getCarrierCycles();
     method public float getCarrierFrequencyHz();
     method public double getCarrierPhase();
@@ -19753,12 +20133,14 @@
     method public int getState();
     method public int getSvid();
     method public double getTimeOffsetNanos();
+    method public boolean hasAutomaticGainControlLevelInDb();
     method public boolean hasCarrierCycles();
     method public boolean hasCarrierFrequencyHz();
     method public boolean hasCarrierPhase();
     method public boolean hasCarrierPhaseUncertainty();
     method public boolean hasSnrInDb();
     method public void reset();
+    method public void resetAutomaticGainControlLevel();
     method public void resetCarrierCycles();
     method public void resetCarrierFrequencyHz();
     method public void resetCarrierPhase();
@@ -19768,6 +20150,7 @@
     method public void setAccumulatedDeltaRangeMeters(double);
     method public void setAccumulatedDeltaRangeState(int);
     method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
+    method public void setAutomaticGainControlLevelInDb(double);
     method public void setCarrierCycles(long);
     method public void setCarrierFrequencyHz(float);
     method public void setCarrierPhase(double);
@@ -19801,11 +20184,13 @@
     field public static final int STATE_GAL_E1C_2ND_CODE_LOCK = 2048; // 0x800
     field public static final int STATE_GLO_STRING_SYNC = 64; // 0x40
     field public static final int STATE_GLO_TOD_DECODED = 128; // 0x80
+    field public static final int STATE_GLO_TOD_KNOWN = 32768; // 0x8000
     field public static final int STATE_MSEC_AMBIGUOUS = 16; // 0x10
     field public static final int STATE_SBAS_SYNC = 8192; // 0x2000
     field public static final int STATE_SUBFRAME_SYNC = 4; // 0x4
     field public static final int STATE_SYMBOL_SYNC = 32; // 0x20
     field public static final int STATE_TOW_DECODED = 8; // 0x8
+    field public static final int STATE_TOW_KNOWN = 16384; // 0x4000
     field public static final int STATE_UNKNOWN = 0; // 0x0
   }
 
@@ -19872,12 +20257,14 @@
 
   public final class GnssStatus {
     method public float getAzimuthDegrees(int);
+    method public float getCarrierFrequencyHz(int);
     method public float getCn0DbHz(int);
     method public int getConstellationType(int);
     method public float getElevationDegrees(int);
     method public int getSatelliteCount();
     method public int getSvid(int);
     method public boolean hasAlmanacData(int);
+    method public boolean hasCarrierFrequency(int);
     method public boolean hasEphemerisData(int);
     method public boolean usedInFix(int);
     field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
@@ -19938,34 +20325,46 @@
     method public float getAccuracy();
     method public double getAltitude();
     method public float getBearing();
+    method public float getBearingAccuracyDegrees();
     method public long getElapsedRealtimeNanos();
     method public android.os.Bundle getExtras();
     method public double getLatitude();
     method public double getLongitude();
     method public java.lang.String getProvider();
     method public float getSpeed();
+    method public float getSpeedAccuracyMetersPerSecond();
     method public long getTime();
+    method public float getVerticalAccuracyMeters();
     method public boolean hasAccuracy();
     method public boolean hasAltitude();
     method public boolean hasBearing();
+    method public boolean hasBearingAccuracy();
     method public boolean hasSpeed();
+    method public boolean hasSpeedAccuracy();
+    method public boolean hasVerticalAccuracy();
     method public boolean isFromMockProvider();
     method public void removeAccuracy();
     method public void removeAltitude();
     method public void removeBearing();
+    method public void removeBearingAccuracy();
     method public void removeSpeed();
+    method public void removeSpeedAccuracy();
+    method public void removeVerticalAccuracy();
     method public void reset();
     method public void set(android.location.Location);
     method public void setAccuracy(float);
     method public void setAltitude(double);
     method public void setBearing(float);
+    method public void setBearingAccuracyDegrees(float);
     method public void setElapsedRealtimeNanos(long);
     method public void setExtras(android.os.Bundle);
     method public void setLatitude(double);
     method public void setLongitude(double);
     method public void setProvider(java.lang.String);
     method public void setSpeed(float);
+    method public void setSpeedAccuracyMetersPerSecond(float);
     method public void setTime(long);
+    method public void setVerticalAccuracyMeters(float);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.Location> CREATOR;
     field public static final int FORMAT_DEGREES = 0; // 0x0
@@ -20098,7 +20497,7 @@
     field public static final android.os.Parcelable.Creator<android.media.AudioAttributes> CREATOR;
     field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
     field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
-    field public static final int FLAG_LOW_LATENCY = 256; // 0x100
+    field public static final deprecated int FLAG_LOW_LATENCY = 256; // 0x100
     field public static final int USAGE_ALARM = 4; // 0x4
     field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
     field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
@@ -20539,6 +20938,7 @@
     method protected deprecated int getNativeFrameCount();
     method public static int getNativeOutputSampleRate(int);
     method public int getNotificationMarkerPosition();
+    method public int getPerformanceMode();
     method public int getPlayState();
     method public int getPlaybackHeadPosition();
     method public android.media.PlaybackParams getPlaybackParams();
@@ -20585,6 +20985,9 @@
     field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd
     field public static final int MODE_STATIC = 0; // 0x0
     field public static final int MODE_STREAM = 1; // 0x1
+    field public static final int PERFORMANCE_MODE_LOW_LATENCY = 1; // 0x1
+    field public static final int PERFORMANCE_MODE_NONE = 0; // 0x0
+    field public static final int PERFORMANCE_MODE_POWER_SAVING = 2; // 0x2
     field public static final int PLAYSTATE_PAUSED = 2; // 0x2
     field public static final int PLAYSTATE_PLAYING = 3; // 0x3
     field public static final int PLAYSTATE_STOPPED = 1; // 0x1
@@ -20602,6 +21005,7 @@
     method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioTrack.Builder setPerformanceMode(int);
     method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
   }
@@ -20616,6 +21020,40 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public final class BufferingParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getInitialBufferingMode();
+    method public int getInitialBufferingWatermarkKB();
+    method public int getInitialBufferingWatermarkMs();
+    method public int getRebufferingMode();
+    method public int getRebufferingWatermarkHighKB();
+    method public int getRebufferingWatermarkHighMs();
+    method public int getRebufferingWatermarkLowKB();
+    method public int getRebufferingWatermarkLowMs();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUFFERING_MODE_NONE = 0; // 0x0
+    field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2
+    field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1
+    field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
+  }
+
+  public static class BufferingParams.Builder {
+    ctor public BufferingParams.Builder();
+    ctor public BufferingParams.Builder(android.media.BufferingParams);
+    method public android.media.BufferingParams build();
+    method public android.media.BufferingParams.Builder setInitialBufferingMode(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingMode(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -21760,6 +22198,7 @@
 
   public final class MediaMuxer {
     ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
+    ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
     method public int addTrack(android.media.MediaFormat);
     method public void release();
     method public void setLocation(float, float);
@@ -21788,7 +22227,9 @@
     method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
     method public void deselectTrack(int) throws java.lang.IllegalStateException;
     method public int getAudioSessionId();
+    method public android.media.BufferingParams getBufferingParams();
     method public int getCurrentPosition();
+    method public android.media.BufferingParams getDefaultBufferingParams();
     method public int getDuration();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -21811,6 +22252,7 @@
     method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public deprecated void setAudioStreamType(int);
     method public void setAuxEffectSendLevel(float);
+    method public void setBufferingParams(android.media.BufferingParams);
     method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
@@ -21936,6 +22378,8 @@
     method public void setLocation(float, float);
     method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
     method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
+    method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
+    method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
     method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
     method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
     method public void setOrientationHint(int);
@@ -21954,7 +22398,9 @@
     field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
     field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
+    field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802; // 0x322
     field public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801; // 0x321
+    field public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803; // 0x323
     field public static final int MEDIA_RECORDER_INFO_UNKNOWN = 1; // 0x1
   }
 
@@ -23469,6 +23915,7 @@
     field public static final java.lang.String TYPE_NTSC = "TYPE_NTSC";
     field public static final java.lang.String TYPE_OTHER = "TYPE_OTHER";
     field public static final java.lang.String TYPE_PAL = "TYPE_PAL";
+    field public static final java.lang.String TYPE_PREVIEW = "TYPE_PREVIEW";
     field public static final java.lang.String TYPE_SECAM = "TYPE_SECAM";
     field public static final java.lang.String TYPE_S_DMB = "TYPE_S_DMB";
     field public static final java.lang.String TYPE_T_DMB = "TYPE_T_DMB";
@@ -23496,6 +23943,7 @@
 
   public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns {
     field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+    field public static final java.lang.String COLUMN_AUTHOR = "author";
     field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
     field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
     field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
@@ -23504,14 +23952,24 @@
     field public static final java.lang.String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
     field public static final deprecated java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
     field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
+    field public static final java.lang.String COLUMN_INTERACTION_COUNT = "interaction_count";
+    field public static final java.lang.String COLUMN_INTERACTION_TYPE = "interaction_type";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_DURATION = "preview_duration";
+    field public static final java.lang.String COLUMN_PREVIEW_INTENT_URI = "preview_intent_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_LAST_PLAYBACK_POSITION = "preview_last_playback_position";
+    field public static final java.lang.String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+    field public static final java.lang.String COLUMN_PREVIEW_WEIGHT = "preview_weight";
     field public static final java.lang.String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
+    field public static final java.lang.String COLUMN_REVIEW_RATING = "review_rating";
+    field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
     field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
     field public static final java.lang.String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
     field public static final deprecated java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -23526,6 +23984,16 @@
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS";
+    field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS";
+    field public static final java.lang.String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES";
+    field public static final java.lang.String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS";
+    field public static final java.lang.String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS";
+    field public static final java.lang.String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS";
+    field public static final java.lang.String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS";
+    field public static final java.lang.String REVIEW_RATING_STYLE_PERCENTAGE = "REVIEW_RATING_STYLE_PERCENTAGE";
+    field public static final java.lang.String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS";
+    field public static final java.lang.String REVIEW_RATING_STYLE_THUMPS_UP_DOWN = "REVIEW_RATING_STYLE_THUMPS_UP_DOWN";
   }
 
   public static final class TvContract.Programs.Genres {
@@ -23636,9 +24104,13 @@
     method public void unregisterCallback(android.media.tv.TvInputManager.TvInputCallback);
     method public void updateTvInputInfo(android.media.tv.TvInputInfo);
     field public static final java.lang.String ACTION_BLOCKED_RATINGS_CHANGED = "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
+    field public static final java.lang.String ACTION_MAKE_CHANNEL_BROWSABLE = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE";
     field public static final java.lang.String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
     field public static final java.lang.String ACTION_QUERY_CONTENT_RATING_SYSTEMS = "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
     field public static final java.lang.String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
+    field public static final java.lang.String ACTION_VIEW_RECORDING_SCHEDULES = "android.media.tv.action.VIEW_RECORDING_SCHEDULES";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
     field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
     field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
     field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
@@ -24476,6 +24948,7 @@
   public class TrafficStats {
     ctor public TrafficStats();
     method public static void clearThreadStatsTag();
+    method public static int getAndSetThreadStatsTag(int);
     method public static long getMobileRxBytes();
     method public static long getMobileRxPackets();
     method public static long getMobileTxBytes();
@@ -25085,7 +25558,9 @@
   public class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
+    method public android.net.ProxyInfo getHttpProxy();
     method public boolean isPasspoint();
+    method public void setHttpProxy(android.net.ProxyInfo);
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
     field public java.lang.String FQDN;
@@ -29716,6 +30191,7 @@
     method public final android.util.SizeF readSizeF();
     method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
     method public final android.util.SparseBooleanArray readSparseBooleanArray();
+    method public final android.util.SparseIntArray readSparseIntArray();
     method public final java.lang.String readString();
     method public final void readStringArray(java.lang.String[]);
     method public final void readStringList(java.util.List<java.lang.String>);
@@ -29760,6 +30236,7 @@
     method public final void writeSizeF(android.util.SizeF);
     method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
     method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
+    method public final void writeSparseIntArray(android.util.SparseIntArray);
     method public final void writeString(java.lang.String);
     method public final void writeStringArray(java.lang.String[]);
     method public final void writeStringList(java.util.List<java.lang.String>);
@@ -29954,6 +30431,15 @@
     field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
   }
 
+  public abstract class ProxyFileDescriptorCallback {
+    ctor public ProxyFileDescriptorCallback();
+    method public void onFsync() throws android.system.ErrnoException;
+    method public long onGetSize() throws android.system.ErrnoException;
+    method public int onRead(long, int, byte[]) throws android.system.ErrnoException;
+    method public abstract void onRelease();
+    method public int onWrite(long, int, byte[]) throws android.system.ErrnoException;
+  }
+
   public class RecoverySystem {
     method public static void installPackage(android.content.Context, java.io.File) throws java.io.IOException;
     method public static void rebootWipeCache(android.content.Context) throws java.io.IOException;
@@ -30068,6 +30554,7 @@
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
+    method public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
@@ -30365,13 +30852,22 @@
   }
 
   public class StorageManager {
+    method public long getCacheQuotaBytes();
+    method public long getCacheSizeBytes();
+    method public long getExternalCacheQuotaBytes();
+    method public long getExternalCacheSizeBytes();
     method public java.lang.String getMountedObbPath(java.lang.String);
     method public android.os.storage.StorageVolume getPrimaryStorageVolume();
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
+    method public boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
+    method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
+    method public void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
+    method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
     field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
   }
@@ -30499,6 +30995,7 @@
     method public android.preference.Preference.OnPreferenceChangeListener getOnPreferenceChangeListener();
     method public android.preference.Preference.OnPreferenceClickListener getOnPreferenceClickListener();
     method public int getOrder();
+    method public android.preference.PreferenceGroup getParent();
     method protected boolean getPersistedBoolean(boolean);
     method protected float getPersistedFloat(float);
     method protected int getPersistedInt(int);
@@ -32472,7 +32969,7 @@
     ctor public ContactsContract.Intents();
     field public static final java.lang.String ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS = "android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS";
     field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
-    field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
+    field public static final deprecated java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
     field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
     field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
@@ -32582,8 +33079,10 @@
   public static final class ContactsContract.ProviderStatus {
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/provider_status";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DATABASE_CREATION_TIMESTAMP = "database_creation_timestamp";
     field public static final java.lang.String STATUS = "status";
     field public static final int STATUS_BUSY = 1; // 0x1
+    field public static final android.net.Uri STATUS_CHANGE_NOTIFICATION_CONTENT_URI;
     field public static final int STATUS_EMPTY = 2; // 0x2
     field public static final int STATUS_NORMAL = 0; // 0x0
   }
@@ -32729,6 +33228,7 @@
     method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
     method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri);
     method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
+    method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle);
     method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
     method public static java.util.List<java.lang.String> findDocumentPath(android.content.ContentResolver, android.net.Uri);
     method public static java.lang.String getDocumentId(android.net.Uri);
@@ -32773,6 +33273,7 @@
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
     field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
+    field public static final int FLAG_WEB_LINKABLE = 4096; // 0x1000
     field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
   }
 
@@ -32806,6 +33307,7 @@
     ctor public DocumentsProvider();
     method public java.lang.String copyDocument(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public java.lang.String createDocument(java.lang.String, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+    method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException;
     method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
@@ -32825,7 +33327,6 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public android.content.res.AssetFileDescriptor openTypedDocument(java.lang.String, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
-    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public abstract android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
     method public android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], android.os.Bundle) throws java.io.FileNotFoundException;
@@ -32839,6 +33340,16 @@
     method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
+  public class FontsContract {
+  }
+
+  public static final class FontsContract.Columns implements android.provider.BaseColumns {
+    ctor public FontsContract.Columns();
+    field public static final java.lang.String STYLE = "font_style";
+    field public static final java.lang.String TTC_INDEX = "font_ttc_index";
+    field public static final java.lang.String VARIATION_SETTINGS = "font_variation_settings";
+  }
+
   public final deprecated class LiveFolders implements android.provider.BaseColumns {
     field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
     field public static final java.lang.String DESCRIPTION = "description";
@@ -33204,6 +33715,7 @@
     field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
     field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
     field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
+    field public static final java.lang.String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
     field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
     field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
     field public static final java.lang.String ACTION_DEVICE_INFO_SETTINGS = "android.settings.DEVICE_INFO_SETTINGS";
@@ -33222,6 +33734,7 @@
     field public static final java.lang.String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
     field public static final java.lang.String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
+    field public static final java.lang.String ACTION_MANAGE_EXTERNAL_SOURCES = "android.settings.action.MANAGE_EXTERNAL_SOURCES";
     field public static final java.lang.String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
     field public static final java.lang.String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS";
     field public static final java.lang.String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS";
@@ -33257,8 +33770,10 @@
     field public static final java.lang.String AUTHORITY = "settings";
     field public static final java.lang.String EXTRA_ACCOUNT_TYPES = "account_types";
     field public static final java.lang.String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled";
+    field public static final java.lang.String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
     field public static final java.lang.String EXTRA_AUTHORITIES = "authorities";
     field public static final java.lang.String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
+    field public static final java.lang.String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
     field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
     field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
     field public static final java.lang.String EXTRA_INPUT_METHOD_ID = "input_method_id";
@@ -33369,7 +33884,7 @@
     field public static final java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
     field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
     field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
-    field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
+    field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String LOCATION_MODE = "location_mode";
     field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
     field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
@@ -33926,6 +34441,8 @@
 
   public static final class VoicemailContract.Voicemails implements android.provider.BaseColumns android.provider.OpenableColumns {
     method public static android.net.Uri buildSourceUri(java.lang.String);
+    field public static final java.lang.String ARCHIVED = "archived";
+    field public static final java.lang.String BACKED_UP = "backed_up";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATE = "date";
     field public static final java.lang.String DELETED = "deleted";
@@ -33933,6 +34450,7 @@
     field public static final java.lang.String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String HAS_CONTENT = "has_content";
+    field public static final java.lang.String IS_OMTP_VOICEMAIL = "is_omtp_voicemail";
     field public static final java.lang.String IS_READ = "is_read";
     field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
     field public static final java.lang.String LAST_MODIFIED = "last_modified";
@@ -33940,6 +34458,7 @@
     field public static final java.lang.String NUMBER = "number";
     field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
     field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
+    field public static final java.lang.String RESTORED = "restored";
     field public static final java.lang.String SOURCE_DATA = "source_data";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String TRANSCRIPTION = "transcription";
@@ -35144,6 +35663,18 @@
 
 package android.security.keystore {
 
+  public abstract class AttestationUtils {
+    method public static java.security.cert.X509Certificate[] attestDeviceIds(android.content.Context, int[], byte[]) throws android.security.keystore.DeviceIdAttestationException;
+    field public static final int ID_TYPE_IMEI = 2; // 0x2
+    field public static final int ID_TYPE_MEID = 3; // 0x3
+    field public static final int ID_TYPE_SERIAL = 1; // 0x1
+  }
+
+  public class DeviceIdAttestationException extends java.lang.Exception {
+    ctor public DeviceIdAttestationException(java.lang.String);
+    ctor public DeviceIdAttestationException(java.lang.String, java.lang.Throwable);
+  }
+
   public class KeyExpiredException extends java.security.InvalidKeyException {
     ctor public KeyExpiredException();
     ctor public KeyExpiredException(java.lang.String);
@@ -35263,6 +35794,7 @@
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
     field public static final int PURPOSE_VERIFY = 8; // 0x8
+    field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
   }
@@ -35316,16 +35848,25 @@
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
+    method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
     method public void onDisconnected();
     method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
+    field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
+    field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
+    field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
+    field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
+    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
   public final class FillCallback {
+    method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
     method public void onFailure(java.lang.CharSequence);
+    method public void onFillResponseAuthentication(int);
     method public void onSuccess(android.view.autofill.FillResponse);
   }
 
@@ -35527,7 +36068,6 @@
     field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
     field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
     field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-    field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
   }
 
   public class MediaBrowserService.Result<T> {
@@ -35611,6 +36151,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
+    method public final void unsnoozeNotification(java.lang.String);
     method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
@@ -35626,6 +36167,7 @@
     method public final int getCurrentInterruptionFilter();
     method public final int getCurrentListenerHints();
     method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
+    method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
@@ -35644,8 +36186,6 @@
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void snoozeNotification(java.lang.String, java.lang.String);
     method public final void snoozeNotification(java.lang.String, long);
-    method public final void snoozeNotification(java.lang.String);
-    method public final void unsnoozeNotification(java.lang.String);
     field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
@@ -35670,9 +36210,9 @@
     field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
     field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
     field public static final int REASON_SNOOZED = 18; // 0x12
+    field public static final int REASON_TIMEOUT = 19; // 0x13
     field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10
     field public static final int REASON_USER_STOPPED = 6; // 0x6
-    field public static final int REASON_USER_SWITCH = 19; // 0x13
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
     field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2
@@ -35680,6 +36220,7 @@
 
   public static class NotificationListenerService.Ranking {
     ctor public NotificationListenerService.Ranking();
+    method public boolean canShowBadge();
     method public java.util.List<java.lang.String> getAdditionalPeople();
     method public android.app.NotificationChannel getChannel();
     method public int getImportance();
@@ -35721,7 +36262,6 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
-    method public android.app.NotificationChannel getNotificationChannel();
     method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
@@ -36137,6 +36677,7 @@
     method public abstract int getMaxBufferSize();
     method public abstract boolean hasFinished();
     method public abstract boolean hasStarted();
+    method public default void rangeStart(int, int, int);
     method public abstract int start(int, int, int);
   }
 
@@ -36289,6 +36830,7 @@
     method public void onError(java.lang.String, int);
     method public abstract void onStart(java.lang.String);
     method public void onStop(java.lang.String, boolean);
+    method public void onUtteranceRangeStart(java.lang.String, int, int);
   }
 
   public class Voice implements android.os.Parcelable {
@@ -37201,6 +37743,7 @@
     method public void onReject();
     method public void onReject(java.lang.String);
     method public void onSeparate();
+    method public void onShowIncomingCallUi();
     method public void onStateChanged(int);
     method public void onStopDtmfTone();
     method public void onUnhold();
@@ -37212,6 +37755,7 @@
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
     method public final void setAudioModeIsVoip(boolean);
+    method public final void setAudioRoute(int);
     method public final void setCallerDisplayName(java.lang.String, int);
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -37260,6 +37804,7 @@
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+    field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
     field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -37327,7 +37872,9 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
     method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
     method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -37440,6 +37987,7 @@
     field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
     field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
+    field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
     field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -37621,6 +38169,8 @@
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
+    method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+    method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
     method public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
@@ -37631,7 +38181,7 @@
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
     field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
-    field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+    field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
     field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -37734,7 +38284,8 @@
     field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
     field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
     field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
-    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+    field public static final deprecated java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
+    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY = "carrier_vvm_package_name_string_array";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
     field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
     field public static final java.lang.String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
@@ -37745,6 +38296,7 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
+    field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
@@ -37826,9 +38378,13 @@
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
     field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL = "vvm_cellular_data_required_bool";
+    field public static final java.lang.String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
     field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
+    field public static final java.lang.String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY = "vvm_disabled_capabilities_string_array";
+    field public static final java.lang.String KEY_VVM_LEGACY_MODE_ENABLED_BOOL = "vvm_legacy_mode_enabled_bool";
     field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
     field public static final java.lang.String KEY_VVM_PREFETCH_BOOL = "vvm_prefetch_bool";
+    field public static final java.lang.String KEY_VVM_SSL_ENABLED_BOOL = "vvm_ssl_enabled_bool";
     field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
     field public static final java.lang.String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
   }
@@ -38356,6 +38912,7 @@
     method public int getSimState();
     method public int getSimState(int);
     method public java.lang.String getSubscriberId();
+    method public java.lang.String getVisualVoicemailPackageName(android.telecom.PhoneAccountHandle);
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
     method public int getVoiceNetworkType();
@@ -38367,6 +38924,7 @@
     method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(java.lang.String);
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
+    method public boolean isConcurrentVoiceAndDataAllowed();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
     method public boolean isSmsCapable();
@@ -38407,6 +38965,7 @@
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+    field public static final java.lang.String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
     field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
     field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
     field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
@@ -38415,6 +38974,7 @@
     field public static final java.lang.String EXTRA_STATE_OFFHOOK;
     field public static final java.lang.String EXTRA_STATE_RINGING;
     field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
+    field public static final java.lang.String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
     field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
     field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -38960,6 +39520,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+    method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.Context createDeviceProtectedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -39045,6 +39606,7 @@
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public android.content.ComponentName startService(android.content.Intent);
+    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -39107,6 +39669,7 @@
     method public boolean addPermission(android.content.pm.PermissionInfo);
     method public boolean addPermissionAsync(android.content.pm.PermissionInfo);
     method public void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
+    method public boolean canRequestPackageInstalls();
     method public java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]);
     method public int checkPermission(java.lang.String, java.lang.String);
     method public int checkSignatures(java.lang.String, java.lang.String);
@@ -39147,6 +39710,7 @@
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInstaller getPackageInstaller();
     method public int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] getPackagesForUid(int);
@@ -39161,6 +39725,7 @@
     method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo);
     method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
     method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public java.lang.String[] getSystemSharedLibraryNames();
     method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -39390,6 +39955,65 @@
     method public android.text.Editable newEditable(java.lang.CharSequence);
   }
 
+  public final class FontConfig implements android.os.Parcelable {
+    ctor public FontConfig();
+    ctor public FontConfig(android.text.FontConfig);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Alias> getAliases();
+    method public java.util.List<android.text.FontConfig.Family> getFamilies();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
+  }
+
+  public static final class FontConfig.Alias implements android.os.Parcelable {
+    ctor public FontConfig.Alias(java.lang.String, java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getName();
+    method public java.lang.String getToName();
+    method public int getWeight();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Alias> CREATOR;
+  }
+
+  public static final class FontConfig.Axis implements android.os.Parcelable {
+    ctor public FontConfig.Axis(int, float);
+    method public int describeContents();
+    method public float getStyleValue();
+    method public int getTag();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Axis> CREATOR;
+  }
+
+  public static final class FontConfig.Family implements android.os.Parcelable {
+    ctor public FontConfig.Family(java.lang.String, java.util.List<android.text.FontConfig.Font>, java.lang.String, java.lang.String);
+    ctor public FontConfig.Family(android.text.FontConfig.Family);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Font> getFonts();
+    method public java.lang.String getLanguage();
+    method public java.lang.String getName();
+    method public java.lang.String getVariant();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR;
+  }
+
+  public static final class FontConfig.Font implements android.os.Parcelable {
+    ctor public FontConfig.Font(java.lang.String, int, java.util.List<android.text.FontConfig.Axis>, int, boolean);
+    ctor public FontConfig.Font(android.text.FontConfig.Font);
+    method public int describeContents();
+    method public java.util.List<android.text.FontConfig.Axis> getAxes();
+    method public android.os.ParcelFileDescriptor getFd();
+    method public java.lang.String getFontName();
+    method public int getTtcIndex();
+    method public int getWeight();
+    method public boolean isItalic();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.text.FontConfig.Font> CREATOR;
+  }
+
+  public final class FontManager {
+    method public android.text.FontConfig getSystemFonts();
+  }
+
   public abstract interface GetChars implements java.lang.CharSequence {
     method public abstract void getChars(int, int, char[], int);
   }
@@ -39745,22 +40369,6 @@
     method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
   }
 
-  public abstract interface TextAssistant {
-    method public abstract void addLinks(android.text.Spannable, int);
-    method public abstract android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int);
-  }
-
-  public class TextClassification {
-    ctor public TextClassification();
-    method public java.util.Map<java.lang.String, java.lang.Float> getTypeConfidence();
-  }
-
-  public final class TextClassificationManager implements android.text.TextAssistant {
-    method public void addLinks(android.text.Spannable, int);
-    method public java.util.List<android.text.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.text.TextSelection suggestSelection(java.lang.CharSequence, int, int);
-  }
-
   public abstract interface TextDirectionHeuristic {
     method public abstract boolean isRtl(char[], int, int);
     method public abstract boolean isRtl(java.lang.CharSequence, int, int);
@@ -39776,13 +40384,6 @@
     field public static final android.text.TextDirectionHeuristic RTL;
   }
 
-  public final class TextLanguage {
-    ctor public TextLanguage(int, int, java.util.Map<java.lang.String, java.lang.Float>);
-    method public int getEndIndex();
-    method public java.util.Map<java.lang.String, java.lang.Float> getLanguageConfidence();
-    method public int getStartIndex();
-  }
-
   public class TextPaint extends android.graphics.Paint {
     ctor public TextPaint();
     ctor public TextPaint(int);
@@ -39795,13 +40396,6 @@
     field public int linkColor;
   }
 
-  public class TextSelection {
-    ctor public TextSelection();
-    method public int getSelectionEndIndex();
-    method public int getSelectionStartIndex();
-    method public android.text.TextClassification getTextClassification();
-  }
-
   public class TextUtils {
     method public static deprecated java.lang.CharSequence commaEllipsize(java.lang.CharSequence, android.text.TextPaint, float, java.lang.String, java.lang.String);
     method public static java.lang.CharSequence concat(java.lang.CharSequence...);
@@ -42220,7 +42814,9 @@
     method public android.view.Display.Mode[] getSupportedModes();
     method public deprecated float[] getSupportedRefreshRates();
     method public deprecated int getWidth();
+    method public boolean isHdr();
     method public boolean isValid();
+    method public boolean isWideColorGamut();
     field public static final int DEFAULT_DISPLAY = 0; // 0x0
     field public static final int FLAG_PRESENTATION = 8; // 0x8
     field public static final int FLAG_PRIVATE = 4; // 0x4
@@ -42289,7 +42885,7 @@
     method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
     method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
     method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int);
-    method public android.view.View findNextKeyboardNavigationGroup(int, android.view.View, android.view.View, int);
+    method public android.view.View findNextKeyboardNavigationCluster(android.view.View, android.view.View, int);
     method public static android.view.FocusFinder getInstance();
   }
 
@@ -42472,6 +43068,7 @@
     field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
     field public static final int SOURCE_KEYBOARD = 257; // 0x101
     field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_MOUSE_RELATIVE = 131076; // 0x20004
     field public static final int SOURCE_STYLUS = 16386; // 0x4002
     field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
     field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
@@ -43074,7 +43671,7 @@
     method public abstract android.view.SubMenu getSubMenu();
     method public abstract java.lang.CharSequence getTitle();
     method public abstract java.lang.CharSequence getTitleCondensed();
-    method public default java.lang.CharSequence getTooltip();
+    method public default java.lang.CharSequence getTooltipText();
     method public abstract boolean hasSubMenu();
     method public abstract boolean isActionViewExpanded();
     method public abstract boolean isCheckable();
@@ -43101,7 +43698,7 @@
     method public abstract android.view.MenuItem setTitle(java.lang.CharSequence);
     method public abstract android.view.MenuItem setTitle(int);
     method public abstract android.view.MenuItem setTitleCondensed(java.lang.CharSequence);
-    method public default android.view.MenuItem setTooltip(java.lang.CharSequence);
+    method public default android.view.MenuItem setTooltipText(java.lang.CharSequence);
     method public abstract android.view.MenuItem setVisible(boolean);
     field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
     field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
@@ -43589,7 +44186,7 @@
     method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
-    method public void addKeyboardNavigationGroups(int, java.util.Collection<android.view.View>, int);
+    method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
     method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
     method public void addTouchables(java.util.ArrayList<android.view.View>);
@@ -43628,6 +44225,7 @@
     method public void createContextMenu(android.view.ContextMenu);
     method public void destroyDrawingCache();
     method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
+    method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
     method public void dispatchDisplayHint(int);
     method public boolean dispatchDragEvent(android.view.DragEvent);
@@ -43646,6 +44244,7 @@
     method public boolean dispatchNestedPrePerformAccessibilityAction(int, android.os.Bundle);
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
+    method public void dispatchPointerCaptureChanged(boolean);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void dispatchProvideAutoFillStructure(android.view.ViewStructure, int);
     method public void dispatchProvideStructure(android.view.ViewStructure);
@@ -43711,6 +44310,7 @@
     method public float getElevation();
     method public boolean getFilterTouchesWhenObscured();
     method public boolean getFitsSystemWindows();
+    method public int getFocusable();
     method public java.util.ArrayList<android.view.View> getFocusables(int);
     method public void getFocusedRect(android.graphics.Rect);
     method public android.graphics.drawable.Drawable getForeground();
@@ -43753,7 +44353,6 @@
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
-    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -43797,7 +44396,7 @@
     method public java.lang.Object getTag(int);
     method public int getTextAlignment();
     method public int getTextDirection();
-    method public final java.lang.CharSequence getTooltip();
+    method public final java.lang.CharSequence getTooltipText();
     method public android.view.View getTooltipView();
     method public final int getTop();
     method protected float getTopFadingEdgeStrength();
@@ -43828,6 +44427,7 @@
     method public boolean hasNestedScrollingParent();
     method public boolean hasOnClickListeners();
     method public boolean hasOverlappingRendering();
+    method public boolean hasPointerCapture();
     method public boolean hasTransientState();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
@@ -43859,7 +44459,6 @@
     method public boolean isInLayout();
     method public boolean isInTouchMode();
     method public final boolean isKeyboardNavigationCluster();
-    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -43882,7 +44481,7 @@
     method public boolean isVerticalFadingEdgeEnabled();
     method public boolean isVerticalScrollBarEnabled();
     method public void jumpDrawablesToCurrentState();
-    method public android.view.View keyboardNavigationGroupSearch(int, android.view.View, int);
+    method public android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public void layout(int, int, int, int);
     method public final void measure(int, int);
     method protected static int[] mergeDrawableStates(int[], int[]);
@@ -43893,6 +44492,7 @@
     method public android.view.WindowInsets onApplyWindowInsets(android.view.WindowInsets);
     method protected void onAttachedToWindow();
     method public void onCancelPendingInputEvents();
+    method public boolean onCapturedPointerEvent(android.view.MotionEvent);
     method public boolean onCheckIsTextEditor();
     method protected void onConfigurationChanged(android.content.res.Configuration);
     method protected void onCreateContextMenu(android.view.ContextMenu);
@@ -43922,6 +44522,7 @@
     method protected void onLayout(boolean, int, int, int, int);
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
+    method public void onPointerCaptureChange(boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void onProvideAutoFillStructure(android.view.ViewStructure, int);
     method public void onProvideAutoFillVirtualStructure(android.view.ViewStructure, int);
@@ -43964,6 +44565,7 @@
     method public void postOnAnimation(java.lang.Runnable);
     method public void postOnAnimationDelayed(java.lang.Runnable, long);
     method public void refreshDrawableState();
+    method public void releasePointerCapture();
     method public boolean removeCallbacks(java.lang.Runnable);
     method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
@@ -43974,6 +44576,7 @@
     method public boolean requestFocus(int, android.graphics.Rect);
     method public final boolean requestFocusFromTouch();
     method public void requestLayout();
+    method public void requestPointerCapture();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
     method public final void requestUnbufferedDispatch(android.view.MotionEvent);
@@ -44017,6 +44620,7 @@
     method public void setFilterTouchesWhenObscured(boolean);
     method public void setFitsSystemWindows(boolean);
     method public void setFocusable(boolean);
+    method public void setFocusable(int);
     method public void setFocusableInTouchMode(boolean);
     method public void setFocusedByDefault(boolean);
     method public void setForeground(android.graphics.drawable.Drawable);
@@ -44032,7 +44636,6 @@
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
     method public void setKeyboardNavigationCluster(boolean);
-    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -44050,8 +44653,8 @@
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
-    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
+    method public void setOnCapturedPointerListener(android.view.View.OnCapturedPointerListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
     method public void setOnCreateContextMenuListener(android.view.View.OnCreateContextMenuListener);
@@ -44099,7 +44702,7 @@
     method public void setTag(int, java.lang.Object);
     method public void setTextAlignment(int);
     method public void setTextDirection(int);
-    method public final void setTooltip(java.lang.CharSequence);
+    method public final void setTooltipText(java.lang.CharSequence);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
     method public final void setTransitionName(java.lang.String);
@@ -44156,8 +44759,10 @@
     field protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET;
     field public static final int FIND_VIEWS_WITH_CONTENT_DESCRIPTION = 2; // 0x2
     field public static final int FIND_VIEWS_WITH_TEXT = 1; // 0x1
+    field public static final int FOCUSABLE = 1; // 0x1
     field public static final int FOCUSABLES_ALL = 0; // 0x0
     field public static final int FOCUSABLES_TOUCH_MODE = 1; // 0x1
+    field public static final int FOCUSABLE_AUTO = 16; // 0x10
     field protected static final int[] FOCUSED_SELECTED_STATE_SET;
     field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_STATE_SET;
@@ -44176,8 +44781,6 @@
     field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
     field public static final int INVISIBLE = 4; // 0x4
     field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
-    field public static final int KEYBOARD_NAVIGATION_GROUP_CLUSTER = 1; // 0x1
-    field public static final int KEYBOARD_NAVIGATION_GROUP_SECTION = 2; // 0x2
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
     field public static final int LAYER_TYPE_NONE = 0; // 0x0
     field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
@@ -44189,6 +44792,7 @@
     field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
     field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
     field public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field public static final int NOT_FOCUSABLE = 0; // 0x0
     field public static final int NO_ID = -1; // 0xffffffff
     field public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
     field public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
@@ -44323,6 +44927,10 @@
     method public abstract void onViewDetachedFromWindow(android.view.View);
   }
 
+  public static abstract interface View.OnCapturedPointerListener {
+    method public abstract boolean onCapturedPointer(android.view.View, android.view.MotionEvent);
+  }
+
   public static abstract interface View.OnClickListener {
     method public abstract void onClick(android.view.View);
   }
@@ -44700,7 +45308,7 @@
     method public abstract boolean isLayoutRequested();
     method public abstract boolean isTextAlignmentResolved();
     method public abstract boolean isTextDirectionResolved();
-    method public abstract android.view.View keyboardNavigationGroupSearch(int, android.view.View, int);
+    method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
     method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
     method public abstract boolean onNestedPreFling(android.view.View, float, float);
@@ -44897,6 +45505,7 @@
     method public boolean getAllowReturnTransitionOverlap();
     method public final android.view.WindowManager.LayoutParams getAttributes();
     method public final android.view.Window.Callback getCallback();
+    method public int getColorMode();
     method public final android.view.Window getContainer();
     method public android.transition.Scene getContentScene();
     method public final android.content.Context getContext();
@@ -44953,6 +45562,7 @@
     method public abstract void setChildDrawable(int, android.graphics.drawable.Drawable);
     method public abstract void setChildInt(int, int);
     method public void setClipToOutline(boolean);
+    method public void setColorMode(int);
     method public void setContainer(android.view.Window);
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
@@ -45057,6 +45667,7 @@
     method public abstract boolean onMenuItemSelected(int, android.view.MenuItem);
     method public abstract boolean onMenuOpened(int, android.view.Menu);
     method public abstract void onPanelClosed(int, android.view.Menu);
+    method public default void onPointerCaptureChanged(boolean);
     method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public default void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
     method public abstract boolean onSearchRequested();
@@ -45151,8 +45762,10 @@
     method public final int copyFrom(android.view.WindowManager.LayoutParams);
     method public java.lang.String debug(java.lang.String);
     method public int describeContents();
+    method public int getColorMode();
     method public final java.lang.CharSequence getTitle();
     method public static boolean mayUseInputMethod(int);
+    method public void setColorMode(int);
     method public final void setTitle(java.lang.CharSequence);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ALPHA_CHANGED = 128; // 0x80
@@ -46023,6 +46636,13 @@
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
   }
 
+  public final class AutoFillManager {
+    method public void updateAutoFillInput(android.view.View, int);
+    method public void updateAutoFillInput(android.view.View, int, android.graphics.Rect, int);
+    field public static final int FLAG_UPDATE_UI_HIDE = 2; // 0x2
+    field public static final int FLAG_UPDATE_UI_SHOW = 1; // 0x1
+  }
+
   public final class AutoFillType implements android.os.Parcelable {
     method public int describeContents();
     method public static android.view.autofill.AutoFillType forList();
@@ -46057,6 +46677,8 @@
   public static final class Dataset.Builder {
     ctor public Dataset.Builder(java.lang.CharSequence);
     method public android.view.autofill.Dataset build();
+    method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
+    method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
     method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
     method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
@@ -46072,6 +46694,8 @@
     method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
     method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
     method public android.view.autofill.FillResponse build();
+    method public android.view.autofill.FillResponse.Builder requiresCustomAuthentication(android.os.Bundle, int);
+    method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
     method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
@@ -46082,7 +46706,7 @@
 
   public static abstract class VirtualViewDelegate.Callback {
     ctor public VirtualViewDelegate.Callback();
-    method public void onFocusChanged(int, boolean);
+    method public void onAutoFillInputUpdated(int, android.graphics.Rect, int);
     method public void onNodeRemoved(int...);
     method public void onValueChanged(int);
   }
@@ -46489,6 +47113,83 @@
 
 }
 
+package android.view.textclassifier {
+
+  public abstract interface LinksInfo {
+    method public abstract boolean apply(java.lang.CharSequence);
+  }
+
+  public final class TextClassificationManager {
+    method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
+    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+  }
+
+  public final class TextClassificationResult {
+    method public float getConfidenceScore(java.lang.String);
+    method public java.lang.String getEntity(int);
+    method public int getEntityCount();
+    method public android.graphics.drawable.Drawable getIcon();
+    method public android.content.Intent getIntent();
+    method public java.lang.CharSequence getLabel();
+    method public android.view.View.OnClickListener getOnClickListener();
+    method public java.lang.String getText();
+  }
+
+  public static final class TextClassificationResult.Builder {
+    ctor public TextClassificationResult.Builder();
+    method public android.view.textclassifier.TextClassificationResult build();
+    method public android.view.textclassifier.TextClassificationResult.Builder setEntityType(java.lang.String, float);
+    method public android.view.textclassifier.TextClassificationResult.Builder setIcon(android.graphics.drawable.Drawable);
+    method public android.view.textclassifier.TextClassificationResult.Builder setIntent(android.content.Intent);
+    method public android.view.textclassifier.TextClassificationResult.Builder setLabel(java.lang.String);
+    method public android.view.textclassifier.TextClassificationResult.Builder setOnClickListener(android.view.View.OnClickListener);
+    method public android.view.textclassifier.TextClassificationResult.Builder setText(java.lang.String);
+  }
+
+  public abstract interface TextClassifier {
+    method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
+    method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
+    method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+    field public static final android.view.textclassifier.TextClassifier NO_OP;
+    field public static final java.lang.String TYPE_ADDRESS = "address";
+    field public static final java.lang.String TYPE_EMAIL = "email";
+    field public static final java.lang.String TYPE_OTHER = "other";
+    field public static final java.lang.String TYPE_PHONE = "phone";
+  }
+
+  public static abstract class TextClassifier.EntityType implements java.lang.annotation.Annotation {
+  }
+
+  public final class TextLanguage {
+    method public float getConfidenceScore(java.util.Locale);
+    method public int getEndIndex();
+    method public java.util.Locale getLanguage(int);
+    method public int getLanguageCount();
+    method public int getStartIndex();
+  }
+
+  public static final class TextLanguage.Builder {
+    ctor public TextLanguage.Builder(int, int);
+    method public android.view.textclassifier.TextLanguage build();
+    method public android.view.textclassifier.TextLanguage.Builder setLanguage(java.util.Locale, float);
+  }
+
+  public final class TextSelection {
+    method public float getConfidenceScore(java.lang.String);
+    method public java.lang.String getEntity(int);
+    method public int getEntityCount();
+    method public int getSelectionEndIndex();
+    method public int getSelectionStartIndex();
+  }
+
+  public static final class TextSelection.Builder {
+    ctor public TextSelection.Builder(int, int);
+    method public android.view.textclassifier.TextSelection build();
+    method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
+  }
+
+}
+
 package android.view.textservice {
 
   public final class SentenceSuggestionsInfo implements android.os.Parcelable {
@@ -46719,6 +47420,7 @@
   public abstract class RenderProcessGoneDetail {
     ctor public RenderProcessGoneDetail();
     method public abstract boolean didCrash();
+    method public abstract int rendererPriorityAtExit();
   }
 
   public class ServiceWorkerClient {
@@ -47135,6 +47837,8 @@
     method public deprecated java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String);
     method public java.lang.String getOriginalUrl();
     method public int getProgress();
+    method public boolean getRendererPriorityWaivedWhenNotVisible();
+    method public int getRendererRequestedPriority();
     method public deprecated float getScale();
     method public android.webkit.WebSettings getSettings();
     method public java.lang.String getTitle();
@@ -47182,6 +47886,7 @@
     method public deprecated void setMapTrackballToArrowKeys(boolean);
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
+    method public void setRendererPriorityPolicy(int, boolean);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
@@ -47191,6 +47896,9 @@
     method public void zoomBy(float);
     method public boolean zoomIn();
     method public boolean zoomOut();
+    field public static final int RENDERER_PRIORITY_BOUND = 1; // 0x1
+    field public static final int RENDERER_PRIORITY_IMPORTANT = 2; // 0x2
+    field public static final int RENDERER_PRIORITY_WAIVED = 0; // 0x0
     field public static final java.lang.String SCHEME_GEO = "geo:0,0?q=";
     field public static final java.lang.String SCHEME_MAILTO = "mailto:";
     field public static final java.lang.String SCHEME_TEL = "tel:";
@@ -48669,6 +49377,7 @@
     method public int getGravity();
     method public android.view.Menu getMenu();
     method public android.view.MenuInflater getMenuInflater();
+    method public android.widget.ListView getMenuListView();
     method public void inflate(int);
     method public void setGravity(int);
     method public void setOnDismissListener(android.widget.PopupMenu.OnDismissListener);
@@ -49502,6 +50211,10 @@
     method public void endBatchEdit();
     method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText);
     method public final int getAutoLinkMask();
+    method public int getAutoSizeMaxTextSize();
+    method public int getAutoSizeMinTextSize();
+    method public int getAutoSizeStepGranularity();
+    method public int getAutoSizeTextType();
     method public int getBreakStrategy();
     method public int getCompoundDrawablePadding();
     method public android.content.res.ColorStateList getCompoundDrawableTintList();
@@ -49527,7 +50240,6 @@
     method public int getExtendedPaddingTop();
     method public android.text.InputFilter[] getFilters();
     method public java.lang.String getFontFeatureSettings();
-    method public java.lang.String getFontVariationSettings();
     method public boolean getFreezesText();
     method public int getGravity();
     method public int getHighlightColor();
@@ -49574,7 +50286,7 @@
     method public float getShadowRadius();
     method public final boolean getShowSoftInputOnFocus();
     method public java.lang.CharSequence getText();
-    method public android.text.TextAssistant getTextAssistant();
+    method public android.view.textclassifier.TextClassifier getTextClassifier();
     method public final android.content.res.ColorStateList getTextColors();
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
@@ -49611,6 +50323,10 @@
     method public void removeTextChangedListener(android.text.TextWatcher);
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
+    method public void setAutoSizeMaxTextSize(int, float);
+    method public void setAutoSizeMinTextSize(int, float);
+    method public void setAutoSizeStepGranularity(int, float);
+    method public void setAutoSizeTextType(int);
     method public void setBreakStrategy(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
@@ -49633,7 +50349,6 @@
     method public void setExtractedText(android.view.inputmethod.ExtractedText);
     method public void setFilters(android.text.InputFilter[]);
     method public void setFontFeatureSettings(java.lang.String);
-    method public void setFontVariationSettings(java.lang.String);
     method protected boolean setFrame(int, int, int, int);
     method public void setFreezesText(boolean);
     method public void setGravity(int);
@@ -49687,7 +50402,7 @@
     method public final void setText(int, android.widget.TextView.BufferType);
     method public void setTextAppearance(int);
     method public deprecated void setTextAppearance(android.content.Context, int);
-    method public void setTextAssistant(android.text.TextAssistant);
+    method public void setTextClassifier(android.view.textclassifier.TextClassifier);
     method public void setTextColor(int);
     method public void setTextColor(android.content.res.ColorStateList);
     method public void setTextIsSelectable(boolean);
@@ -49702,8 +50417,8 @@
     method public void setTypeface(android.graphics.Typeface, int);
     method public void setTypeface(android.graphics.Typeface);
     method public void setWidth(int);
-    field public static final int AUTO_SIZE_TYPE_NONE = 0; // 0x0
-    field public static final int AUTO_SIZE_TYPE_XY = 1; // 0x1
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_XY = 1; // 0x1
   }
 
   public static final class TextView.BufferType extends java.lang.Enum {
@@ -50330,6 +51045,10 @@
     method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
   }
 
+  public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+    ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
+  }
+
   public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
     ctor public PathClassLoader(java.lang.String, java.lang.ClassLoader);
     ctor public PathClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader);
@@ -53428,6 +54147,133 @@
 
 }
 
+package java.lang.invoke {
+
+  public class LambdaConversionException extends java.lang.Exception {
+    ctor public LambdaConversionException();
+    ctor public LambdaConversionException(java.lang.String);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.Throwable);
+    ctor public LambdaConversionException(java.lang.String, java.lang.Throwable, boolean, boolean);
+  }
+
+  public abstract class MethodHandle {
+    method public java.lang.invoke.MethodHandle asFixedArity();
+    method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
+    method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>);
+    method public java.lang.invoke.MethodHandle bindTo(java.lang.Object);
+    method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable;
+    method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+    method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
+    method public boolean isVarargsCollector();
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public abstract interface MethodHandleInfo {
+    method public abstract java.lang.Class<?> getDeclaringClass();
+    method public abstract java.lang.invoke.MethodType getMethodType();
+    method public abstract int getModifiers();
+    method public abstract java.lang.String getName();
+    method public abstract int getReferenceKind();
+    method public default boolean isVarArgs();
+    method public static boolean refKindIsField(int);
+    method public static boolean refKindIsValid(int);
+    method public static java.lang.String refKindName(int);
+    method public static java.lang.String referenceKindToString(int);
+    method public abstract <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandles.Lookup);
+    method public static java.lang.String toString(int, java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType);
+    field public static final int REF_getField = 1; // 0x1
+    field public static final int REF_getStatic = 2; // 0x2
+    field public static final int REF_invokeInterface = 9; // 0x9
+    field public static final int REF_invokeSpecial = 7; // 0x7
+    field public static final int REF_invokeStatic = 6; // 0x6
+    field public static final int REF_invokeVirtual = 5; // 0x5
+    field public static final int REF_newInvokeSpecial = 8; // 0x8
+    field public static final int REF_putField = 3; // 0x3
+    field public static final int REF_putStatic = 4; // 0x4
+  }
+
+  public class MethodHandles {
+    method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandles.Lookup lookup();
+    method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
+    method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+    method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>);
+  }
+
+  public static final class MethodHandles.Lookup {
+    method public java.lang.invoke.MethodHandle bind(java.lang.Object, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findConstructor(java.lang.Class<?>, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSpecial(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStatic(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStaticGetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findStaticSetter(java.lang.Class<?>, java.lang.String, java.lang.Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findVirtual(java.lang.Class<?>, java.lang.String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>);
+    method public java.lang.Class<?> lookupClass();
+    method public int lookupModes();
+    method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSpecial(java.lang.reflect.Method, java.lang.Class<?>) throws java.lang.IllegalAccessException;
+    field public static final int PACKAGE = 8; // 0x8
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+    field public static final int PUBLIC = 1; // 0x1
+  }
+
+  public final class MethodType implements java.io.Serializable {
+    method public java.lang.invoke.MethodType appendParameterTypes(java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType appendParameterTypes(java.util.List<java.lang.Class<?>>);
+    method public java.lang.invoke.MethodType changeParameterType(int, java.lang.Class<?>);
+    method public java.lang.invoke.MethodType changeReturnType(java.lang.Class<?>);
+    method public java.lang.invoke.MethodType dropParameterTypes(int, int);
+    method public java.lang.invoke.MethodType erase();
+    method public static java.lang.invoke.MethodType fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) throws java.lang.IllegalArgumentException, java.lang.TypeNotPresentException;
+    method public java.lang.invoke.MethodType generic();
+    method public static java.lang.invoke.MethodType genericMethodType(int, boolean);
+    method public static java.lang.invoke.MethodType genericMethodType(int);
+    method public boolean hasPrimitives();
+    method public boolean hasWrappers();
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.lang.Class<?>...);
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>[]);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>, java.lang.Class<?>...);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.Class<?>);
+    method public static java.lang.invoke.MethodType methodType(java.lang.Class<?>, java.lang.invoke.MethodType);
+    method public java.lang.Class<?>[] parameterArray();
+    method public int parameterCount();
+    method public java.util.List<java.lang.Class<?>> parameterList();
+    method public java.lang.Class<?> parameterType(int);
+    method public java.lang.Class<?> returnType();
+    method public java.lang.String toMethodDescriptorString();
+    method public java.lang.invoke.MethodType unwrap();
+    method public java.lang.invoke.MethodType wrap();
+  }
+
+  public class WrongMethodTypeException extends java.lang.RuntimeException {
+    ctor public WrongMethodTypeException();
+    ctor public WrongMethodTypeException(java.lang.String);
+  }
+
+}
+
 package java.lang.ref {
 
   public class PhantomReference<T> extends java.lang.ref.Reference {
@@ -61707,6 +62553,9 @@
     method public static <E> java.util.Collection<E> checkedCollection(java.util.Collection<E>, java.lang.Class<E>);
     method public static <E> java.util.List<E> checkedList(java.util.List<E>, java.lang.Class<E>);
     method public static <K, V> java.util.Map<K, V> checkedMap(java.util.Map<K, V>, java.lang.Class<K>, java.lang.Class<V>);
+    method public static <K, V> java.util.NavigableMap<K, V> checkedNavigableMap(java.util.NavigableMap<K, V>, java.lang.Class<K>, java.lang.Class<V>);
+    method public static <E> java.util.NavigableSet<E> checkedNavigableSet(java.util.NavigableSet<E>, java.lang.Class<E>);
+    method public static <E> java.util.Queue<E> checkedQueue(java.util.Queue<E>, java.lang.Class<E>);
     method public static <E> java.util.Set<E> checkedSet(java.util.Set<E>, java.lang.Class<E>);
     method public static <K, V> java.util.SortedMap<K, V> checkedSortedMap(java.util.SortedMap<K, V>, java.lang.Class<K>, java.lang.Class<V>);
     method public static <E> java.util.SortedSet<E> checkedSortedSet(java.util.SortedSet<E>, java.lang.Class<E>);
@@ -61717,7 +62566,11 @@
     method public static final <T> java.util.List<T> emptyList();
     method public static <T> java.util.ListIterator<T> emptyListIterator();
     method public static final <K, V> java.util.Map<K, V> emptyMap();
+    method public static final <K, V> java.util.NavigableMap<K, V> emptyNavigableMap();
+    method public static <E> java.util.NavigableSet<E> emptyNavigableSet();
     method public static final <T> java.util.Set<T> emptySet();
+    method public static final <K, V> java.util.SortedMap<K, V> emptySortedMap();
+    method public static <E> java.util.SortedSet<E> emptySortedSet();
     method public static <T> java.util.Enumeration<T> enumeration(java.util.Collection<T>);
     method public static <T> void fill(java.util.List<? super T>, T);
     method public static int frequency(java.util.Collection<?>, java.lang.Object);
@@ -61746,12 +62599,16 @@
     method public static <T> java.util.Collection<T> synchronizedCollection(java.util.Collection<T>);
     method public static <T> java.util.List<T> synchronizedList(java.util.List<T>);
     method public static <K, V> java.util.Map<K, V> synchronizedMap(java.util.Map<K, V>);
+    method public static <K, V> java.util.NavigableMap<K, V> synchronizedNavigableMap(java.util.NavigableMap<K, V>);
+    method public static <T> java.util.NavigableSet<T> synchronizedNavigableSet(java.util.NavigableSet<T>);
     method public static <T> java.util.Set<T> synchronizedSet(java.util.Set<T>);
     method public static <K, V> java.util.SortedMap<K, V> synchronizedSortedMap(java.util.SortedMap<K, V>);
     method public static <T> java.util.SortedSet<T> synchronizedSortedSet(java.util.SortedSet<T>);
     method public static <T> java.util.Collection<T> unmodifiableCollection(java.util.Collection<? extends T>);
     method public static <T> java.util.List<T> unmodifiableList(java.util.List<? extends T>);
     method public static <K, V> java.util.Map<K, V> unmodifiableMap(java.util.Map<? extends K, ? extends V>);
+    method public static <K, V> java.util.NavigableMap<K, V> unmodifiableNavigableMap(java.util.NavigableMap<K, ? extends V>);
+    method public static <T> java.util.NavigableSet<T> unmodifiableNavigableSet(java.util.NavigableSet<T>);
     method public static <T> java.util.Set<T> unmodifiableSet(java.util.Set<? extends T>);
     method public static <K, V> java.util.SortedMap<K, V> unmodifiableSortedMap(java.util.SortedMap<K, ? extends V>);
     method public static <T> java.util.SortedSet<T> unmodifiableSortedSet(java.util.SortedSet<T>);
@@ -65196,14 +66053,14 @@
     method public java.util.logging.ErrorManager getErrorManager();
     method public java.util.logging.Filter getFilter();
     method public java.util.logging.Formatter getFormatter();
-    method public synchronized java.util.logging.Level getLevel();
+    method public java.util.logging.Level getLevel();
     method public boolean isLoggable(java.util.logging.LogRecord);
     method public abstract void publish(java.util.logging.LogRecord);
     method protected void reportError(java.lang.String, java.lang.Exception, int);
-    method public void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
-    method public void setErrorManager(java.util.logging.ErrorManager);
-    method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
-    method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+    method public synchronized void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
+    method public synchronized void setErrorManager(java.util.logging.ErrorManager);
+    method public synchronized void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+    method public synchronized void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
     method public synchronized void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
   }
 
@@ -65230,7 +66087,7 @@
   public class LogManager {
     ctor protected LogManager();
     method public boolean addLogger(java.util.logging.Logger);
-    method public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+    method public deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
     method public void checkAccess() throws java.lang.SecurityException;
     method public static java.util.logging.LogManager getLogManager();
     method public java.util.logging.Logger getLogger(java.lang.String);
@@ -65239,7 +66096,7 @@
     method public java.lang.String getProperty(java.lang.String);
     method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
     method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
-    method public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+    method public deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
     method public void reset() throws java.lang.SecurityException;
     field public static final java.lang.String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
   }
@@ -65276,14 +66133,18 @@
     ctor protected Logger(java.lang.String, java.lang.String);
     method public void addHandler(java.util.logging.Handler) throws java.lang.SecurityException;
     method public void config(java.lang.String);
+    method public void config(java.util.function.Supplier<java.lang.String>);
     method public void entering(java.lang.String, java.lang.String);
     method public void entering(java.lang.String, java.lang.String, java.lang.Object);
     method public void entering(java.lang.String, java.lang.String, java.lang.Object[]);
     method public void exiting(java.lang.String, java.lang.String);
     method public void exiting(java.lang.String, java.lang.String, java.lang.Object);
     method public void fine(java.lang.String);
+    method public void fine(java.util.function.Supplier<java.lang.String>);
     method public void finer(java.lang.String);
+    method public void finer(java.util.function.Supplier<java.lang.String>);
     method public void finest(java.lang.String);
+    method public void finest(java.util.function.Supplier<java.lang.String>);
     method public static java.util.logging.Logger getAnonymousLogger();
     method public static java.util.logging.Logger getAnonymousLogger(java.lang.String);
     method public java.util.logging.Filter getFilter();
@@ -65298,28 +66159,38 @@
     method public java.lang.String getResourceBundleName();
     method public boolean getUseParentHandlers();
     method public void info(java.lang.String);
+    method public void info(java.util.function.Supplier<java.lang.String>);
     method public boolean isLoggable(java.util.logging.Level);
     method public void log(java.util.logging.LogRecord);
     method public void log(java.util.logging.Level, java.lang.String);
+    method public void log(java.util.logging.Level, java.util.function.Supplier<java.lang.String>);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Object);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Object[]);
     method public void log(java.util.logging.Level, java.lang.String, java.lang.Throwable);
+    method public void log(java.util.logging.Level, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String);
+    method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.util.function.Supplier<java.lang.String>);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
     method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
-    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+    method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
+    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Object...);
+    method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+    method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Throwable);
     method public void removeHandler(java.util.logging.Handler) throws java.lang.SecurityException;
     method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
     method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
     method public void setParent(java.util.logging.Logger);
+    method public void setResourceBundle(java.util.ResourceBundle);
     method public void setUseParentHandlers(boolean);
     method public void severe(java.lang.String);
+    method public void severe(java.util.function.Supplier<java.lang.String>);
     method public void throwing(java.lang.String, java.lang.String, java.lang.Throwable);
     method public void warning(java.lang.String);
+    method public void warning(java.util.function.Supplier<java.lang.String>);
     field public static final java.lang.String GLOBAL_LOGGER_NAME = "global";
     field public static final deprecated java.util.logging.Logger global;
   }
@@ -65340,10 +66211,10 @@
     ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
     method public void close() throws java.lang.SecurityException;
     method public void flush();
-    method public synchronized java.util.logging.Level getPushLevel();
+    method public java.util.logging.Level getPushLevel();
     method public synchronized void publish(java.util.logging.LogRecord);
     method public synchronized void push();
-    method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
+    method public synchronized void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
   }
 
   public class SimpleFormatter extends java.util.logging.Formatter {
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index d5580ac..0ea141c 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -184,10 +184,6 @@
 
 int main(int argc, char* const argv[])
 {
-    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
-        LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
-    }
-
     if (!LOG_NDEBUG) {
       String8 argv_String;
       for (int i = 0; i < argc; ++i) {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 5bf8076..7e91391 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -18,20 +18,24 @@
 
 import android.app.backup.BackupManager;
 import android.app.backup.BackupProgress;
-import android.app.backup.RestoreSet;
 import android.app.backup.IBackupManager;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IRestoreObserver;
 import android.app.backup.IRestoreSession;
+import android.app.backup.RestoreSet;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 
 public final class Bmgr {
     IBackupManager mBmgr;
@@ -270,7 +274,7 @@
         }
     }
 
-    private void backupNowAllPackages() {
+    private void backupNowAllPackages(boolean nonIncrementalBackup) {
         int userId = UserHandle.USER_SYSTEM;
         IPackageManager mPm =
                 IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
@@ -297,14 +301,19 @@
                     System.err.println(BMGR_NOT_RUNNING_ERR);
                 }
             }
-            backupNowPackages(packages);
+            backupNowPackages(packages, nonIncrementalBackup);
         }
     }
 
-    private void backupNowPackages(List<String> packages) {
+    private void backupNowPackages(List<String> packages, boolean nonIncrementalBackup) {
+        int flags = 0;
+        if (nonIncrementalBackup) {
+            flags |= BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
+        }
         try {
             BackupObserver observer = new BackupObserver();
-            int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer);
+            int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer,
+                    flags);
             if (err == 0) {
                 // Off and running -- wait for the backup to complete
                 observer.waitForCompletion();
@@ -320,24 +329,31 @@
     private void doBackupNow() {
         String pkg;
         boolean backupAll = false;
+        boolean nonIncrementalBackup = false;
         ArrayList<String> allPkgs = new ArrayList<String>();
         while ((pkg = nextArg()) != null) {
             if (pkg.equals("--all")) {
                 backupAll = true;
+            } else if (pkg.equals("--non-incremental")) {
+                nonIncrementalBackup = true;
+            } else if (pkg.equals("--incremental")) {
+                nonIncrementalBackup = false;
             } else {
                 allPkgs.add(pkg);
             }
         }
         if (backupAll) {
             if (allPkgs.size() == 0) {
-                System.out.println("Running backup for all packages.");
-                backupNowAllPackages();
+                System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
+                        "incremental backup for all packages.");
+                backupNowAllPackages(nonIncrementalBackup);
             } else {
                 System.err.println("Provide only '--all' flag or list of packages.");
             }
         } else if (allPkgs.size() > 0) {
-            System.out.println("Running backup for " + allPkgs.size() +" requested packages.");
-            backupNowPackages(allPkgs);
+            System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
+                    "incremental backup for " + allPkgs.size() +" requested packages.");
+            backupNowPackages(allPkgs, nonIncrementalBackup);
         } else {
             System.err.println("Provide '--all' flag or list of packages.");
         }
@@ -351,6 +367,11 @@
                 return;
             }
 
+            if ("-c".equals(which)) {
+                doTransportByComponent();
+                return;
+            }
+
             String old = mBmgr.selectBackupTransport(which);
             if (old == null) {
                 System.out.println("Unknown transport '" + which
@@ -358,12 +379,50 @@
             } else {
                 System.out.println("Selected transport " + which + " (formerly " + old + ")");
             }
+
         } catch (RemoteException e) {
             System.err.println(e.toString());
             System.err.println(BMGR_NOT_RUNNING_ERR);
         }
     }
 
+    private void doTransportByComponent() {
+        String which = nextArg();
+        if (which == null) {
+            showUsage();
+            return;
+        }
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        try {
+            mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
+                    new ISelectBackupTransportCallback.Stub() {
+                        @Override
+                        public void onSuccess(String transportName) {
+                            System.out.println("Success. Selected transport: " + transportName);
+                            latch.countDown();
+                        }
+
+                        @Override
+                        public void onFailure(int reason) {
+                            System.err.println("Failure. error=" + reason);
+                            latch.countDown();
+                        }
+                    });
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(BMGR_NOT_RUNNING_ERR);
+            return;
+        }
+
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+            System.err.println("Operation interrupted.");
+        }
+    }
+
     private void doWipe() {
         String transport = nextArg();
         if (transport == null) {
@@ -415,7 +474,16 @@
     }
 
     private void doListTransports() {
+        String arg = nextArg();
+
         try {
+            if ("-c".equals(arg)) {
+                for (ComponentName transport : mBmgr.listAllTransportComponents()) {
+                    System.out.println(transport.flattenToShortString());
+                }
+                return;
+            }
+
             String current = mBmgr.getCurrentTransport();
             String[] transports = mBmgr.listAllTransports();
             if (transports == null || transports.length == 0) {
@@ -637,9 +705,9 @@
         System.err.println("       bmgr backup PACKAGE");
         System.err.println("       bmgr enable BOOL");
         System.err.println("       bmgr enabled");
-        System.err.println("       bmgr list transports");
+        System.err.println("       bmgr list transports [-c]");
         System.err.println("       bmgr list sets");
-        System.err.println("       bmgr transport WHICH");
+        System.err.println("       bmgr transport WHICH|-c WHICH_COMPONENT");
         System.err.println("       bmgr restore TOKEN");
         System.err.println("       bmgr restore TOKEN PACKAGE...");
         System.err.println("       bmgr restore PACKAGE");
@@ -661,15 +729,18 @@
         System.err.println("the backup mechanism.");
         System.err.println("");
         System.err.println("The 'list transports' command reports the names of the backup transports");
-        System.err.println("currently available on the device.  These names can be passed as arguments");
+        System.err.println("BackupManager is currently bound to. These names can be passed as arguments");
         System.err.println("to the 'transport' and 'wipe' commands.  The currently active transport");
-        System.err.println("is indicated with a '*' character.");
+        System.err.println("is indicated with a '*' character. If -c flag is used, all available");
+        System.err.println("transport components on the device are listed. These can be used with");
+        System.err.println("the component variant of 'transport' command.");
         System.err.println("");
         System.err.println("The 'list sets' command reports the token and name of each restore set");
         System.err.println("available to the device via the currently active transport.");
         System.err.println("");
         System.err.println("The 'transport' command designates the named transport as the currently");
-        System.err.println("active one.  This setting is persistent across reboots.");
+        System.err.println("active one.  This setting is persistent across reboots. If -c flag is");
+        System.err.println("specified, the following string is treated as a component name.");
         System.err.println("");
         System.err.println("The 'restore' command when given just a restore token initiates a full-system");
         System.err.println("restore operation from the currently active transport.  It will deliver");
diff --git a/cmds/media/src/com/android/commands/media/VolumeCtrl.java b/cmds/media/src/com/android/commands/media/VolumeCtrl.java
index a171932..1629c6f 100755
--- a/cmds/media/src/com/android/commands/media/VolumeCtrl.java
+++ b/cmds/media/src/com/android/commands/media/VolumeCtrl.java
@@ -42,6 +42,7 @@
 
     // --stream affects --set, --adj or --get options.
     // --show affects --set and --adj options.
+    // --get can be used with --set, --adj or by itself.
     public final static String USAGE = new String(
             "the options are as follows: \n" +
             "\t\t--stream STREAM selects the stream to control, see AudioManager.STREAM_*\n" +
@@ -56,9 +57,8 @@
             "\t\tadb shell media volume --stream 3 --get\n"
             );
 
-    private final static int VOLUME_CONTROL_MODE_SET = 0;
-    private final static int VOLUME_CONTROL_MODE_ADJUST = 1;
-    private final static int VOLUME_CONTROL_MODE_GET = 2;
+    private final static int VOLUME_CONTROL_MODE_SET = 1;
+    private final static int VOLUME_CONTROL_MODE_ADJUST = 2;
 
     private final static String ADJUST_LOWER = "lower";
     private final static String ADJUST_SAME = "same";
@@ -69,9 +69,10 @@
         // Default parameters
         int stream = AudioManager.STREAM_MUSIC;
         int volIndex = 5;
-        int mode = VOLUME_CONTROL_MODE_SET;
+        int mode = 0;
         int adjDir = AudioManager.ADJUST_RAISE;
         boolean showUi = false;
+        boolean doGet = false;
 
         //----------------------------------------
         // read options
@@ -83,7 +84,7 @@
                     showUi = true;
                     break;
                 case "--get":
-                    mode = VOLUME_CONTROL_MODE_GET;
+                    doGet = true;
                     log(LOG_V, "will get volume");
                     break;
                 case "--stream":
@@ -150,15 +151,16 @@
         // Non-interactive test
         final int flag = showUi? AudioManager.FLAG_SHOW_UI : 0;
         final String pack = cmd.getClass().getPackage().getName();
-        if (mode == VOLUME_CONTROL_MODE_GET) {
-            log(LOG_V, "volume is " + audioService.getStreamVolume(stream) +
-                       " in range [" + audioService.getStreamMinVolume(stream) +
-                       ".." + audioService.getStreamMaxVolume(stream) + "]");
-        } else if (mode == VOLUME_CONTROL_MODE_SET) {
+        if (mode == VOLUME_CONTROL_MODE_SET) {
             audioService.setStreamVolume(stream, volIndex, flag, pack/*callingPackage*/);
         } else if (mode == VOLUME_CONTROL_MODE_ADJUST) {
             audioService.adjustStreamVolume(stream, adjDir, flag, pack);
         }
+        if (doGet) {
+            log(LOG_V, "volume is " + audioService.getStreamVolume(stream) +
+                       " in range [" + audioService.getStreamMinVolume(stream) +
+                       ".." + audioService.getStreamMaxVolume(stream) + "]");
+        }
     }
 
     //--------------------------------------------
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 810d201..7015381 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -408,7 +408,7 @@
             if (file.isFile()) {
                 try {
                     ApkLite baseApk = PackageParser.parseApkLite(file, 0);
-                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null);
+                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null);
                     params.sessionParams.setSize(
                             PackageHelper.calculateInstalledSize(pkgLite, false,
                             params.sessionParams.abiOverride));
@@ -1556,7 +1556,7 @@
         System.err.println("       pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]");
         System.err.println("       pm install-commit SESSION_ID");
         System.err.println("       pm install-abandon SESSION_ID");
-        System.err.println("       pm uninstall [-k] [--user USER_ID] PACKAGE");
+        System.err.println("       pm uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE] PACKAGE");
         System.err.println("       pm set-installer PACKAGE INSTALLER");
         System.err.println("       pm move-package PACKAGE [internal|UUID]");
         System.err.println("       pm move-primary-storage [internal|UUID]");
diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
index 8e9791f..02a92b9 100644
--- a/cmds/svc/src/com/android/commands/svc/NfcCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
@@ -58,7 +58,8 @@
                 IPackageManager pm = IPackageManager.Stub.asInterface(
                         ServiceManager.getService("package"));
                 try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0)) {
+                    if (pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0) ||
+			pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)) {
                         INfcAdapter nfc = INfcAdapter.Stub
                                 .asInterface(ServiceManager.getService(Context.NFC_SERVICE));
                         try {
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index af2e25a..f932388 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -29,13 +29,14 @@
 LOCAL_SRC_FILES := $(uiautomator.core_src_files)
 LOCAL_MODULE := uiautomator.core
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 ###############################################
 # Generate the stub source files
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(uiautomator.core_src_files)
-LOCAL_JAVA_LIBRARIES := $(uiautomator.core_java_libraries)
+LOCAL_JAVA_LIBRARIES := $(uiautomator.core_java_libraries) legacy-android-test
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/core-src \
 	$(LOCAL_PATH)/testrunner-src
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
index 751bbe8..ef6d55f 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiObject.java
@@ -808,7 +808,7 @@
      *
      * @return Rect
      * @throws UiObjectNotFoundException
-     * @see {@link #getBounds()}
+     * @see #getBounds()
      * @since API Level 17
      */
     public Rect getVisibleBounds() throws UiObjectNotFoundException {
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
index d98b4ff..71561c3 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
@@ -40,7 +40,7 @@
      * actions such as dialing 911 or posting messages to public forums, etc.
      *
      * @param isSet True to set as monkey test. False to set as regular functional test (default).
-     * @see {@link ActivityManager#isUserAMonkey()}
+     * @see ActivityManager#isUserAMonkey()
      */
     public void setRunAsMonkey(boolean isSet) {
         IActivityManager am = ActivityManager.getService();
diff --git a/compiled-classes-phone b/compiled-classes-phone
index ebc54f2..ed0a4a6 100644
--- a/compiled-classes-phone
+++ b/compiled-classes-phone
@@ -1195,13 +1195,13 @@
 android.graphics.DiscretePathEffect
 android.graphics.DrawFilter
 android.graphics.EmbossMaskFilter
+android.graphics.FontConfig
+android.graphics.FontConfig$Alias
+android.graphics.FontConfig$Axis
+android.graphics.FontConfig$Family
+android.graphics.FontConfig$Font
 android.graphics.FontFamily
 android.graphics.FontListParser
-android.graphics.FontListParser$Alias
-android.graphics.FontListParser$Axis
-android.graphics.FontListParser$Config
-android.graphics.FontListParser$Family
-android.graphics.FontListParser$Font
 android.graphics.ImageFormat
 android.graphics.Insets
 android.graphics.Interpolator
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 07a8253..b76aeb7 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -414,9 +414,9 @@
     public int flags;
 
     /**
-     * The unique string Id to identify the accessibility service.
+     * The component name the accessibility service.
      */
-    private String mId;
+    private ComponentName mComponentName;
 
     /**
      * The Service that implements this accessibility service component.
@@ -464,7 +464,7 @@
     public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
             throws XmlPullParserException, IOException {
         ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-        mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
+        mComponentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
         mResolveInfo = resolveInfo;
 
         XmlResourceParser parser = null;
@@ -574,7 +574,14 @@
      * @hide
      */
     public void setComponentName(ComponentName component) {
-        mId = component.flattenToShortString();
+        mComponentName = component;
+    }
+
+    /**
+     * @hide
+     */
+    public ComponentName getComponentName() {
+        return mComponentName;
     }
 
     /**
@@ -585,7 +592,7 @@
      * @return The id.
      */
     public String getId() {
-        return mId;
+        return mComponentName.flattenToShortString();
     }
 
     /**
@@ -715,7 +722,7 @@
         parcel.writeInt(feedbackType);
         parcel.writeLong(notificationTimeout);
         parcel.writeInt(flags);
-        parcel.writeString(mId);
+        parcel.writeParcelable(mComponentName, flagz);
         parcel.writeParcelable(mResolveInfo, 0);
         parcel.writeString(mSettingsActivityName);
         parcel.writeInt(mCapabilities);
@@ -729,7 +736,7 @@
         feedbackType = parcel.readInt();
         notificationTimeout = parcel.readLong();
         flags = parcel.readInt();
-        mId = parcel.readString();
+        mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
         mResolveInfo = parcel.readParcelable(null);
         mSettingsActivityName = parcel.readString();
         mCapabilities = parcel.readInt();
@@ -739,7 +746,7 @@
 
     @Override
     public int hashCode() {
-        return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
+        return 31 * 1 + ((mComponentName == null) ? 0 : mComponentName.hashCode());
     }
 
     @Override
@@ -754,11 +761,11 @@
             return false;
         }
         AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj;
-        if (mId == null) {
-            if (other.mId != null) {
+        if (mComponentName == null) {
+            if (other.mComponentName != null) {
                 return false;
             }
-        } else if (!mId.equals(other.mId)) {
+        } else if (!mComponentName.equals(other.mComponentName)) {
             return false;
         }
         return true;
@@ -777,7 +784,7 @@
         stringBuilder.append(", ");
         appendFlags(stringBuilder, flags);
         stringBuilder.append(", ");
-        stringBuilder.append("id: ").append(mId);
+        stringBuilder.append("id: ").append(getId());
         stringBuilder.append(", ");
         stringBuilder.append("resolveInfo: ").append(mResolveInfo);
         stringBuilder.append(", ");
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index c9e09e4..87e512c 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -707,7 +707,7 @@
      * @param account the account to clone, will never be null
      * @return a Bundle result or null if the result is to be returned via the response.
      * @throws NetworkErrorException
-     * @see {@link #addAccountFromCredentials(AccountAuthenticatorResponse, Account, Bundle)}
+     * @see #addAccountFromCredentials(AccountAuthenticatorResponse, Account, Bundle)
      */
     public Bundle getAccountCredentialsForCloning(final AccountAuthenticatorResponse response,
             final Account account) throws NetworkErrorException {
@@ -732,7 +732,7 @@
      * provided by {@link #getAccountCredentialsForCloning(AccountAuthenticatorResponse, Account)}.
      * @return a Bundle result or null if the result is to be returned via the response.
      * @throws NetworkErrorException
-     * @see {@link #getAccountCredentialsForCloning(AccountAuthenticatorResponse, Account)}
+     * @see #getAccountCredentialsForCloning(AccountAuthenticatorResponse, Account)
      */
     public Bundle addAccountFromCredentials(final AccountAuthenticatorResponse response,
             Account account,
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 8185818..def0ff9 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -53,9 +53,12 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.SuppressWarnings;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
@@ -286,46 +289,31 @@
 
     /**
      * Account visibility was not set.
-     * @hide
      */
     public static final int VISIBILITY_UNDEFINED = 0;
 
     /**
      * Account is always visible to given application and only authenticator can revoke visibility.
-     * @hide
      */
     public static final int VISIBILITY_VISIBLE = 1;
 
     /**
      * Account is visible to given application, but user can revoke visibility.
-     * @hide
      */
     public static final int VISIBILITY_USER_MANAGED_VISIBLE = 2;
 
     /**
      * Account is not visible to given application and only authenticator can grant visibility.
-     * @hide
      */
     public static final int VISIBILITY_NOT_VISIBLE = 3;
 
     /**
      * Account is not visible to given application, but user can reveal it, for example, using
      * {@link #newChooseAccountIntent(Account, List, String[], String, String, String[], Bundle)}
-     * @hide
      */
     public static final int VISIBILITY_USER_MANAGED_NOT_VISIBLE = 4;
 
     /**
-     * Key to manifest entry with a list of account types in which application is interested.
-     * Example value: "com.google;com.customtype". If it is  specified then the application
-     * will only get notifications related to the types in the list (see
-     * {@link #ACTION_VISIBLE_ACCOUNTS_CHANGED}). Authenticators managing whitelisted types will be
-     * able to know about the application using {@link #ACTION_ACCOUNTS_LISTENER_PACKAGE_INSTALLED}
-     * @hide
-     */
-    public static final String SUPPORTED_ACCOUNT_TYPES = "android.accounts.SupportedAccountTypes";
-
-    /**
      * Token type for the special case where a UID has access only to an account
      * but no authenticator specific auth token types.
      *
@@ -344,48 +332,27 @@
      *
      * @see #addOnAccountsUpdatedListener
      *
-     * Deprecated - use ACTION_VISIBLE_ACCOUNTS_CHANGED instead.
+     * @deprecated use #addOnAccountsUpdatedListener to get account updates in runtime.
      */
     public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
         "android.accounts.LOGIN_ACCOUNTS_CHANGED";
 
     /**
-     * Action sent as a broadcast Intent by the AccountsService when accounts potentially visible to
-     * the applications are added, accounts are removed, or an account's credentials (saved
-     * password, etc) are changed. List of supported account types shoud be specified in the
-     * Manifest file using {@link #SUPPORTED_ACCOUNT_TYPES}
-     *
-     * @see #addOnAccountsUpdatedListener
-     * @hide
-     */
-    public static final String ACTION_VISIBLE_ACCOUNTS_CHANGED =
-            "android.accounts.action.VISIBLE_ACCOUNTS_CHANGED";
-
-    /**
-     * Authenticators may subscribe to get notifications about apps interested in their managed account
-     * types using {@link #SUPPORTED_ACCOUNT_TYPES}.
-     * @hide
-     */
-    public static final String ACTION_ACCOUNTS_LISTENER_PACKAGE_INSTALLED =
-            "android.accounts.action.ACCOUNTS_LISTENER_PACKAGE_INSTALLED";
-
-    /**
      * Uid key to set default visibility for applications targeting API level
-     * {@link android.os.Build.VERSION_CODES#O} or above. See {@link #getAccountVisibility}. If the
-     * value was not set by authenticator USER_MANAGED_NOT_VISIBLE is used.
-     * @hide
+     * {@link android.os.Build.VERSION_CODES#O} or above and don't have the same signature as
+     * authenticator See {@link #getAccountVisibility}. If the value was not set by authenticator
+     * USER_MANAGED_NOT_VISIBLE is used.
      */
-    public static final int DEFAULT_VISIBILITY = -2;
+    public static final int UID_KEY_DEFAULT_VISIBILITY = -2;
 
     /**
      * Uid key to set visibility for applications targeting API level below
-     * {@link android.os.Build.VERSION_CODES#O}, which were able to see the account before. It
-     * includes applications with GET_ACCOUNTS permission or with the same signature as
-     * authenticator. See {@link #getAccountVisibility}. If the value was not set by authenticator
-     * USER_MANAGED_VISIBLE is used.
-     * @hide
+     * {@link android.os.Build.VERSION_CODES#O} with GET_ACCOUNS permission, or applications with
+     * any targeting API level with the same signature as authenticator. See
+     * {@link #getAccountVisibility}. If the value was not set by authenticator USER_MANAGED_VISIBLE
+     * is used.
      */
-    public static final int DEFAULT_LEGACY_VISIBILITY = -3;
+    public static final int UID_KEY_DEFAULT_LEGACY_VISIBILITY = -3;
 
     /**
      * @hide
@@ -874,43 +841,11 @@
      * @param account The {@link Account} to add
      * @param password The password to associate with the account, null for none
      * @param extras String values to use for the account's userdata, null for none
-     * @param selectedUids Array of uids whose associated applications can access this account
-     *        without any additional user approval.
-     *
-     * @return True if the account was successfully added, false if the account already exists, the
-     *         account is null, or another error occurs.
-     */
-    public boolean addAccountExplicitly(Account account, String password, Bundle extras,
-            int[] selectedUids) {
-        return false; // TODO remove this method.
-    }
-
-    /**
-     * Adds an account directly to the AccountManager. Additionally this makes the Account visible
-     * to desired UIDs of applications on the device, and sends directed broadcasts to these
-     * individual applications.
-     * <p>
-     * Normally used by sign-up wizards associated with authenticators, not directly by
-     * applications.
-     * <p>
-     * Calling this method does not update the last authenticated timestamp, referred by
-     * {@link #KEY_LAST_AUTHENTICATED_TIME}. To update it, call
-     * {@link #notifyAccountAuthenticated(Account)} after getting success.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * This method requires the caller to have a signature match with the authenticator that owns
-     * the specified account.
-     *
-     * @param account The {@link Account} to add
-     * @param password The password to associate with the account, null for none
-     * @param extras String values to use for the account's userdata, null for none
      * @param visibility Map from uid to visibility values which will be set before account is
      *        added. See getAccountVisibility for possilbe values.
      *
      * @return True if the account was successfully added, false if the account already exists, the
      *         account is null, or another error occurs.
-     * @hide
      */
     public boolean addAccountExplicitly(Account account, String password, Bundle extras,
             Map<Integer, Integer> visibility) {
@@ -925,19 +860,21 @@
     }
 
     /**
-     * Returns all UIDs for applications that requested the account type.
-     * <p>This method requires the caller to have a signature match with the authenticator
-     * that owns the specified account.
+     * Returns UIDs of applications for which visibility of given account was explicitly set.
+     * <p>
+     * This method requires the caller to have a signature match with the authenticator that owns
+     * the specified account.
      *
-     * @param accountType The account type to be authenticated.
+     * @param account The account for which visibility data should be returned.
      *
-     * @return array of all UIDs that support accounts of this
-     * account type that seek approval (to be used to know which accounts for
-     * the authenticator to include in addAccountExplicitly). Null if none.
+     * @return Map from uid to visibility for given account.
      */
-    public int[] getRequestingUidsForType(String accountType) {
+    public Map<Integer, Integer> getUidsAndVisibilityForAccount(Account account) {
         try {
-            return mService.getRequestingUidsForType(accountType);
+            @SuppressWarnings("unchecked")
+            Map<Integer, Integer> result = (Map<Integer, Integer>) mService
+                    .getUidsAndVisibilityForAccount(account);
+            return result;
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -954,9 +891,8 @@
      * @param packageName Package name.
      * @param accountType Account type.
      *
-     * @return Map with visibility for all accounts of given type. See {@link #getAccountVisibility}
-     *         for possilbe values.
-     * @hide
+     * @return Map with visibility for all accounts of given type.
+     * See {@link #getAccountVisibility} for possilbe values.
      */
     public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
             String accountType) {
@@ -971,67 +907,6 @@
     }
 
     /**
-     * Gives a certain UID, represented a application, access to an account
-     * <p>
-     * This method requires the caller to have a signature match with the authenticator that owns
-     * the specified account.
-     *
-     * @param account Account to make visible.
-     * @param uid The UID of the application to add account access.
-     *
-     * @return True if account made visible to application and was not previously visible.
-     */
-    public boolean makeAccountVisible(Account account, int uid) {
-        try {
-            return mService.setAccountVisibility(account, uid, VISIBILITY_USER_MANAGED_VISIBLE);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Removes visibility of certain account of a process identified by a given UID to an
-     * application. This is called by the Authenticator.
-     * <p>
-     * This method requires the caller to have a signature match with the authenticator that owns
-     * the specified account.
-     *
-     * @param account Remove visibility of this account..
-     * @param uid The UID of the application to remove account access.
-     *
-     * @return True if application access to account removed and was previously visible.
-     */
-    public boolean removeAccountVisibility(Account account, int uid) {
-        try {
-            return mService.setAccountVisibility(account, uid, VISIBILITY_USER_MANAGED_NOT_VISIBLE);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Checks visibility of certain account of a process identified by a given UID. This is called
-     * by the Authenticator.
-     * <p>
-     * This method requires the caller to have a signature match with the authenticator that owns
-     * the specified account.
-     *
-     * @param account Account to check visibility.
-     * @param uid The UID of the application to check account access.
-     *
-     * @return True if application has access to the account
-     */
-    public boolean isAccountVisible(Account account, int uid) {
-        try {
-            Integer visibility = mService.getAccountVisibility(account, uid);
-            return visibility == VISIBILITY_USER_MANAGED_NOT_VISIBLE
-                    || visibility == VISIBILITY_VISIBLE;
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Set visibility value of given account to certain UID.
      * <p>
      * See {@link #getAccountVisibility} for possible values.
@@ -1044,7 +919,6 @@
      * @param visibility - new visibility value.
      *
      * @return True if visibility value was succesfully updated.
-     * @hide
      */
     public boolean setAccountVisibility(Account account, int uid,
             @AccountVisibility int visibility) {
@@ -1058,6 +932,7 @@
     /**
      * Gets visibility of certain account for given UID. Possible returned values are:
      * <ul>
+     * <li>{@link #VISIBILITY_UNDEFINED}</li>
      * <li>{@link #VISIBILITY_VISIBLE}</li>
      * <li>{@link #VISIBILITY_USER_MANAGED_VISIBLE}</li>
      * <li>{@link #VISIBILITY_NOT_VISIBLE}
@@ -1072,7 +947,6 @@
      * @param uid The UID of the application to get account visibility.
      *
      * @return int Visibility for given account and uid.
-     * @hide
      */
     public @AccountVisibility int getAccountVisibility(Account account, int uid) {
         try {
@@ -2245,10 +2119,23 @@
                 synchronized (mAccountsUpdatedListeners) {
                     try {
                         if (mAccountsUpdatedListeners.containsKey(listener)) {
-                            listener.onAccountsUpdated(accountsCopy);
+                            Set<String> types = mAccountsUpdatedListenersTypes.get(listener);
+                            if (types != null) {
+                                // filter by account type;
+                                ArrayList<Account> filtered = new ArrayList<>();
+                                for (Account account : accountsCopy) {
+                                    if (types.contains(account.type)) {
+                                        filtered.add(account);
+                                    }
+                                }
+                                listener.onAccountsUpdated(
+                                        filtered.toArray(new Account[filtered.size()]));
+                            } else {
+                                listener.onAccountsUpdated(accountsCopy);
+                            }
                         }
                     } catch (SQLException e) {
-                        // Better luck next time.  If the problem was disk-full,
+                        // Better luck next time. If the problem was disk-full,
                         // the STORAGE_OK intent will re-trigger the update.
                         Log.e(TAG, "Can't update accounts", e);
                     }
@@ -2894,6 +2781,9 @@
     private final HashMap<OnAccountsUpdateListener, Handler> mAccountsUpdatedListeners =
             Maps.newHashMap();
 
+    private final HashMap<OnAccountsUpdateListener, Set<String> > mAccountsUpdatedListenersTypes =
+            Maps.newHashMap();
+
     /**
      * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
      * so that it can read the updated list of accounts and send them to the listener
@@ -2916,20 +2806,29 @@
     /**
      * Adds an {@link OnAccountsUpdateListener} to this instance of the {@link AccountManager}. This
      * listener will be notified whenever user or AbstractAcccountAuthenticator made changes to
-     * accounts related to the caller - either list of accounts returned by {@link #getAccounts()}
-     * was changed, or new account was added for which user can grant access to the caller.
+     * accounts of any type related to the caller. This method is equivalent to
+     * addOnAccountsUpdatedListener(listener, handler, updateImmediately, null)
      *
+     * @see #addOnAccountsUpdatedListener(OnAccountsUpdateListener, Handler, boolean,
+     *      String[])
+     */
+    public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
+            Handler handler, boolean updateImmediately) {
+        addOnAccountsUpdatedListener(listener, handler,updateImmediately, null);
+    }
+
+    /**
+     * Adds an {@link OnAccountsUpdateListener} to this instance of the {@link AccountManager}. This
+     * listener will be notified whenever user or AbstractAcccountAuthenticator made changes to
+     * accounts of given types related to the caller -
+     * either list of accounts returned by {@link #getAccounts()}
+     * was changed, or new account was added for which user can grant access to the caller.
      * <p>
      * As long as this listener is present, the AccountManager instance will not be
      * garbage-collected, and neither will the {@link Context} used to retrieve it, which may be a
      * large Activity instance. To avoid memory leaks, you must remove this listener before then.
      * Normally listeners are added in an Activity or Service's {@link Activity#onCreate} and
      * removed in {@link Activity#onDestroy}.
-     *
-     *
-     * If SUPPORTED_ACCOUNT_TYPES is specified in the manifest file, listener will only be
-     * notified about whitelisted types.
-     *
      * <p>
      * It is safe to call this method from the main thread.
      *
@@ -2938,11 +2837,12 @@
      *        main thread
      * @param updateImmediately If true, the listener will be invoked (on the handler thread) right
      *        away with the current account list
+     * @param accountTypes If set, only changes to accounts of given types will be reported.
      * @throws IllegalArgumentException if listener is null
      * @throws IllegalStateException if listener was already added
      */
     public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
-            Handler handler, boolean updateImmediately) {
+            Handler handler, boolean updateImmediately, String[] accountTypes) {
         if (listener == null) {
             throw new IllegalArgumentException("the listener is null");
         }
@@ -2953,16 +2853,19 @@
             final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
 
             mAccountsUpdatedListeners.put(listener, handler);
-
+            if (accountTypes != null) {
+                mAccountsUpdatedListenersTypes.put(listener,
+                        new HashSet<String>(Arrays.asList(accountTypes)));
+            }
 
             if (wasEmpty) {
                 // Register a broadcast receiver to monitor account changes
                 IntentFilter intentFilter = new IntentFilter();
-                if (isVisibleAccountsChangedBroadcastSupported()) {
-                    intentFilter.addAction(ACTION_VISIBLE_ACCOUNTS_CHANGED);
-                } else {
-                    intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
-                }
+                // TODO get rid of the broadcast receiver
+                // create android.os.ResultReceiver
+                // send it to the service via aidl
+                // handle onReceiveResult
+                intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
                 // To recover from disk-full.
                 intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
                 // Register a broadcast receiver to monitor account changes
@@ -2975,26 +2878,6 @@
     }
 
     /**
-     * @hide
-     */
-    private boolean isVisibleAccountsChangedBroadcastSupported() {
-        String interestedTypes = null;
-        try {
-            String packageName = mContext.getOpPackageName();
-            ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_META_DATA);
-            Bundle b = ai.metaData;
-            if (b == null) {
-                return false;
-            }
-            interestedTypes = b.getString(SUPPORTED_ACCOUNT_TYPES);
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
-        return !TextUtils.isEmpty(interestedTypes);
-    }
-
-    /**
      * Removes an {@link OnAccountsUpdateListener} previously registered with
      * {@link #addOnAccountsUpdatedListener}.  The listener will no longer
      * receive notifications of account changes.
@@ -3015,6 +2898,7 @@
                 return;
             }
             mAccountsUpdatedListeners.remove(listener);
+            mAccountsUpdatedListenersTypes.remove(listener);
             if (mAccountsUpdatedListeners.isEmpty()) {
                 mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
             }
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
index 242b3ea..16a45ba 100644
--- a/core/java/android/accounts/ChooseAccountActivity.java
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -52,7 +52,9 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
+        // TODO This activity is only used by getAuthTokenByFeatures and can not see
+        // VISIBILITY_USER_MANAGED_NOT_VISIBLE accounts. It should be moved to account managed
+        // service.
         mAccounts = getIntent().getParcelableArrayExtra(AccountManager.KEY_ACCOUNTS);
         mAccountManagerResponse =
                 getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE);
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 8c71f50..95fdfef 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -40,7 +40,9 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -110,7 +112,7 @@
     private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = "existingAccounts";
     private static final String KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME = "selectedAccountName";
     private static final String KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT = "selectedAddAccount";
-    private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = "accountList";
+    private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = "accountAndVisibilityList";
 
     private static final int SELECTED_ITEM_NONE = -1;
 
@@ -120,7 +122,11 @@
     private boolean mSelectedAddNewAccount = false;
     private String mDescriptionOverride;
 
-    private ArrayList<Account> mAccounts;
+    private Map<Account, Integer> mAccounts;
+    // TODO Redesign flow to show NOT_VISIBLE accounts
+    // and display a warning if they are selected.
+    // Currently NOT_VISBILE accounts are not shown at all.
+    private ArrayList<Account> mPossiblyVisibleAccounts;
     private int mPendingRequest = REQUEST_NULL;
     private Parcelable[] mExistingAccounts = null;
     private int mSelectedItemIndex;
@@ -164,12 +170,12 @@
                     savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS);
 
             // Makes sure that any user selection is preserved across orientation changes.
-            mSelectedAccountName = savedInstanceState.getString(
-                    KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
-
-            mSelectedAddNewAccount = savedInstanceState.getBoolean(
-                    KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
-            mAccounts = savedInstanceState.getParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST);
+            mSelectedAccountName =
+                    savedInstanceState.getString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
+            mSelectedAddNewAccount =
+                    savedInstanceState.getBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
+            mAccounts = (Map<Account, Integer>) savedInstanceState
+                    .getSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST);
         } else {
             mPendingRequest = REQUEST_NULL;
             mExistingAccounts = null;
@@ -220,9 +226,15 @@
             }
         }
 
-        String[] listItems = getListOfDisplayableOptions(mAccounts);
-        mSelectedItemIndex = getItemIndexToSelect(
-            mAccounts, mSelectedAccountName, mSelectedAddNewAccount);
+        mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size());
+        for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) {
+            if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) {
+                mPossiblyVisibleAccounts.add(entry.getKey());
+            }
+        }
+        String[] listItems = getListOfDisplayableOptions(mPossiblyVisibleAccounts);
+        mSelectedItemIndex = getItemIndexToSelect(mPossiblyVisibleAccounts, mSelectedAccountName,
+                mSelectedAddNewAccount);
 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.choose_type_and_account);
@@ -250,15 +262,18 @@
             outState.putParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS, mExistingAccounts);
         }
         if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
-            if (mSelectedItemIndex == mAccounts.size()) {
+            if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) {
                 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, true);
             } else {
                 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
                 outState.putString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME,
-                        mAccounts.get(mSelectedItemIndex).name);
+                        mPossiblyVisibleAccounts.get(mSelectedItemIndex).name);
             }
         }
-        outState.putParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST, mAccounts);
+        // should be HashMap by default.
+        HashMap<Account, Integer> accountsHashMap = (mAccounts instanceof HashMap)
+                ? (HashMap) mAccounts : new HashMap<Account, Integer>(mAccounts);
+        outState.putSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST, accountsHashMap);
     }
 
     public void onCancelButtonClicked(View view) {
@@ -266,11 +281,11 @@
     }
 
     public void onOkButtonClicked(View view) {
-        if (mSelectedItemIndex == mAccounts.size()) {
+        if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) {
             // Selected "Add New Account" option
             startChooseAccountTypeActivity();
         } else if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
-            onAccountSelected(mAccounts.get(mSelectedItemIndex));
+            onAccountSelected(mPossiblyVisibleAccounts.get(mSelectedItemIndex));
         }
     }
 
@@ -321,6 +336,7 @@
                 }
 
                 if (accountName == null || accountType == null) {
+                    // new account was added.
                     Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage(
                             mCallingPackage, mCallingUid);
                     Set<Account> preExistingAccounts = new HashSet<Account>();
@@ -328,6 +344,7 @@
                         preExistingAccounts.add((Account) accountParcel);
                     }
                     for (Account account : currentAccounts) {
+                        // New account is visible to the app - return it.
                         if (!preExistingAccounts.contains(account)) {
                             accountName = account.name;
                             accountType = account.type;
@@ -409,13 +426,27 @@
     }
 
     private void setResultAndFinish(final String accountName, final String accountType) {
+        // Mark account as visible since user chose it.
+        Account account = new Account(accountName, accountType);
+        Integer oldVisibility = mAccounts.get(account);
+        // oldVisibility is null if new account was added
+        if (oldVisibility == null) {
+            Map<Account, Integer> accountsAndVisibility = AccountManager.get(this)
+                    .getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */);
+            oldVisibility = accountsAndVisibility.get(account);
+        }
+        if (oldVisibility != null
+                && oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) {
+            AccountManager.get(this).setAccountVisibility(account, mCallingUid,
+                    AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
+        }
         Bundle bundle = new Bundle();
         bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
         bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
         setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: "
-                    + "selected account " + accountName + ", " + accountType);
+            Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: selected account "
+                    + accountName + ", " + accountType);
         }
         finish();
     }
@@ -474,25 +505,28 @@
     }
 
     /**
-     * Create a list of Account objects for each account that is acceptable. Filter out
-     * accounts that don't match the allowable types, if provided, or that don't match the
-     * allowable accounts, if provided.
+     * Create a list of Account objects for each account that is acceptable. Filter out accounts
+     * that don't match the allowable types, if provided, or that don't match the allowable
+     * accounts, if provided.
      */
-    private ArrayList<Account> getAcceptableAccountChoices(AccountManager accountManager) {
-      final Account[] accounts = accountManager.getAccountsForPackage(mCallingPackage,
-              mCallingUid);
-      ArrayList<Account> accountsToPopulate = new ArrayList<Account>(accounts.length);
-      for (Account account : accounts) {
-          if (mSetOfAllowableAccounts != null && !mSetOfAllowableAccounts.contains(account)) {
-              continue;
-          }
-          if (mSetOfRelevantAccountTypes != null
-                  && !mSetOfRelevantAccountTypes.contains(account.type)) {
-              continue;
-          }
-          accountsToPopulate.add(account);
-      }
-      return accountsToPopulate;
+    private Map<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) {
+        Map<Account, Integer> accountsAndVisibility =
+                accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */);
+
+        Map<Account, Integer> accountsToPopulate =
+                new HashMap<Account, Integer>(accountsAndVisibility.size());
+        for (Map.Entry<Account, Integer> entry : accountsAndVisibility.entrySet()) {
+            if (mSetOfAllowableAccounts != null
+                    && !mSetOfAllowableAccounts.contains(entry.getKey())) {
+                continue;
+            }
+            if (mSetOfRelevantAccountTypes != null
+                    && !mSetOfRelevantAccountTypes.contains(entry.getKey().type)) {
+                continue;
+            }
+            accountsToPopulate.put(entry.getKey(), entry.getValue());
+        }
+        return accountsToPopulate;
     }
 
     /**
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 66c3ca3..63a0919 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -108,8 +108,8 @@
     void isCredentialsUpdateSuggested(in IAccountManagerResponse response, in Account account,
         String statusToken);
 
-    /* Allows Authenticator to get UIDs of packages which registered to receive updates about given account type.*/
-    int[] getRequestingUidsForType(String accountType);
+    /* Returns Map<Integer, Integer> from UID to visibility with all values stored for given account*/
+    Map getUidsAndVisibilityForAccount(in Account account);
 
     boolean addAccountExplicitlyWithVisibility(in Account account, String password, in Bundle extras,
             in Map visibility);
@@ -117,7 +117,7 @@
     boolean setAccountVisibility(in Account a, int uid, int newVisibility);
     int getAccountVisibility(in Account a, int uid);
 
-    /* Type may be null  returns Map <Account, Integer>*/
+    /* Type may be null returns Map <Account, Integer>*/
     Map getAccountsAndVisibilityForPackage(in String packageName, in String accountType);
 
     /* Check if the package in a user can access an account */
diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java
index 95262ab..e2e5a8f 100644
--- a/core/java/android/animation/AnimationHandler.java
+++ b/core/java/android/animation/AnimationHandler.java
@@ -276,8 +276,9 @@
          * Run animation based on the frame time.
          * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
          *                  base.
+         * @return if the animation has finished.
          */
-        void doAnimationFrame(long frameTime);
+        boolean doAnimationFrame(long frameTime);
 
         /**
          * This notifies the callback of frame commit time. Frame commit time is the time after
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index c51725a..634dc1fd 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -26,7 +26,7 @@
  * This is the superclass for classes which provide basic support for animations which can be
  * started, ended, and have <code>AnimatorListeners</code> added to them.
  */
-public abstract class Animator implements Cloneable {
+public abstract class Animator implements Cloneable, AnimationHandler.AnimationFrameCallback {
 
     /**
      * The value used to indicate infinite duration (e.g. when Animators repeat infinitely).
@@ -465,11 +465,102 @@
     }
 
     /**
+     * @hide
+     */
+    @Override
+    public boolean doAnimationFrame(long frameTime) {
+        // TODO: Need to find a better signal than this
+        return getDuration() + getStartDelay() >= frameTime;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void commitAnimationFrame(long frameTime) {}
+
+
+    /**
+     * Internal use only.
+     * This call starts the animation in regular or reverse direction without requiring them to
+     * register frame callbacks. The caller will be responsible for all the subsequent animation
+     * pulses. Specifically, the caller needs to call doAnimationFrame(...) for the animation on
+     * every frame.
+     *
+     * @param inReverse whether the animation should play in reverse direction
+     */
+    void startWithoutPulsing(boolean inReverse) {
+        if (inReverse) {
+            reverse();
+        } else {
+            start();
+        }
+    }
+
+    /**
+     * Internal use only.
+     * Skips the animation value to end/start, depending on whether the play direction is forward
+     * or backward.
+     *
+     * @param inReverse whether the end value is based on a reverse direction. If yes, this is
+     *                  equivalent to skip to start value in a forward playing direction.
+     */
+    void skipToEndValue(boolean inReverse) {}
+
+
+    /**
+     * Internal use only.
+     *
+     * Returns whether the animation has start/end values setup. For most of the animations, this
+     * should always be true. For ObjectAnimators, the start values are setup in the initialization
+     * of the animation.
+     */
+    boolean isInitialized() {
+        return true;
+    }
+
+    /**
+     * Internal use only.
+     */
+    void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {}
+
+    /**
      * <p>An animation listener receives notifications from an animation.
      * Notifications indicate animation related events, such as the end or the
      * repetition of the animation.</p>
      */
     public static interface AnimatorListener {
+
+        /**
+         * <p>Notifies the start of the animation as well as the animation's overall play direction.
+         * This method's default behavior is to call {@link #onAnimationStart(Animator)}. This
+         * method can be overridden, though not required, to get the additional play direction info
+         * when an animation starts. Skipping calling super when overriding this method results in
+         * {@link #onAnimationStart(Animator)} not getting called.
+         *
+         * @param animation The started animation.
+         * @param isReverse Whether the animation is playing in reverse.
+         */
+        default void onAnimationStart(Animator animation, boolean isReverse) {
+            onAnimationStart(animation);
+        }
+
+        /**
+         * <p>Notifies the end of the animation. This callback is not invoked
+         * for animations with repeat count set to INFINITE.</p>
+         *
+         * <p>This method's default behavior is to call {@link #onAnimationEnd(Animator)}. This
+         * method can be overridden, though not required, to get the additional play direction info
+         * when an animation ends. Skipping calling super when overriding this method results in
+         * {@link #onAnimationEnd(Animator)} not getting called.
+         *
+         * @param animation The animation which reached its end.
+         * @param isReverse Whether the animation is playing in reverse.
+         */
+        default void onAnimationEnd(Animator animation, boolean isReverse) {
+            onAnimationEnd(animation);
+        }
+
         /**
          * <p>Notifies the start of the animation.</p>
          *
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index a904f9d..bca736a25 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -19,11 +19,15 @@
 import android.app.ActivityThread;
 import android.app.Application;
 import android.os.Build;
+import android.os.Looper;
+import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.view.animation.Animation;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.List;
 
 /**
@@ -52,7 +56,7 @@
  * Animation</a> developer guide.</p>
  * </div>
  */
-public final class AnimatorSet extends Animator {
+public final class AnimatorSet extends Animator implements AnimationHandler.AnimationFrameCallback {
 
     private static final String TAG = "AnimatorSet";
     /**
@@ -66,7 +70,7 @@
      * Tracks animations currently being played, so that we know what to
      * cancel or end when cancel() or end() is called on this AnimatorSet
      */
-    private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
+    private ArrayList<Node> mPlayingSet = new ArrayList<Node>();
 
     /**
      * Contains all nodes, mapped to their respective Animators. When new
@@ -77,6 +81,11 @@
     private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>();
 
     /**
+     * Contains the start and end events of all the nodes. All these events are sorted in this list.
+     */
+    private ArrayList<AnimationEvent> mEvents = new ArrayList<>();
+
+    /**
      * Set of all nodes created for this AnimatorSet. This list is used upon
      * starting the set, and the nodes are placed in sorted order into the
      * sortedNodes collection.
@@ -84,21 +93,6 @@
     private ArrayList<Node> mNodes = new ArrayList<Node>();
 
     /**
-     * Animator Listener that tracks the lifecycle of each Animator in the set. It will be added
-     * to each Animator before they start and removed after they end.
-     */
-    private AnimatorSetListener mSetListener = new AnimatorSetListener(this);
-
-    /**
-     * Flag indicating that the AnimatorSet has been manually
-     * terminated (by calling cancel() or end()).
-     * This flag is used to avoid starting other animations when currently-playing
-     * child animations of this AnimatorSet end. It also determines whether cancel/end
-     * notifications are sent out via the normal AnimatorSetListener mechanism.
-     */
-    private boolean mTerminated = false;
-
-    /**
      * Tracks whether any change has been made to the AnimatorSet, which is then used to
      * determine whether the dependency graph should be re-constructed.
      */
@@ -131,8 +125,6 @@
     // was set on this AnimatorSet, so it should not be passed down to the children.
     private TimeInterpolator mInterpolator = null;
 
-    // Whether the AnimatorSet can be reversed.
-    private boolean mReversible = true;
     // The total duration of finishing all the Animators in the set.
     private long mTotalDuration = 0;
 
@@ -142,6 +134,46 @@
     // the animator set and immediately end it for N and forward.
     private final boolean mShouldIgnoreEndWithoutStart;
 
+    // In pre-O releases, calling start() doesn't reset all the animators values to start values.
+    // As a result, the start of the animation is inconsistent with what setCurrentPlayTime(0) would
+    // look like on O. Also it is inconsistent with what reverse() does on O, as reverse would
+    // advance all the animations to the right beginning values for before starting to reverse.
+    // From O and forward, we will add an additional step of resetting the animation values (unless
+    // the animation was previously seeked and therefore doesn't start from the beginning).
+    private final boolean mShouldResetValuesAtStart;
+
+    // The time, in milliseconds, when last frame of the animation came in. -1 when the animation is
+    // not running.
+    private long mLastFrameTime = -1;
+
+    // The time, in milliseconds, when the first frame of the animation came in.
+    // -1 when the animation is not running.
+    private long mFirstFrame = -1;
+
+    // The time, in milliseconds, when the first frame of the animation came in.
+    // -1 when the animation is not running.
+    private int mLastEventId = -1;
+
+    // Indicates whether the animation is reversing.
+    private boolean mReversing = false;
+
+    // Indicates whether the animation should register frame callbacks. If false, the animation will
+    // passively wait for an AnimatorSet to pulse it.
+    private boolean mSelfPulse = true;
+
+    // SeekState stores the last seeked play time as well as seek direction.
+    private SeekState mSeekState = new SeekState();
+
+    // Indicates where children animators are all initialized with their start values captured.
+    private boolean mChildrenInitialized = false;
+
+    /**
+     * Set on the next frame after pause() is called, used to calculate a new startTime
+     * or delayStartTime which allows the animator set to continue from the point at which
+     * it was paused. If negative, has not yet been set.
+     */
+    private long mPauseTime = -1;
+
     public AnimatorSet() {
         super();
         mNodeMap.put(mDelayAnim, mRootNode);
@@ -150,10 +182,19 @@
         Application app = ActivityThread.currentApplication();
         if (app == null || app.getApplicationInfo() == null) {
             mShouldIgnoreEndWithoutStart = true;
-        } else if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
-            mShouldIgnoreEndWithoutStart = true;
+            mShouldResetValuesAtStart = false;
         } else {
-            mShouldIgnoreEndWithoutStart = false;
+            if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+                mShouldIgnoreEndWithoutStart = true;
+            } else {
+                mShouldIgnoreEndWithoutStart = false;
+            }
+
+            if (app.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O) {
+                mShouldResetValuesAtStart = false;
+            } else {
+                mShouldResetValuesAtStart = true;
+            }
         }
     }
 
@@ -206,7 +247,6 @@
             if (items.length == 1) {
                 play(items[0]);
             } else {
-                mReversible = false;
                 for (int i = 0; i < items.length - 1; ++i) {
                     play(items[i]).before(items[i + 1]);
                 }
@@ -225,7 +265,6 @@
             if (items.size() == 1) {
                 play(items.get(0));
             } else {
-                mReversible = false;
                 for (int i = 0; i < items.size() - 1; ++i) {
                     play(items.get(i)).before(items.get(i + 1));
                 }
@@ -350,7 +389,9 @@
     @SuppressWarnings("unchecked")
     @Override
     public void cancel() {
-        mTerminated = true;
+        if (Looper.myLooper() == null) {
+            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+        }
         if (isStarted()) {
             ArrayList<AnimatorListener> tmpListeners = null;
             if (mListeners != null) {
@@ -360,18 +401,13 @@
                     tmpListeners.get(i).onAnimationCancel(this);
                 }
             }
-            ArrayList<Animator> playingSet = new ArrayList<>(mPlayingSet);
+            ArrayList<Node> playingSet = new ArrayList<>(mPlayingSet);
             int setSize = playingSet.size();
             for (int i = 0; i < setSize; i++) {
-                playingSet.get(i).cancel();
+                playingSet.get(i).mAnimation.cancel();
             }
-            if (tmpListeners != null) {
-                int size = tmpListeners.size();
-                for (int i = 0; i < size; i++) {
-                    tmpListeners.get(i).onAnimationEnd(this);
-                }
-            }
-            mStarted = false;
+            mPlayingSet.clear();
+            endAnimation();
         }
     }
 
@@ -383,50 +419,40 @@
      */
     @Override
     public void end() {
+        if (Looper.myLooper() == null) {
+            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+        }
         if (mShouldIgnoreEndWithoutStart && !isStarted()) {
             return;
         }
-        mTerminated = true;
         if (isStarted()) {
-            endRemainingAnimations();
-        }
-        if (mListeners != null) {
-            ArrayList<AnimatorListener> tmpListeners =
-                    (ArrayList<AnimatorListener>) mListeners.clone();
-            for (int i = 0; i < tmpListeners.size(); i++) {
-                tmpListeners.get(i).onAnimationEnd(this);
-            }
-        }
-        mStarted = false;
-    }
-
-    /**
-     * Iterate the animations that haven't finished or haven't started, and end them.
-     */
-    private void endRemainingAnimations() {
-        ArrayList<Animator> remainingList = new ArrayList<Animator>(mNodes.size());
-        remainingList.addAll(mPlayingSet);
-
-        int index = 0;
-        while (index < remainingList.size()) {
-            Animator anim = remainingList.get(index);
-            anim.end();
-            index++;
-            Node node = mNodeMap.get(anim);
-            if (node.mChildNodes != null) {
-                int childSize = node.mChildNodes.size();
-                for (int i = 0; i < childSize; i++) {
-                    Node child = node.mChildNodes.get(i);
-                    if (child.mLatestParent != node) {
-                        continue;
+            // Iterate the animations that haven't finished or haven't started, and end them.
+            if (mReversing) {
+                // Between start() and first frame, mLastEventId would be unset (i.e. -1)
+                mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
+                for (int j = mLastEventId - 1; j >= 0; j--) {
+                    AnimationEvent event = mEvents.get(j);
+                    if (event.mEvent == AnimationEvent.ANIMATION_END) {
+                        event.mNode.mAnimation.reverse();
+                    } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
+                        event.mNode.mAnimation.end();
                     }
-                    remainingList.add(child.mAnimation);
+                }
+            } else {
+                for (int j = mLastEventId + 1; j < mEvents.size(); j++) {
+                    AnimationEvent event = mEvents.get(j);
+                    if (event.mEvent == AnimationEvent.ANIMATION_START) {
+                        event.mNode.mAnimation.start();
+                    } else if (event.mEvent == AnimationEvent.ANIMATION_END) {
+                        event.mNode.mAnimation.end();
+                    }
                 }
             }
+            mPlayingSet.clear();
         }
+        endAnimation();
     }
 
-
     /**
      * Returns true if any of the child animations of this AnimatorSet have been started and have
      * not yet ended. Child animations will not be started until the AnimatorSet has gone past
@@ -437,14 +463,12 @@
      */
     @Override
     public boolean isRunning() {
-        int size = mNodes.size();
-        for (int i = 0; i < size; i++) {
-            Node node = mNodes.get(i);
-            if (node != mRootNode && node.mAnimation.isStarted()) {
-                return true;
-            }
+        if (mStartDelay > 0) {
+            return mStarted && !mDelayAnim.isRunning();
+        } else {
+            // No start delay, animation should start right away
+            return mStarted;
         }
-        return false;
     }
 
     @Override
@@ -482,9 +506,6 @@
             return;
         }
         mStartDelay = startDelay;
-        if (mStartDelay > 0) {
-            mReversible = false;
-        }
         if (!mDependencyDirty) {
             // Dependency graph already constructed, update all the nodes' start/end time
             int size = mNodes.size();
@@ -562,40 +583,26 @@
 
     @Override
     public void pause() {
+        if (Looper.myLooper() == null) {
+            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+        }
         boolean previouslyPaused = mPaused;
         super.pause();
         if (!previouslyPaused && mPaused) {
-            if (mDelayAnim.isStarted()) {
-                // If delay hasn't passed, pause the start delay animator.
-                mDelayAnim.pause();
-            } else {
-                int size = mNodes.size();
-                for (int i = 0; i < size; i++) {
-                    Node node = mNodes.get(i);
-                    if (node != mRootNode) {
-                        node.mAnimation.pause();
-                    }
-                }
-            }
+            mPauseTime = -1;
         }
     }
 
     @Override
     public void resume() {
+        if (Looper.myLooper() == null) {
+            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+        }
         boolean previouslyPaused = mPaused;
         super.resume();
         if (previouslyPaused && !mPaused) {
-            if (mDelayAnim.isStarted()) {
-                // If start delay hasn't passed, resume the previously paused start delay animator
-                mDelayAnim.resume();
-            } else {
-                int size = mNodes.size();
-                for (int i = 0; i < size; i++) {
-                    Node node = mNodes.get(i);
-                    if (node != mRootNode) {
-                        node.mAnimation.resume();
-                    }
-                }
+            if (mPauseTime >= 0) {
+                addAnimationCallback(0);
             }
         }
     }
@@ -610,9 +617,33 @@
     @SuppressWarnings("unchecked")
     @Override
     public void start() {
-        mTerminated = false;
+        start(false, true);
+    }
+
+    @Override
+    void startWithoutPulsing(boolean inReverse) {
+        start(inReverse, false);
+    }
+
+    private void initAnimation() {
+        if (mInterpolator != null) {
+            for (int i = 0; i < mNodes.size(); i++) {
+                Node node = mNodes.get(i);
+                node.mAnimation.setInterpolator(mInterpolator);
+            }
+        }
+        updateAnimatorsDuration();
+        createDependencyGraph();
+    }
+
+    private void start(boolean inReverse, boolean selfPulse) {
+        if (Looper.myLooper() == null) {
+            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+        }
         mStarted = true;
+        mSelfPulse = selfPulse;
         mPaused = false;
+        mPauseTime = -1;
 
         int size = mNodes.size();
         for (int i = 0; i < size; i++) {
@@ -621,26 +652,17 @@
             node.mAnimation.setAllowRunningAsynchronously(false);
         }
 
-        if (mInterpolator != null) {
-            for (int i = 0; i < size; i++) {
-                Node node = mNodes.get(i);
-                node.mAnimation.setInterpolator(mInterpolator);
-            }
+        initAnimation();
+        if (inReverse && !canReverse()) {
+            throw new UnsupportedOperationException("Cannot reverse infinite AnimatorSet");
         }
 
-        updateAnimatorsDuration();
-        createDependencyGraph();
+        mReversing = inReverse;
 
         // Now that all dependencies are set up, start the animations that should be started.
-        boolean setIsEmpty = false;
-        if (mStartDelay > 0) {
-            start(mRootNode);
-        } else if (isEmptySet(this)) {
-            // Set is empty or contains only empty animator sets. Skip to end in this case.
-            setIsEmpty = true;
-        } else {
-            // No delay, but there are other animators in the set
-            onChildAnimatorEnded(mDelayAnim);
+        boolean setIsEmpty = isEmptySet(this);
+        if (!setIsEmpty) {
+            startAnimation();
         }
 
         if (mListeners != null) {
@@ -648,12 +670,12 @@
                     (ArrayList<AnimatorListener>) mListeners.clone();
             int numListeners = tmpListeners.size();
             for (int i = 0; i < numListeners; ++i) {
-                tmpListeners.get(i).onAnimationStart(this);
+                tmpListeners.get(i).onAnimationStart(this, inReverse);
             }
         }
         if (setIsEmpty) {
             // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away.
-            onChildAnimatorEnded(mDelayAnim);
+            end();
         }
     }
 
@@ -690,11 +712,419 @@
         mDelayAnim.setDuration(mStartDelay);
     }
 
-    void start(final Node node) {
-        final Animator anim = node.mAnimation;
-        mPlayingSet.add(anim);
-        anim.addListener(mSetListener);
-        anim.start();
+    @Override
+    void skipToEndValue(boolean inReverse) {
+        if (!isInitialized()) {
+            throw new UnsupportedOperationException("Children must be initialized.");
+        }
+
+        // This makes sure the animation events are sorted an up to date.
+        initAnimation();
+
+        // Calling skip to the end in the sequence that they would be called in a forward/reverse
+        // run, such that the sequential animations modifying the same property would have
+        // the right value in the end.
+        if (inReverse) {
+            for (int i = mEvents.size() - 1; i >= 0; i--) {
+                if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
+                    mEvents.get(i).mNode.mAnimation.skipToEndValue(true);
+                }
+            }
+        } else {
+            for (int i = 0; i < mEvents.size(); i++) {
+                if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_END) {
+                    mEvents.get(i).mNode.mAnimation.skipToEndValue(false);
+                }
+            }
+        }
+    }
+
+    /**
+     * Internal only.
+     *
+     * This method sets the animation values based on the play time. It also fast forward or
+     * backward all the child animations progress accordingly.
+     *
+     * This method is also responsible for calling
+     * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)},
+     * as needed, based on the last play time and current play time.
+     */
+    @Override
+    void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {
+        if (currentPlayTime < 0 || lastPlayTime < 0) {
+            throw new UnsupportedOperationException("Error: Play time should never be negative.");
+        }
+        // TODO: take into account repeat counts and repeat callback when repeat is implemented.
+        // Clamp currentPlayTime and lastPlayTime
+
+        // TODO: Make this more efficient
+
+        // Convert the play times to the forward direction.
+        if (inReverse) {
+            if (getTotalDuration() == DURATION_INFINITE) {
+                throw new UnsupportedOperationException("Cannot reverse AnimatorSet with infinite"
+                        + " duration");
+            }
+            long duration = getTotalDuration() - mStartDelay;
+            currentPlayTime = Math.min(currentPlayTime, duration);
+            currentPlayTime = duration - currentPlayTime;
+            lastPlayTime = duration - lastPlayTime;
+            inReverse = false;
+        }
+        // Skip all values to start, and iterate mEvents to get animations to the right fraction.
+        skipToStartValue(false);
+
+        ArrayList<Node> unfinishedNodes = new ArrayList<>();
+        // Assumes forward playing from here on.
+        for (int i = 0; i < mEvents.size(); i++) {
+            AnimationEvent event = mEvents.get(i);
+            if (event.getTime() > currentPlayTime) {
+                break;
+            }
+
+            // This animation started prior to the current play time, and won't finish before the
+            // play time, add to the unfinished list.
+            if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
+                if (event.mNode.mEndTime == DURATION_INFINITE
+                        || event.mNode.mEndTime > currentPlayTime) {
+                    unfinishedNodes.add(event.mNode);
+                }
+            }
+            // For animations that do finish before the play time, end them in the sequence that
+            // they would in a normal run.
+            if (event.mEvent == AnimationEvent.ANIMATION_END) {
+                // Skip to the end of the animation.
+                event.mNode.mAnimation.skipToEndValue(false);
+            }
+        }
+
+        // Seek unfinished animation to the right time.
+        for (int i = 0; i < unfinishedNodes.size(); i++) {
+            Node node = unfinishedNodes.get(i);
+            long playTime = getPlayTimeForNode(currentPlayTime, node, inReverse);
+            node.mAnimation.animateBasedOnPlayTime(playTime, lastPlayTime, inReverse);
+        }
+    }
+
+    @Override
+    boolean isInitialized() {
+        if (mChildrenInitialized) {
+            return true;
+        }
+
+        boolean allInitialized = true;
+        for (int i = 0; i < mNodes.size(); i++) {
+            if (!mNodes.get(i).mAnimation.isInitialized()) {
+                allInitialized = false;
+                break;
+            }
+        }
+        mChildrenInitialized = allInitialized;
+        return mChildrenInitialized;
+    }
+
+    private void skipToStartValue(boolean inReverse) {
+        skipToEndValue(!inReverse);
+    }
+
+    /**
+     * Sets the position of the animation to the specified point in time. This time should
+     * be between 0 and the total duration of the animation, including any repetition. If
+     * the animation has not yet been started, then it will not advance forward after it is
+     * set to this time; it will simply set the time to this value and perform any appropriate
+     * actions based on that time. If the animation is already running, then setCurrentPlayTime()
+     * will set the current playing time to this value and continue playing from that point.
+     *
+     * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
+     *                 Unless the animation is reversing, the playtime is considered the time since
+     *                 the end of the start delay of the AnimatorSet in a forward playing direction.
+     *
+     */
+    public void setCurrentPlayTime(long playTime) {
+        if (mReversing && getTotalDuration() == DURATION_INFINITE) {
+            // Should never get here
+            throw new UnsupportedOperationException("Error: Cannot seek in reverse in an infinite"
+                    + " AnimatorSet");
+        }
+
+        if ((getTotalDuration() != DURATION_INFINITE && playTime > getTotalDuration() - mStartDelay)
+                || playTime < 0) {
+            throw new UnsupportedOperationException("Error: Play time should always be in between"
+                    + "0 and duration.");
+        }
+
+        initAnimation();
+
+        if (!isStarted()) {
+            if (mReversing) {
+                throw new UnsupportedOperationException("Error: Something went wrong. mReversing"
+                        + " should not be set when AnimatorSet is not started.");
+            }
+            if (!mSeekState.isActive()) {
+                findLatestEventIdForTime(0);
+                // Set all the values to start values.
+                initChildren();
+                skipToStartValue(mReversing);
+                mSeekState.setPlayTime(0, mReversing);
+            }
+            animateBasedOnPlayTime(playTime, 0, mReversing);
+            mSeekState.setPlayTime(playTime, mReversing);
+        } else {
+            // If the animation is running, just set the seek time and wait until the next frame
+            // (i.e. doAnimationFrame(...)) to advance the animation.
+            mSeekState.setPlayTime(playTime, mReversing);
+        }
+    }
+
+    private void initChildren() {
+        if (!isInitialized()) {
+            mChildrenInitialized = true;
+            // Forcefully initialize all children based on their end time, so that if the start
+            // value of a child is dependent on a previous animation, the animation will be
+            // initialized after the the previous animations have been advanced to the end.
+            skipToEndValue(false);
+        }
+    }
+
+    /**
+     * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
+     *                  base.
+     * @return
+     * @hide
+     */
+    @Override
+    public boolean doAnimationFrame(long frameTime) {
+        if (mLastFrameTime < 0) {
+            mFirstFrame = mLastFrameTime = frameTime;
+        }
+
+        // Handle pause/resume
+        if (mPaused) {
+            // Note: Child animations don't receive pause events. Since it's never a contract that
+            // the child animators will be paused when set is paused, this is unlikely to be an
+            // issue.
+            mPauseTime = frameTime;
+            removeAnimationCallback();
+            return false;
+        } else if (mPauseTime > 0) {
+                // Offset by the duration that the animation was paused
+            mFirstFrame += (frameTime - mPauseTime);
+            mPauseTime = -1;
+        }
+
+        // Continue at seeked position
+        if (mSeekState.isActive()) {
+            mSeekState.updateSeekDirection(mReversing);
+            mFirstFrame = frameTime - mSeekState.getPlayTime() - mStartDelay;
+            mSeekState.reset();
+        }
+
+        // This playTime includes the start delay.
+        long playTime = frameTime - mFirstFrame;
+
+        // 1. Pulse the animators that will start or end in this frame
+        // 2. Pulse the animators that will finish in a later frame
+        int latestId = findLatestEventIdForTime(playTime);
+        int startId = mLastEventId;
+
+        handleAnimationEvents(startId, latestId, playTime);
+
+        mLastEventId = latestId;
+
+        // Pump a frame to the on-going animators
+        for (int i = 0; i < mPlayingSet.size(); i++) {
+            Node node = mPlayingSet.get(i);
+            if (!node.mEnded) {
+                node.mEnded = node.mAnimation.doAnimationFrame(getPlayTimeForNode(playTime, node));
+            }
+        }
+
+        // Remove all the finished anims
+        for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
+            if (mPlayingSet.get(i).mEnded) {
+                mPlayingSet.remove(i);
+            }
+        }
+
+        mLastFrameTime = frameTime;
+        if (mPlayingSet.isEmpty()) {
+            boolean finished;
+            if (mReversing) {
+                // Make sure there's no more END event before current event id and after start delay
+                finished = mLastEventId <= 3;
+            } else {
+                // Make sure there's no more START event before current event id:
+                finished = (mLastEventId == mEvents.size() - 1);
+            }
+            if (finished) {
+                endAnimation();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * When playing forward, we call start() at the animation's scheduled start time, and make sure
+     * to pump a frame at the animation's scheduled end time.
+     *
+     * When playing in reverse, we should reverse the animation when we hit animation's end event,
+     * and expect the animation to end at the its delay ended event, rather than start event.
+     */
+    private void handleAnimationEvents(int startId, int latestId, long playTime) {
+        if (mReversing) {
+            startId = startId == -1 ? mEvents.size() : startId;
+            for (int i = startId - 1; i >= latestId; i--) {
+                AnimationEvent event = mEvents.get(i);
+                Node node = event.mNode;
+                if (event.mEvent == AnimationEvent.ANIMATION_END) {
+                    mPlayingSet.add(event.mNode);
+                    node.mAnimation.startWithoutPulsing(true);
+                    node.mAnimation.doAnimationFrame(0);
+                } else if (event.mEvent == AnimationEvent.ANIMATION_DELAY_ENDED && !node.mEnded) {
+                    // end event:
+                    node.mEnded =
+                            node.mAnimation.doAnimationFrame(getPlayTimeForNode(playTime, node));
+                }
+            }
+        } else {
+            for (int i = startId + 1; i <= latestId; i++) {
+                AnimationEvent event = mEvents.get(i);
+                Node node = event.mNode;
+                if (event.mEvent == AnimationEvent.ANIMATION_START) {
+                    mPlayingSet.add(event.mNode);
+                    node.mAnimation.startWithoutPulsing(false);
+                    node.mAnimation.doAnimationFrame(0);
+                } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) {
+                    // start event:
+                    node.mEnded =
+                            node.mAnimation.doAnimationFrame(getPlayTimeForNode(playTime, node));
+                }
+            }
+        }
+    }
+
+    private long getPlayTimeForNode(long overallPlayTime, Node node) {
+        return getPlayTimeForNode(overallPlayTime, node, mReversing);
+    }
+
+    private long getPlayTimeForNode(long overallPlayTime, Node node, boolean inReverse) {
+        if (inReverse) {
+            overallPlayTime = getTotalDuration() - overallPlayTime;
+            return node.mEndTime - overallPlayTime;
+        } else {
+            return overallPlayTime - node.mStartTime;
+        }
+    }
+
+    private void startAnimation() {
+        // Register animation callback
+        addAnimationCallback(mStartDelay);
+
+        if (mSeekState.getPlayTimeNormalized() == 0 && mReversing) {
+            // Maintain old behavior, if seeked to 0 then call reverse, we'll treat the case
+            // the same as no seeking at all.
+            mSeekState.reset();
+        }
+        // Set the child animators to the right end:
+        if (mShouldResetValuesAtStart) {
+            if (mReversing || isInitialized()) {
+                skipToEndValue(!mReversing);
+            } else {
+                // If not all children are initialized and play direction is forward
+                for (int i = mEvents.size() - 1; i >= 0; i--) {
+                    if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
+                        Animator anim = mEvents.get(i).mNode.mAnimation;
+                        // Only reset the animations that have been initialized to start value,
+                        // so that if they are defined without a start value, they will get the
+                        // values set at the right time (i.e. the next animation run)
+                        if (anim.isInitialized()) {
+                            anim.skipToEndValue(true);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (mReversing || mStartDelay == 0 || mSeekState.isActive()) {
+            long playTime;
+            // If no delay, we need to call start on the first animations to be consistent with old
+            // behavior.
+            if (mSeekState.isActive()) {
+                mSeekState.updateSeekDirection(mReversing);
+                playTime = mSeekState.getPlayTime();
+            } else {
+                playTime = 0;
+            }
+            int toId = findLatestEventIdForTime(playTime);
+            handleAnimationEvents(-1, toId, playTime);
+            mLastEventId = toId;
+        }
+    }
+
+    private int findLatestEventIdForTime(long currentPlayTime) {
+        int size = mEvents.size();
+        int latestId = mLastEventId;
+        // Call start on the first animations now to be consistent with the old behavior
+        if (mReversing) {
+            currentPlayTime = getTotalDuration() - currentPlayTime;
+            mLastEventId = mLastEventId == -1 ? size : mLastEventId;
+            for (int j = mLastEventId - 1; j >= 0; j--) {
+                AnimationEvent event = mEvents.get(j);
+                if (event.getTime() >= currentPlayTime) {
+                    latestId = j;
+                }
+            }
+        } else {
+            for (int i = mLastEventId + 1; i < size; i++) {
+                AnimationEvent event = mEvents.get(i);
+                if (event.getTime() <= currentPlayTime) {
+                    latestId = i;
+                }
+            }
+        }
+        return latestId;
+    }
+
+    private void endAnimation() {
+        mStarted = false;
+        mLastFrameTime = -1;
+        mFirstFrame = -1;
+        mLastEventId = -1;
+        mPaused = false;
+        mPauseTime = -1;
+        mSeekState.reset();
+        mPlayingSet.clear();
+
+        // No longer receive callbacks
+        removeAnimationCallback();
+        // Call end listener
+        if (mListeners != null) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            int numListeners = tmpListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                tmpListeners.get(i).onAnimationEnd(this, mReversing);
+            }
+        }
+        mSelfPulse = true;
+        mReversing = false;
+    }
+
+    private void removeAnimationCallback() {
+        if (!mSelfPulse) {
+            return;
+        }
+        AnimationHandler handler = AnimationHandler.getInstance();
+        handler.removeCallback(this);
+    }
+
+    private void addAnimationCallback(long delay) {
+        if (!mSelfPulse) {
+            return;
+        }
+        AnimationHandler handler = AnimationHandler.getInstance();
+        handler.addAnimationFrameCallback(this, delay);
     }
 
     @Override
@@ -709,13 +1139,20 @@
          * and will populate any appropriate lists, when it is started.
          */
         final int nodeCount = mNodes.size();
-        anim.mTerminated = false;
         anim.mStarted = false;
-        anim.mPlayingSet = new ArrayList<Animator>();
+        anim.mLastFrameTime = -1;
+        anim.mFirstFrame = -1;
+        anim.mLastEventId = -1;
+        anim.mPaused = false;
+        anim.mPauseTime = -1;
+        anim.mSeekState = new SeekState();
+        anim.mSelfPulse = true;
+        anim.mPlayingSet = new ArrayList<Node>();
         anim.mNodeMap = new ArrayMap<Animator, Node>();
         anim.mNodes = new ArrayList<Node>(nodeCount);
-        anim.mReversible = mReversible;
-        anim.mSetListener = new AnimatorSetListener(anim);
+        anim.mEvents = new ArrayList<AnimationEvent>();
+        anim.mReversing = false;
+        anim.mDependencyDirty = true;
 
         // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
         // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
@@ -727,17 +1164,6 @@
             node.mTmpClone = nodeClone;
             anim.mNodes.add(nodeClone);
             anim.mNodeMap.put(nodeClone.mAnimation, nodeClone);
-
-            // clear out any listeners that were set up by the AnimatorSet
-            final ArrayList<AnimatorListener> cloneListeners = nodeClone.mAnimation.getListeners();
-            if (cloneListeners != null) {
-                for (int i = cloneListeners.size() - 1; i >= 0; i--) {
-                    final AnimatorListener listener = cloneListeners.get(i);
-                    if (listener instanceof AnimatorSetListener) {
-                        cloneListeners.remove(i);
-                    }
-                }
-            }
         }
 
         anim.mRootNode = mRootNode.mTmpClone;
@@ -771,89 +1197,6 @@
     }
 
 
-    private static class AnimatorSetListener implements AnimatorListener {
-
-        private AnimatorSet mAnimatorSet;
-
-        AnimatorSetListener(AnimatorSet animatorSet) {
-            mAnimatorSet = animatorSet;
-        }
-
-        public void onAnimationCancel(Animator animation) {
-
-            if (!mAnimatorSet.mTerminated) {
-                // Listeners are already notified of the AnimatorSet canceling in cancel().
-                // The logic below only kicks in when animations end normally
-                if (mAnimatorSet.mPlayingSet.size() == 0) {
-                    ArrayList<AnimatorListener> listeners = mAnimatorSet.mListeners;
-                    if (listeners != null) {
-                        int numListeners = listeners.size();
-                        for (int i = 0; i < numListeners; ++i) {
-                            listeners.get(i).onAnimationCancel(mAnimatorSet);
-                        }
-                    }
-                }
-            }
-        }
-
-        @SuppressWarnings("unchecked")
-        public void onAnimationEnd(Animator animation) {
-            animation.removeListener(this);
-            mAnimatorSet.mPlayingSet.remove(animation);
-            mAnimatorSet.onChildAnimatorEnded(animation);
-        }
-
-        // Nothing to do
-        public void onAnimationRepeat(Animator animation) {
-        }
-
-        // Nothing to do
-        public void onAnimationStart(Animator animation) {
-        }
-
-    }
-
-    private void onChildAnimatorEnded(Animator animation) {
-        Node animNode = mNodeMap.get(animation);
-        animNode.mEnded = true;
-
-        if (!mTerminated) {
-            List<Node> children = animNode.mChildNodes;
-            // Start children animations, if any.
-            int childrenSize = children == null ? 0 : children.size();
-            for (int i = 0; i < childrenSize; i++) {
-                if (children.get(i).mLatestParent == animNode) {
-                    start(children.get(i));
-                }
-            }
-            // Listeners are already notified of the AnimatorSet ending in cancel() or
-            // end(); the logic below only kicks in when animations end normally
-            boolean allDone = true;
-            // Traverse the tree and find if there's any unfinished node
-            int size = mNodes.size();
-            for (int i = 0; i < size; i++) {
-                if (!mNodes.get(i).mEnded) {
-                    allDone = false;
-                    break;
-                }
-            }
-            if (allDone) {
-                mStarted = false;
-                mPaused = false;
-                // If this was the last child animation to end, then notify listeners that this
-                // AnimatorSet has ended
-                if (mListeners != null) {
-                    ArrayList<AnimatorListener> tmpListeners =
-                            (ArrayList<AnimatorListener>) mListeners.clone();
-                    int numListeners = tmpListeners.size();
-                    for (int i = 0; i < numListeners; ++i) {
-                        tmpListeners.get(i).onAnimationEnd(this);
-                    }
-                }
-            }
-        }
-    }
-
     /**
      * AnimatorSet is only reversible when the set contains no sequential animation, and no child
      * animators have a start delay.
@@ -861,32 +1204,21 @@
      */
     @Override
     public boolean canReverse() {
-        if (!mReversible)  {
-            return false;
-        }
-        // Loop to make sure all the Nodes can reverse.
-        int size = mNodes.size();
-        for (int i = 0; i < size; i++) {
-            Node node = mNodes.get(i);
-            if (!node.mAnimation.canReverse() || node.mAnimation.getStartDelay() > 0) {
-                return false;
-            }
-        }
-        return true;
+        return getTotalDuration() != DURATION_INFINITE;
     }
 
     /**
-     * @hide
+     * Plays the AnimatorSet in reverse. If the animation has been seeked to a specific play time
+     * using {@link #setCurrentPlayTime(long)}, it will play backwards from the point seeked when
+     * reverse was called. Otherwise, then it will start from the end and play backwards. This
+     * behavior is only set for the current animation; future playing of the animation will use the
+     * default behavior of playing forward.
+     * <p>
+     * Note: reverse is not supported for infinite AnimatorSet.
      */
     @Override
     public void reverse() {
-        if (canReverse()) {
-            int size = mNodes.size();
-            for (int i = 0; i < size; i++) {
-                Node node = mNodes.get(i);
-                node.mAnimation.reverse();
-            }
-        }
+        start(true, true);
     }
 
     @Override
@@ -993,18 +1325,124 @@
         mRootNode.mEndTime = mDelayAnim.getDuration();
         updatePlayTime(mRootNode, visited);
 
-        long maxEndTime = 0;
-        for (int i = 0; i < size; i++) {
+        sortAnimationEvents();
+        mTotalDuration = mEvents.get(mEvents.size() - 1).getTime();
+    }
+
+    private void sortAnimationEvents() {
+        // Sort the list of events in ascending order of their time
+        // Create the list including the delay animation.
+        mEvents.clear();
+        for (int i = 1; i < mNodes.size(); i++) {
             Node node = mNodes.get(i);
-            node.mTotalDuration = node.mAnimation.getTotalDuration();
-            if (node.mEndTime == DURATION_INFINITE) {
-                maxEndTime = DURATION_INFINITE;
-                break;
+            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_START));
+            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_DELAY_ENDED));
+            mEvents.add(new AnimationEvent(node, AnimationEvent.ANIMATION_END));
+        }
+        mEvents.sort(new Comparator<AnimationEvent>() {
+            @Override
+            public int compare(AnimationEvent e1, AnimationEvent e2) {
+                long t1 = e1.getTime();
+                long t2 = e2.getTime();
+                if (t1 == t2) {
+                    // For events that happen at the same time, we need them to be in the sequence
+                    // (end, start, start delay ended)
+                    if (e2.mEvent + e1.mEvent == AnimationEvent.ANIMATION_START
+                            + AnimationEvent.ANIMATION_DELAY_ENDED) {
+                        // Ensure start delay happens after start
+                        return e1.mEvent - e2.mEvent;
+                    } else {
+                        return e2.mEvent - e1.mEvent;
+                    }
+                }
+                if (t2 == DURATION_INFINITE) {
+                    return -1;
+                }
+                if (t1 == DURATION_INFINITE) {
+                    return 1;
+                }
+                // When neither event happens at INFINITE time:
+                return (int) (t1 - t2);
+            }
+        });
+
+        int eventSize = mEvents.size();
+        // For the same animation, start event has to happen before end.
+        for (int i = 0; i < eventSize;) {
+            AnimationEvent event = mEvents.get(i);
+            if (event.mEvent == AnimationEvent.ANIMATION_END) {
+                boolean needToSwapStart;
+                if (event.mNode.mStartTime == event.mNode.mEndTime) {
+                    needToSwapStart = true;
+                } else if (event.mNode.mEndTime == event.mNode.mStartTime
+                        + event.mNode.mAnimation.getStartDelay()) {
+                    // Swapping start delay
+                    needToSwapStart = false;
+                } else {
+                    i++;
+                    continue;
+                }
+
+                int startEventId = eventSize;
+                int startDelayEndId = eventSize;
+                for (int j = i + 1; j < eventSize; j++) {
+                    if (startEventId < eventSize && startDelayEndId < eventSize) {
+                        break;
+                    }
+                    if (mEvents.get(j).mNode == event.mNode) {
+                        if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_START) {
+                            // Found start event
+                            startEventId = j;
+                        } else if (mEvents.get(j).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
+                            startDelayEndId = j;
+                        }
+                    }
+
+                }
+                if (needToSwapStart && startEventId == mEvents.size()) {
+                    throw new UnsupportedOperationException("Something went wrong, no start is"
+                            + "found after stop for an animation that has the same start and end"
+                            + "time.");
+
+                }
+                if (startDelayEndId == mEvents.size()) {
+                    throw new UnsupportedOperationException("Something went wrong, no start"
+                            + "delay end is found after stop for an animation");
+
+                }
+
+                // We need to make sure start is inserted before start delay ended event,
+                // because otherwise inserting start delay ended events first would change
+                // the start event index.
+                if (needToSwapStart) {
+                    AnimationEvent startEvent = mEvents.remove(startEventId);
+                    mEvents.add(i, startEvent);
+                    i++;
+                }
+
+                AnimationEvent startDelayEndEvent = mEvents.remove(startDelayEndId);
+                mEvents.add(i, startDelayEndEvent);
+                i += 2;
             } else {
-                maxEndTime = node.mEndTime > maxEndTime ? node.mEndTime : maxEndTime;
+                i++;
             }
         }
-        mTotalDuration = maxEndTime;
+
+        if (!mEvents.isEmpty() && mEvents.get(0).mEvent != AnimationEvent.ANIMATION_START) {
+            throw new UnsupportedOperationException(
+                    "Sorting went bad, the start event should always be at index 0");
+        }
+
+        // Add AnimatorSet's start delay node to the beginning
+        mEvents.add(0, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_START));
+        mEvents.add(1, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_DELAY_ENDED));
+        mEvents.add(2, new AnimationEvent(mRootNode, AnimationEvent.ANIMATION_END));
+
+        if (mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_START
+                || mEvents.get(mEvents.size() - 1).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) {
+            throw new UnsupportedOperationException(
+                    "Something went wrong, the last event is not an end event");
+        }
     }
 
     /**
@@ -1235,6 +1673,95 @@
     }
 
     /**
+     * This class is a wrapper around a node and an event for the animation corresponding to the
+     * node. The 3 types of events represent the start of an animation, the end of a start delay of
+     * an animation, and the end of an animation. When playing forward (i.e. in the non-reverse
+     * direction), start event marks when start() should be called, and end event corresponds to
+     * when the animation should finish. When playing in reverse, start delay will not be a part
+     * of the animation. Therefore, reverse() is called at the end event, and animation should end
+     * at the delay ended event.
+     */
+    private static class AnimationEvent {
+        static final int ANIMATION_START = 0;
+        static final int ANIMATION_DELAY_ENDED = 1;
+        static final int ANIMATION_END = 2;
+        final Node mNode;
+        final int mEvent;
+
+        AnimationEvent(Node node, int event) {
+            mNode = node;
+            mEvent = event;
+        }
+
+        long getTime() {
+            if (mEvent == ANIMATION_START) {
+                return mNode.mStartTime;
+            } else if (mEvent == ANIMATION_DELAY_ENDED) {
+                return mNode.mStartTime + mNode.mAnimation.getStartDelay();
+            } else {
+                return mNode.mEndTime;
+            }
+        }
+
+        public String toString() {
+            String eventStr = mEvent == ANIMATION_START ? "start" : (
+                    mEvent == ANIMATION_DELAY_ENDED ? "delay ended" : "end");
+            return eventStr + " " + mNode.mAnimation.toString();
+        }
+    }
+
+    private class SeekState {
+        private long mPlayTime = -1;
+        private boolean mSeekingInReverse = false;
+        void reset() {
+            mPlayTime = -1;
+            mSeekingInReverse = false;
+        }
+
+        void setPlayTime(long playTime, boolean inReverse) {
+            // TODO: This can be simplified.
+
+            // Clamp the play time
+            if (getTotalDuration() != DURATION_INFINITE) {
+                mPlayTime = Math.min(playTime, getTotalDuration() - mStartDelay);
+            }
+            mPlayTime = Math.max(0, mPlayTime);
+            mSeekingInReverse = inReverse;
+        }
+
+        void updateSeekDirection(boolean inReverse) {
+            // Change seek direction without changing the overall fraction
+            if (inReverse && getTotalDuration() == DURATION_INFINITE) {
+                throw new UnsupportedOperationException("Error: Cannot reverse infinite animator"
+                        + " set");
+            }
+            if (mPlayTime >= 0) {
+                if (inReverse != mSeekingInReverse) {
+                    mPlayTime = getTotalDuration() - mStartDelay - mPlayTime;
+                }
+            }
+        }
+
+        long getPlayTime() {
+            return mPlayTime;
+        }
+
+        /**
+         * Returns the playtime assuming the animation is forward playing
+         */
+        long getPlayTimeNormalized() {
+            if (mReversing) {
+                return getTotalDuration() - mStartDelay - mPlayTime;
+            }
+            return mPlayTime;
+        }
+
+        boolean isActive() {
+            return mPlayTime != -1;
+        }
+    }
+
+    /**
      * The <code>Builder</code> object is a utility class to facilitate adding animations to a
      * <code>AnimatorSet</code> along with the relationships between the various animations. The
      * intention of the <code>Builder</code> methods, along with the {@link
@@ -1328,7 +1855,6 @@
          * {@link AnimatorSet#play(Animator)} method ends.
          */
         public Builder before(Animator anim) {
-            mReversible = false;
             Node node = getNodeForAnimation(anim);
             mCurrentNode.addChild(node);
             return this;
@@ -1343,7 +1869,6 @@
          * {@link AnimatorSet#play(Animator)} method to play.
          */
         public Builder after(Animator anim) {
-            mReversible = false;
             Node node = getNodeForAnimation(anim);
             mCurrentNode.addParent(node);
             return this;
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 4707bed..1e1f155 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -992,6 +992,11 @@
     }
 
     @Override
+    boolean isInitialized() {
+        return mInitialized;
+    }
+
+    @Override
     public ObjectAnimator clone() {
         final ObjectAnimator anim = (ObjectAnimator) super.clone();
         return anim;
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index f0fc8af..470523f 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -24,6 +24,7 @@
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.LinearInterpolator;
 
@@ -67,7 +68,7 @@
  * </div>
  */
 @SuppressWarnings("unchecked")
-public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
+public class ValueAnimator extends Animator {
     private static final String TAG = "ValueAnimator";
     private static final boolean DEBUG = false;
 
@@ -90,7 +91,7 @@
      *
      * Whenever mStartTime is set, you must also update mStartTimeCommitted.
      */
-    long mStartTime;
+    long mStartTime = -1;
 
     /**
      * When true, the start time has been firmly committed as a chosen reference point in
@@ -152,7 +153,13 @@
     /**
      * Tracks the time (in milliseconds) when the last frame arrived.
      */
-    private long mLastFrameTime = 0;
+    private long mLastFrameTime = -1;
+
+    /**
+     * Tracks the time (in milliseconds) when the first frame arrived. Note the frame may arrive
+     * during the start delay.
+     */
+    private long mFirstFrameTime = -1;
 
     /**
      * Additional playing state to indicate whether an animator has been start()'d. There is
@@ -212,6 +219,12 @@
     private int mRepeatMode = RESTART;
 
     /**
+     * Whether or not the animator should register for its own animation callback to receive
+     * animation pulse.
+     */
+    private boolean mSelfPulse = true;
+
+    /**
      * The time interpolator to be used. The elapsed fraction of the animation will be passed
      * through this interpolator to calculate the interpolated fraction, which is then used to
      * calculate the animated values.
@@ -628,7 +641,7 @@
             mSeekFraction = fraction;
         }
         mOverallFraction = fraction;
-        final float currentIterationFraction = getCurrentIterationFraction(fraction);
+        final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
         animateValue(currentIterationFraction);
     }
 
@@ -654,11 +667,11 @@
      * should be played backwards. E.g. When the animation is played backwards in an iteration,
      * the fraction for that iteration will go from 1f to 0f.
      */
-    private float getCurrentIterationFraction(float fraction) {
+    private float getCurrentIterationFraction(float fraction, boolean inReverse) {
         fraction = clampFraction(fraction);
         int iteration = getCurrentIteration(fraction);
         float currentFraction = fraction - iteration;
-        return shouldPlayBackward(iteration) ? 1f - currentFraction : currentFraction;
+        return shouldPlayBackward(iteration, inReverse) ? 1f - currentFraction : currentFraction;
     }
 
     /**
@@ -682,18 +695,18 @@
      * whether the entire animation is being reversed, 2) repeat mode applied to the current
      * iteration.
      */
-    private boolean shouldPlayBackward(int iteration) {
+    private boolean shouldPlayBackward(int iteration, boolean inReverse) {
         if (iteration > 0 && mRepeatMode == REVERSE &&
                 (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
             // if we were seeked to some other iteration in a reversing animator,
             // figure out the correct direction to start playing based on the iteration
-            if (mReversing) {
+            if (inReverse) {
                 return (iteration % 2) == 0;
             } else {
                 return (iteration % 2) != 0;
             }
         } else {
-            return mReversing;
+            return inReverse;
         }
     }
 
@@ -965,7 +978,7 @@
                     (ArrayList<AnimatorListener>) mListeners.clone();
             int numListeners = tmpListeners.size();
             for (int i = 0; i < numListeners; ++i) {
-                tmpListeners.get(i).onAnimationStart(this);
+                tmpListeners.get(i).onAnimationStart(this, mReversing);
             }
         }
         mStartListenersCalled = true;
@@ -984,11 +997,12 @@
      *
      * @param playBackwards Whether the ValueAnimator should start playing in reverse.
      */
-    private void start(boolean playBackwards) {
+    private void start(boolean playBackwards, boolean selfPulse) {
         if (Looper.myLooper() == null) {
             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
         }
         mReversing = playBackwards;
+        mSelfPulse = selfPulse;
         // Special case: reversing from seek-to-0 should act as if not seeked at all.
         if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
             if (mRepeatCount == INFINITE) {
@@ -1006,11 +1020,11 @@
         // Resets mLastFrameTime when start() is called, so that if the animation was running,
         // calling start() would put the animation in the
         // started-but-not-yet-reached-the-first-frame phase.
-        mLastFrameTime = 0;
-        AnimationHandler animationHandler = AnimationHandler.getInstance();
-        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
+        mLastFrameTime = -1;
+        mFirstFrameTime = -1;
+        addAnimationCallback((long) (mStartDelay * sDurationScale));
 
-        if (mStartDelay == 0 || mSeekFraction >= 0) {
+        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
             // If there's no start delay, init the animation and notify start listeners right away
             // to be consistent with the previous behavior. Otherwise, postpone this until the first
             // frame after the start delay.
@@ -1026,9 +1040,13 @@
         }
     }
 
+    void startWithoutPulsing(boolean inReverse) {
+        start(inReverse, false);
+    }
+
     @Override
     public void start() {
-        start(false);
+        start(false, true);
     }
 
     @Override
@@ -1073,7 +1091,7 @@
         } else if (!mInitialized) {
             initAnimation();
         }
-        animateValue(shouldPlayBackward(mRepeatCount) ? 0f : 1f);
+        animateValue(shouldPlayBackward(mRepeatCount, mReversing) ? 0f : 1f);
         endAnimation();
     }
 
@@ -1086,8 +1104,7 @@
         if (mPaused && !mResumed) {
             mResumed = true;
             if (mPauseTime > 0) {
-                AnimationHandler handler = AnimationHandler.getInstance();
-                handler.addAnimationFrameCallback(this, 0);
+                addAnimationCallback(0);
             }
         }
         super.resume();
@@ -1133,7 +1150,7 @@
             mReversing = !mReversing;
             end();
         } else {
-            start(true);
+            start(true, true);
         }
     }
 
@@ -1153,8 +1170,7 @@
         if (mAnimationEndRequested) {
             return;
         }
-        AnimationHandler handler = AnimationHandler.getInstance();
-        handler.removeCallback(this);
+        removeAnimationCallback();
 
         mAnimationEndRequested = true;
         mPaused = false;
@@ -1166,16 +1182,18 @@
         mRunning = false;
         mStarted = false;
         mStartListenersCalled = false;
+        mLastFrameTime = -1;
+        mFirstFrameTime = -1;
         mReversing = false;
-        mLastFrameTime = 0;
         if (notify && mListeners != null) {
             ArrayList<AnimatorListener> tmpListeners =
                     (ArrayList<AnimatorListener>) mListeners.clone();
             int numListeners = tmpListeners.size();
             for (int i = 0; i < numListeners; ++i) {
-                tmpListeners.get(i).onAnimationEnd(this);
+                tmpListeners.get(i).onAnimationEnd(this, mReversing);
             }
         }
+        mReversing = false;
         if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
             Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                     System.identityHashCode(this));
@@ -1211,7 +1229,7 @@
      * is called (or after start delay if any), which may be before the animation loop starts.
      */
     private boolean isPulsingInternal() {
-        return mLastFrameTime > 0;
+        return mLastFrameTime >= 0;
     }
 
     /**
@@ -1276,24 +1294,116 @@
                 done = true;
             }
             mOverallFraction = clampFraction(fraction);
-            float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
+            float currentIterationFraction = getCurrentIterationFraction(
+                    mOverallFraction, mReversing);
             animateValue(currentIterationFraction);
         }
         return done;
     }
 
     /**
+     * Internal use only.
+     *
+     * This method does not modify any fields of the animation. It should be called when seeking
+     * in an AnimatorSet. When the last play time and current play time are of different repeat
+     * iterations,
+     * {@link android.view.animation.Animation.AnimationListener#onAnimationRepeat(Animation)}
+     * will be called.
+     */
+    @Override
+    void animateBasedOnPlayTime(long currentPlayTime, long lastPlayTime, boolean inReverse) {
+        if (currentPlayTime < 0 || lastPlayTime < 0) {
+            throw new UnsupportedOperationException("Error: Play time should never be negative.");
+        }
+
+        initAnimation();
+        // Check whether repeat callback is needed only when repeat count is non-zero
+        if (mRepeatCount > 0) {
+            int iteration = (int) (currentPlayTime / mDuration);
+            int lastIteration = (int) (lastPlayTime / mDuration);
+
+            // Clamp iteration to [0, mRepeatCount]
+            iteration = Math.min(iteration, mRepeatCount);
+            lastIteration = Math.min(lastIteration, mRepeatCount);
+
+            if (iteration != lastIteration) {
+                if (mListeners != null) {
+                    int numListeners = mListeners.size();
+                    for (int i = 0; i < numListeners; ++i) {
+                        mListeners.get(i).onAnimationRepeat(this);
+                    }
+                }
+            }
+        }
+
+        if (mRepeatCount != INFINITE && currentPlayTime >= (mRepeatCount + 1) * mDuration) {
+            skipToEndValue(inReverse);
+        } else {
+            // Find the current fraction:
+            float fraction = currentPlayTime / (float) mDuration;
+            fraction = getCurrentIterationFraction(fraction, inReverse);
+            animateValue(fraction);
+        }
+    }
+
+    /**
+     * Internal use only.
+     * Skips the animation value to end/start, depending on whether the play direction is forward
+     * or backward.
+     *
+     * @param inReverse whether the end value is based on a reverse direction. If yes, this is
+     *                  equivalent to skip to start value in a forward playing direction.
+     */
+    void skipToEndValue(boolean inReverse) {
+        initAnimation();
+        float endFraction = inReverse ? 0f : 1f;
+        if (mRepeatCount % 2 == 1 && mRepeatMode == REVERSE) {
+            // This would end on fraction = 0
+            endFraction = 0f;
+        }
+        animateValue(endFraction);
+    }
+
+    /**
      * Processes a frame of the animation, adjusting the start time if needed.
      *
      * @param frameTime The frame time.
      * @return true if the animation has ended.
      * @hide
      */
-    public final void doAnimationFrame(long frameTime) {
-        AnimationHandler handler = AnimationHandler.getInstance();
-        if (mLastFrameTime == 0) {
+    public final boolean doAnimationFrame(long frameTime) {
+        if (!mRunning && mStartTime < 0) {
+            // First frame during delay
+            mStartTime = frameTime + mStartDelay;
+        }
+
+        // Handle pause/resume
+        if (mPaused) {
+            mPauseTime = frameTime;
+            removeAnimationCallback();
+            return false;
+        } else if (mResumed) {
+            mResumed = false;
+            if (mPauseTime > 0) {
+                // Offset by the duration that the animation was paused
+                mStartTime += (frameTime - mPauseTime);
+            }
+        }
+
+        if (!mRunning) {
+            // If not running, that means the animation is in the start delay phase. In the case of
+            // reversing, we want to run start delay in the end.
+            if (mStartTime > frameTime) {
+                // During start delay
+                return false;
+            } else {
+                // Start delay has passed.
+                mRunning = true;
+            }
+        }
+
+        if (mLastFrameTime < 0) {
             // First frame
-            handler.addOneShotCommitCallback(this);
             if (mStartDelay > 0) {
                 startAnimation();
             }
@@ -1307,19 +1417,6 @@
             mStartTimeCommitted = false; // allow start time to be compensated for jank
         }
         mLastFrameTime = frameTime;
-        if (mPaused) {
-            mPauseTime = frameTime;
-            handler.removeCallback(this);
-            return;
-        } else if (mResumed) {
-            mResumed = false;
-            if (mPauseTime > 0) {
-                // Offset by the duration that the animation was paused
-                mStartTime += (frameTime - mPauseTime);
-                mStartTimeCommitted = false; // allow start time to be compensated for jank
-            }
-            handler.addOneShotCommitCallback(this);
-        }
         // The frame time might be before the start time during the first frame of
         // an animation.  The "current time" must always be on or after the start
         // time to avoid animating frames at negative time intervals.  In practice, this
@@ -1330,6 +1427,31 @@
         if (finished) {
             endAnimation();
         }
+        return finished;
+    }
+
+    private void addOneShotCommitCallback() {
+        if (!mSelfPulse) {
+            return;
+        }
+        AnimationHandler handler = AnimationHandler.getInstance();
+        handler.addOneShotCommitCallback(this);
+    }
+
+    private void removeAnimationCallback() {
+        if (!mSelfPulse) {
+            return;
+        }
+        AnimationHandler handler = AnimationHandler.getInstance();
+        handler.removeCallback(this);
+    }
+
+    private void addAnimationCallback(long delay) {
+        if (!mSelfPulse) {
+            return;
+        }
+        AnimationHandler handler = AnimationHandler.getInstance();
+        handler.addAnimationFrameCallback(this, delay);
     }
 
     /**
@@ -1384,11 +1506,12 @@
         anim.mPaused = false;
         anim.mResumed = false;
         anim.mStartListenersCalled = false;
-        anim.mStartTime = 0;
+        anim.mStartTime = -1;
         anim.mStartTimeCommitted = false;
         anim.mAnimationEndRequested = false;
-        anim.mPauseTime = 0;
-        anim.mLastFrameTime = 0;
+        anim.mPauseTime = -1;
+        anim.mLastFrameTime = -1;
+        anim.mFirstFrameTime = -1;
         anim.mOverallFraction = 0;
         anim.mCurrentFraction = 0;
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f6771857..580bb50 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -78,8 +78,6 @@
 import android.service.autofill.IAutoFillAppCallback;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
-import android.text.TextAssistant;
-import android.text.TextClassificationManager;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.transition.Scene;
@@ -792,8 +790,6 @@
 
     private VoiceInteractor mVoiceInteractor;
 
-    private TextAssistant mTextAssistant;
-
     private CharSequence mTitle;
     private int mTitleColor = 0;
 
@@ -1398,24 +1394,6 @@
     }
 
     /**
-     * Sets the default {@link TextAssistant} for {@link android.widget.TextView}s in this Activity.
-     */
-    public void setTextAssistant(TextAssistant textAssistant) {
-        mTextAssistant = textAssistant;
-    }
-
-    /**
-     * Returns the default {@link TextAssistant} for {@link android.widget.TextView}s
-     * in this Activity.
-     */
-    public TextAssistant getTextAssistant() {
-        if (mTextAssistant != null) {
-            return mTextAssistant;
-        }
-        return getSystemService(TextClassificationManager.class);
-    }
-
-    /**
      * This is called for activities that set launchMode to "singleTop" in
      * their package, or if a client used the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP}
      * flag when calling {@link #startActivity}.  In either case, when the
@@ -2029,78 +2007,53 @@
     }
 
     /**
-     * Puts the activity in picture-in-picture mode.
+     * Puts the activity in picture-in-picture mode if possible in the current system state. Any
+     * prior calls to {@link #setPictureInPictureArgs(PictureInPictureArgs)} will still apply when
+     * entering picture-in-picture through this call.
+     *
+     * @see #enterPictureInPictureMode(PictureInPictureArgs)
      * @see android.R.attr#supportsPictureInPicture
      */
     public void enterPictureInPictureMode() {
-        try {
-            ActivityManager.getService().enterPictureInPictureMode(mToken);
-        } catch (RemoteException e) {
-        }
+        enterPictureInPictureMode(new PictureInPictureArgs());
     }
 
     /**
-     * Puts the activity in picture-in-picture mode with a given aspect ratio.
+     * Puts the activity in picture-in-picture mode if possible in the current system state with
+     * explicit given arguments. Only the set parameters in {@param args} will override prior calls
+     * {@link #setPictureInPictureArgs(PictureInPictureArgs)}.
+     *
+     * The system may disallow entering picture-in-picture in various cases, including when the
+     * activity is not visible.
+     *
      * @see android.R.attr#supportsPictureInPicture
      *
-     * @param aspectRatio the new aspect ratio of the picture-in-picture.
+     * @param args the explicit non-null arguments to use when entering picture-in-picture.
+     * @return whether the system successfully entered picture-in-picture.
      */
-    public void enterPictureInPictureMode(float aspectRatio) {
+    public boolean enterPictureInPictureMode(@NonNull PictureInPictureArgs args) {
         try {
-            ActivityManagerNative.getDefault().enterPictureInPictureModeWithAspectRatio(mToken,
-                    aspectRatio);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Requests to the system that the activity can be automatically put into picture-in-picture
-     * mode when the user leaves the activity causing it normally to be hidden.  Generally, this
-     * happens when another task is brought to the forground or the task containing this activity
-     * is moved to the background.  This is a *not* a guarantee that the activity will actually be
-     * put in picture-in-picture mode, and depends on a number of factors, including whether there
-     * is already something in picture-in-picture.
-     *
-     * @param enterPictureInPictureOnMoveToBg whether or not this activity can automatically enter
-     *                                        picture-in-picture
-     */
-    public void enterPictureInPictureModeOnMoveToBackground(
-            boolean enterPictureInPictureOnMoveToBg) {
-        try {
-            ActivityManagerNative.getDefault().enterPictureInPictureModeOnMoveToBackground(mToken,
-                    enterPictureInPictureOnMoveToBg);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Updates the aspect ratio of the current picture-in-picture activity if this activity is
-     * already in picture-in-picture mode, or sets it to be used later if
-     * {@link #enterPictureInPictureModeOnMoveToBackground(boolean)} is requested.
-     *
-     * @param aspectRatio the new aspect ratio of the picture-in-picture.
-     */
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        try {
-            ActivityManagerNative.getDefault().setPictureInPictureAspectRatio(mToken, aspectRatio);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Updates the set of user actions associated with the picture-in-picture activity.
-     *
-     * @param actions the new actions for picture-in-picture (can be null to reset the set of
-     *                actions).  The maximum number of actions that will be displayed on this device
-     *                is defined by {@link ActivityManager#getMaxNumPictureInPictureActions()}.
-     */
-    public void setPictureInPictureActions(List<RemoteAction> actions) {
-        try {
-            if (actions == null) {
-                actions = new ArrayList<>();
+            if (args == null) {
+                throw new IllegalArgumentException("Expected non-null picture-in-picture args");
             }
-            ActivityManagerNative.getDefault().setPictureInPictureActions(mToken,
-                    new ParceledListSlice<RemoteAction>(actions));
+            return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, args);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Updates the properties of the picture-in-picture activity, or sets it to be used later when
+     * {@link #enterPictureInPictureMode()} is called.
+     *
+     * @param args the new properties of the picture-in-picture.
+     */
+    public void setPictureInPictureArgs(@NonNull PictureInPictureArgs args) {
+        try {
+            if (args == null) {
+                throw new IllegalArgumentException("Expected non-null picture-in-picture args");
+            }
+            ActivityManagerNative.getDefault().setPictureInPictureArgs(mToken, args);
         } catch (RemoteException e) {
         }
     }
@@ -3155,7 +3108,7 @@
      */
     @Override
     public void enterPictureInPictureModeIfPossible() {
-        if (mActivityInfo.resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE) {
+        if (mActivityInfo.supportsPictureInPicture()) {
             enterPictureInPictureMode();
         }
     }
@@ -4377,7 +4330,7 @@
      * @param requestCode If >= 0, this code will be returned in
      *                    onActivityResult() when the activity exits.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @throws android.content.ActivityNotFoundException
@@ -4586,7 +4539,7 @@
      * <var>flagsMask</var>
      * @param extraFlags Always set to 0.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.  If options
      * have also been supplied by the IntentSender, options given here will
      * override any that conflict with those given by the IntentSender.
@@ -4671,7 +4624,7 @@
      *
      * @param intent The intent to start.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @throws android.content.ActivityNotFoundException
@@ -4720,7 +4673,7 @@
      *
      * @param intents The intents to start.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @throws android.content.ActivityNotFoundException
@@ -4769,7 +4722,7 @@
      * <var>flagsMask</var>
      * @param extraFlags Always set to 0.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.  If options
      * have also been supplied by the IntentSender, options given here will
      * override any that conflict with those given by the IntentSender.
@@ -4829,7 +4782,7 @@
      *         onActivityResult() when the activity exits, as described in
      *         {@link #startActivityForResult}.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @return If a new activity was launched then true is returned; otherwise
@@ -4906,7 +4859,7 @@
      * your own activity; the only changes you can make are to the extras
      * inside of it.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @return Returns a boolean indicating whether there was another Activity
@@ -4961,7 +4914,7 @@
      * @param intent The intent to start.
      * @param requestCode Reply request code.  < 0 if reply is not requested.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @throws android.content.ActivityNotFoundException
@@ -5014,7 +4967,7 @@
      * @param intent The intent to start.
      * @param requestCode Reply request code.  < 0 if reply is not requested.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @throws android.content.ActivityNotFoundException
@@ -6839,6 +6792,8 @@
         }
         mWindowManager = mWindow.getWindowManager();
         mCurrentConfig = config;
+
+        mWindow.setColorMode(info.colorMode);
     }
 
     /** @hide */
@@ -7206,7 +7161,7 @@
      *
      * @return True if caption is displayed on content, false if it pushes the content down.
      *
-     * @see {@link #setOverlayWithDecorCaptionEnabled(boolean)}
+     * @see #setOverlayWithDecorCaptionEnabled(boolean)
      */
     public boolean isOverlayWithDecorCaptionEnabled() {
         return mWindow.isOverlayWithDecorCaptionEnabled();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f1f6e7b..b367d0c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -133,7 +133,7 @@
     public final static boolean ENABLE_TASK_SNAPSHOTS;
 
     static {
-        ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", false);
+        ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", true);
     }
 
     static final class UidObserver extends IUidObserver.Stub {
@@ -520,15 +520,19 @@
     /** @hide Flag for registerUidObserver: report uid has become active. */
     public static final int UID_OBSERVER_ACTIVE = 1<<3;
 
-    /** @hide Mode for {@link IActivityManager#getAppStartMode}: normal free-to-run operation. */
+    /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */
     public static final int APP_START_MODE_NORMAL = 0;
 
-    /** @hide Mode for {@link IActivityManager#getAppStartMode}: delay running until later. */
+    /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: delay running until later. */
     public static final int APP_START_MODE_DELAYED = 1;
 
-    /** @hide Mode for {@link IActivityManager#getAppStartMode}: disable/cancel pending
-     * launches. */
-    public static final int APP_START_MODE_DISABLED = 2;
+    /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: delay running until later, with
+     * rigid errors (throwing exception). */
+    public static final int APP_START_MODE_DELAYED_RIGID = 2;
+
+    /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: disable/cancel pending
+     * launches; this is the mode for ephemeral apps. */
+    public static final int APP_START_MODE_DISABLED = 3;
 
     /**
      * Lock task mode is not active.
@@ -694,6 +698,13 @@
         }
 
         /**
+         * Returns true if the input stack is affected by drag resizing.
+         */
+        public static boolean isStackAffectedByDragResizing(int stackId) {
+            return isStaticStack(stackId) && stackId != PINNED_STACK_ID;
+        }
+
+        /**
          * Returns true if the windows of tasks being moved to the target stack from the source
          * stack should be replaced, meaning that window manager will keep the old window around
          * until the new is ready.
@@ -1478,7 +1489,7 @@
          * True if the task can go in the docked stack.
          * @hide
          */
-        public boolean isDockable;
+        public boolean supportsSplitScreenMultiWindow;
 
         /**
          * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
@@ -1529,7 +1540,7 @@
             } else {
                 dest.writeInt(0);
             }
-            dest.writeInt(isDockable ? 1 : 0);
+            dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
             dest.writeInt(resizeMode);
         }
 
@@ -1553,7 +1564,7 @@
             numActivities = source.readInt();
             bounds = source.readInt() > 0 ?
                     Rect.CREATOR.createFromParcel(source) : null;
-            isDockable = source.readInt() == 1;
+            supportsSplitScreenMultiWindow = source.readInt() == 1;
             resizeMode = source.readInt();
         }
 
@@ -1741,7 +1752,7 @@
          * True if the task can go in the docked stack.
          * @hide
          */
-        public boolean isDockable;
+        public boolean supportsSplitScreenMultiWindow;
 
         /**
          * The resize mode of the task. See {@link ActivityInfo#resizeMode}.
@@ -1771,7 +1782,7 @@
                     Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
             dest.writeInt(numActivities);
             dest.writeInt(numRunning);
-            dest.writeInt(isDockable ? 1 : 0);
+            dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
             dest.writeInt(resizeMode);
         }
 
@@ -1788,7 +1799,7 @@
             description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
             numActivities = source.readInt();
             numRunning = source.readInt();
-            isDockable = source.readInt() != 0;
+            supportsSplitScreenMultiWindow = source.readInt() != 0;
             resizeMode = source.readInt();
         }
 
@@ -2176,6 +2187,12 @@
             dest.writeParcelable(mContentInsets, 0);
         }
 
+        @Override
+        public String toString() {
+            return "TaskSnapshot{mSnapshot=" + mSnapshot + " mOrientation=" + mOrientation
+                    + " mContentInsets=" + mContentInsets.toShortString();
+        }
+
         public static final Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() {
             public TaskSnapshot createFromParcel(Parcel source) {
                 return new TaskSnapshot(source);
@@ -2323,13 +2340,13 @@
         public static final int FLAG_FOREGROUND = 1<<1;
 
         /**
-         * Bit for {@link #flags): set if the service is running in a
+         * Bit for {@link #flags}: set if the service is running in a
          * core system process.
          */
         public static final int FLAG_SYSTEM_PROCESS = 1<<2;
 
         /**
-         * Bit for {@link #flags): set if the service is running in a
+         * Bit for {@link #flags}: set if the service is running in a
          * persistent process.
          */
         public static final int FLAG_PERSISTENT_PROCESS = 1<<3;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 87700dc..89510d9 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -64,36 +64,6 @@
     public static final int APP_TRANSITION_TIMEOUT = 3;
 
     /**
-     * Class to hold deferred properties to apply for picture-in-picture for a given activity.
-     */
-    public static class PictureInPictureArguments {
-        /**
-         * The expected aspect ratio of the picture-in-picture.
-         */
-        public float aspectRatio;
-
-        /**
-         * The set of actions that are associated with this activity when in picture in picture.
-         */
-        public List<RemoteAction> userActions = new ArrayList<>();
-
-        public void dump(PrintWriter pw, String prefix) {
-            pw.println(prefix + "aspectRatio=" + aspectRatio);
-            if (userActions.isEmpty()) {
-                pw.println(prefix + "  userActions=[]");
-            } else {
-                pw.println(prefix + "  userActions=[");
-                for (int i = 0; i < userActions.size(); i++) {
-                    RemoteAction action = userActions.get(i);
-                    pw.print(prefix + "    Action[" + i + "]: ");
-                    action.dump("", pw);
-                }
-                pw.println(prefix + "  ]");
-            }
-        }
-    }
-
-    /**
      * Grant Uri permissions from one app to another. This method only extends
      * permission grants if {@code callingUid} has permission to them.
      */
@@ -201,6 +171,17 @@
     public abstract void setPendingIntentWhitelistDuration(IIntentSender target, long duration);
 
     /**
+     * Allow DeviceIdleController to tell us about what apps are whitelisted.
+     */
+    public abstract void setDeviceIdleWhitelist(int[] appids);
+
+    /**
+     * Update information about which app IDs are on the temp whitelist.
+     */
+    public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId,
+            boolean adding);
+
+    /**
      * Updates and persists the {@link Configuration} for a given user.
      *
      * @param values the configuration to update
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0b3ae3a..34eaa0b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -245,7 +245,7 @@
     boolean mSomeActivitiesChanged = false;
     boolean mUpdatingSystemConfig = false;
 
-    // These can be accessed by multiple threads; mPackages is the lock.
+    // These can be accessed by multiple threads; mResourcesManager is the lock.
     // XXX For now we keep around information about all packages we have
     // seen, not removing entries from this map.
     // NOTE: The activity and window managers need to call in to
@@ -254,12 +254,13 @@
     // holds their own lock.  Thus you MUST NEVER call back into the activity manager
     // or window manager or anything that depends on them while holding this lock.
     // These LoadedApk are only valid for the userId that we're running as.
-    final ArrayMap<String, WeakReference<LoadedApk>> mPackages
-            = new ArrayMap<String, WeakReference<LoadedApk>>();
-    final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages
-            = new ArrayMap<String, WeakReference<LoadedApk>>();
-    final ArrayList<ActivityClientRecord> mRelaunchingActivities
-            = new ArrayList<ActivityClientRecord>();
+    @GuardedBy("mResourcesManager")
+    final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
+    @GuardedBy("mResourcesManager")
+    final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
+    @GuardedBy("mResourcesManager")
+    final ArrayList<ActivityClientRecord> mRelaunchingActivities = new ArrayList<>();
+    @GuardedBy("mResourcesManager")
     Configuration mPendingConfiguration = null;
     // Because we merge activity relaunch operations we can't depend on the ordering provided by
     // the handler messages. We need to introduce secondary ordering mechanism, which will allow
@@ -904,6 +905,10 @@
             sendMessage(H.CONFIGURATION_CHANGED, config);
         }
 
+        public void scheduleApplicationInfoChanged(ApplicationInfo ai) {
+            sendMessage(H.APPLICATION_INFO_CHANGED, ai);
+        }
+
         public void updateTimeZone() {
             TimeZone.setDefault(null);
         }
@@ -1448,6 +1453,7 @@
         public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
         public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
         public static final int ATTACH_AGENT = 155;
+        public static final int APPLICATION_INFO_CHANGED = 156;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1505,6 +1511,7 @@
                     case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
                     case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
                     case ATTACH_AGENT: return "ATTACH_AGENT";
+                    case APPLICATION_INFO_CHANGED: return "APPLICATION_INFO_CHANGED";
                 }
             }
             return Integer.toString(code);
@@ -1636,8 +1643,11 @@
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
                     mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
                     mUpdatingSystemConfig = true;
-                    handleConfigurationChanged((Configuration)msg.obj, null);
-                    mUpdatingSystemConfig = false;
+                    try {
+                        handleConfigurationChanged((Configuration) msg.obj, null);
+                    } finally {
+                        mUpdatingSystemConfig = false;
+                    }
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case CLEAN_UP_CONTEXT:
@@ -1763,6 +1773,14 @@
                 case ATTACH_AGENT:
                     handleAttachAgent((String) msg.obj);
                     break;
+                case APPLICATION_INFO_CHANGED:
+                    mUpdatingSystemConfig = true;
+                    try {
+                        handleApplicationInfoChanged((ApplicationInfo) msg.obj);
+                    } finally {
+                        mUpdatingSystemConfig = false;
+                    }
+                    break;
             }
             Object obj = msg.obj;
             if (obj instanceof SomeArgs) {
@@ -2598,9 +2616,10 @@
                     r.activityInfo.targetActivity);
         }
 
+        ContextImpl appContext = createBaseContextForActivity(r);
         Activity activity = null;
         try {
-            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
+            java.lang.ClassLoader cl = appContext.getClassLoader();
             activity = mInstrumentation.newActivity(
                     cl, component.getClassName(), r.intent);
             StrictMode.incrementExpectedActivityCount(activity.getClass());
@@ -2629,7 +2648,6 @@
                     + ", dir=" + r.packageInfo.getAppDir());
 
             if (activity != null) {
-                Context appContext = createBaseContextForActivity(r, activity);
                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                 Configuration config = new Configuration(mCompatConfiguration);
                 if (r.overrideConfig != null) {
@@ -2643,6 +2661,7 @@
                     r.mPendingRemoveWindow = null;
                     r.mPendingRemoveWindowManager = null;
                 }
+                appContext.setOuterContext(activity);
                 activity.attach(appContext, this, getInstrumentation(), r.token,
                         r.ident, app, r.intent, r.activityInfo, title, r.parent,
                         r.embeddedID, r.lastNonConfigurationInstances, config,
@@ -2718,8 +2737,8 @@
         return activity;
     }
 
-    private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
-        int displayId = Display.DEFAULT_DISPLAY;
+    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
+        final int displayId;
         try {
             displayId = ActivityManager.getService().getActivityDisplayId(r.token);
         } catch (RemoteException e) {
@@ -2727,9 +2746,7 @@
         }
 
         ContextImpl appContext = ContextImpl.createActivityContext(
-                this, r.packageInfo, r.token, displayId, r.overrideConfig);
-        appContext.setOuterContext(activity);
-        Context baseContext = appContext;
+                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
 
         final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
         // For debugging purposes, if the activity's package name contains the value of
@@ -2742,12 +2759,12 @@
                 if (id != Display.DEFAULT_DISPLAY) {
                     Display display =
                             dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
-                    baseContext = appContext.createDisplayContext(display);
+                    appContext = (ContextImpl) appContext.createDisplayContext(display);
                     break;
                 }
             }
         }
-        return baseContext;
+        return appContext;
     }
 
     private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
@@ -3101,9 +3118,16 @@
 
         IActivityManager mgr = ActivityManager.getService();
 
+        Application app;
         BroadcastReceiver receiver;
+        ContextImpl context;
         try {
-            java.lang.ClassLoader cl = packageInfo.getClassLoader();
+            app = packageInfo.makeApplication(false, mInstrumentation);
+            context = (ContextImpl) app.getBaseContext();
+            if (data.info.splitName != null) {
+                context = (ContextImpl) context.createContextForSplit(data.info.splitName);
+            }
+            java.lang.ClassLoader cl = context.getClassLoader();
             data.intent.setExtrasClassLoader(cl);
             data.intent.prepareToEnterProcess();
             data.setExtrasClassLoader(cl);
@@ -3118,8 +3142,6 @@
         }
 
         try {
-            Application app = packageInfo.makeApplication(false, mInstrumentation);
-
             if (localLOGV) Slog.v(
                 TAG, "Performing receive of " + data.intent
                 + ": app=" + app
@@ -3128,7 +3150,6 @@
                 + ", comp=" + data.intent.getComponent().toShortString()
                 + ", dir=" + packageInfo.getAppDir());
 
-            ContextImpl context = (ContextImpl)app.getBaseContext();
             sCurrentBroadcastIntent.set(data.intent);
             receiver.setPendingResult(data);
             receiver.onReceive(context.getReceiverRestrictedContext(),
@@ -3500,15 +3521,17 @@
                 }
                 r.activity.performResume();
 
-                // If there is a pending local relaunch that was requested when the activity was
-                // paused, it will put the activity into paused state when it finally happens.
-                // Since the activity resumed before being relaunched, we don't want that to happen,
-                // so we need to clear the request to relaunch paused.
-                for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
-                    final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
-                    if (relaunching.token == r.token
-                            && relaunching.onlyLocalRequest && relaunching.startsNotResumed) {
-                        relaunching.startsNotResumed = false;
+                synchronized (mResourcesManager) {
+                    // If there is a pending local relaunch that was requested when the activity was
+                    // paused, it will put the activity into paused state when it finally happens.
+                    // Since the activity resumed before being relaunched, we don't want that to
+                    // happen, so we need to clear the request to relaunch paused.
+                    for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
+                        final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
+                        if (relaunching.token == r.token
+                                && relaunching.onlyLocalRequest && relaunching.startsNotResumed) {
+                            relaunching.startsNotResumed = false;
+                        }
                     }
                 }
 
@@ -4898,6 +4921,53 @@
         }
     }
 
+    void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
+        // Updates triggered by package installation go through a package update
+        // receiver. Here we try to capture ApplicationInfo changes that are
+        // caused by other sources, such as overlays. That means we want to be as conservative
+        // about code changes as possible. Take the diff of the old ApplicationInfo and the new
+        // to see if anything needs to change.
+        synchronized (mResourcesManager) {
+            // Update all affected loaded packages with new package information
+            WeakReference<LoadedApk> ref = mPackages.get(ai.packageName);
+            LoadedApk apk = ref != null ? ref.get() : null;
+            if (apk != null) {
+                final ArrayList<String> oldPaths = new ArrayList<>();
+                LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths, null /*outLibPaths*/);
+                apk.updateApplicationInfo(ai, oldPaths);
+            }
+
+            ref = mResourcePackages.get(ai.packageName);
+            apk = ref != null ? ref.get() : null;
+            if (apk != null) {
+                final ArrayList<String> oldPaths = new ArrayList<>();
+                LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths, null /*outLibPaths*/);
+                apk.updateApplicationInfo(ai, oldPaths);
+            }
+
+            // Update all affected Resources objects to use new ResourcesImpl
+            mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs);
+        }
+
+        ApplicationPackageManager.configurationChanged();
+
+        // Trigger a regular Configuration change event, only with a different assetsSeq number
+        // so that we actually call through to all components.
+        Configuration newConfig = new Configuration();
+        newConfig.unset();
+        newConfig.assetsSeq = mConfiguration.assetsSeq + 1;
+        handleConfigurationChanged(newConfig, null);
+
+        // Schedule all activities to reload
+        for (final Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
+            final Activity activity = entry.getValue().activity;
+            if (!activity.mFinished) {
+                requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
+                        false);
+            }
+        }
+    }
+
     static void freeTextLayoutCachesIfNeeded(int configDiff) {
         if (configDiff != 0) {
             // Ask text layout engine to free its caches if there is a locale change
@@ -5964,6 +6034,15 @@
                       info.name);
                 return null;
             }
+
+            if (info.splitName != null) {
+                try {
+                    c = c.createContextForSplit(info.splitName);
+                } catch (NameNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
             try {
                 final java.lang.ClassLoader cl = c.getClassLoader();
                 localProvider = (ContentProvider)cl.
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 67fbc5a..603126b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -243,8 +243,12 @@
     public static final int OP_AUDIO_ACCESSIBILITY_VOLUME = 64;
     /** @hide Read the phone number. */
     public static final int OP_READ_PHONE_NUMBER = 65;
+    /** @hide Request package installs through package installer */
+    public static final int OP_REQUEST_INSTALL_PACKAGES = 66;
+    /** @hide Enter picture-in-picture when hidden. */
+    public static final int OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE = 67;
     /** @hide */
-    public static final int _NUM_OP = 66;
+    public static final int _NUM_OP = 68;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -461,6 +465,8 @@
             OP_RUN_IN_BACKGROUND,
             OP_AUDIO_ACCESSIBILITY_VOLUME,
             OP_READ_PHONE_NUMBER,
+            OP_REQUEST_INSTALL_PACKAGES,
+            OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE,
     };
 
     /**
@@ -534,6 +540,8 @@
             null,
             null, // OP_AUDIO_ACCESSIBILITY_VOLUME
             OPSTR_READ_PHONE_NUMBER,
+            null, // OP_REQUEST_INSTALL_PACKAGES
+            null,
     };
 
     /**
@@ -607,6 +615,8 @@
             "RUN_IN_BACKGROUND",
             "AUDIO_ACCESSIBILITY_VOLUME",
             "READ_PHONE_NUMBER",
+            "REQUEST_INSTALL_PACKAGES",
+            "OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE",
     };
 
     /**
@@ -680,6 +690,8 @@
             null, // no permission for running in background
             null, // no permission for changing accessibility volume
             Manifest.permission.READ_PHONE_NUMBER,
+            Manifest.permission.REQUEST_INSTALL_PACKAGES,
+            null, // no permission for entering picture-in-picture on hide
     };
 
     /**
@@ -754,6 +766,8 @@
             null, // RUN_IN_BACKGROUND
             UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_ACCESSIBILITY_VOLUME
             null, // READ_PHONE_NUMBER
+            null, // REQUEST_INSTALL_PACKAGES
+            null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
     };
 
     /**
@@ -827,6 +841,8 @@
             false, // RUN_IN_BACKGROUND
             false, // AUDIO_ACCESSIBILITY_VOLUME
             false, // READ_PHONE_NUMBER
+            false, // REQUEST_INSTALL_PACKAGES
+            false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
     };
 
     /**
@@ -899,6 +915,8 @@
             AppOpsManager.MODE_ALLOWED,  // OP_RUN_IN_BACKGROUND
             AppOpsManager.MODE_ALLOWED,  // OP_AUDIO_ACCESSIBILITY_VOLUME
             AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_DEFAULT, // OP_REQUEST_INSTALL_PACKAGES
+            AppOpsManager.MODE_ALLOWED,  // OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE
     };
 
     /**
@@ -975,6 +993,8 @@
             false,
             false, // OP_AUDIO_ACCESSIBILITY_VOLUME
             false,
+            false, // OP_REQUEST_INSTALL_PACKAGES
+            false, // OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f3185a8..9f2fffd 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -17,13 +17,14 @@
 package android.app;
 
 import android.annotation.DrawableRes;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.XmlRes;
-import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
@@ -52,13 +53,13 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VersionedPackage;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -136,6 +137,21 @@
     }
 
     @Override
+    public PackageInfo getPackageInfo(VersionedPackage versionedPackage, int flags)
+            throws NameNotFoundException {
+        try {
+            PackageInfo pi = mPM.getPackageInfoVersioned(versionedPackage, flags,
+                    mContext.getUserId());
+            if (pi != null) {
+                return pi;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        throw new NameNotFoundException(versionedPackage.toString());
+    }
+
+    @Override
     public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
         try {
@@ -146,7 +162,6 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-
         throw new NameNotFoundException(packageName);
     }
 
@@ -444,6 +459,28 @@
 
     /** @hide */
     @Override
+    public @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags) {
+        return getSharedLibrariesAsUser(flags, mContext.getUserId());
+    }
+
+    /** @hide */
+    @Override
+    @SuppressWarnings("unchecked")
+    public @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(int flags, int userId) {
+        try {
+            ParceledListSlice<SharedLibraryInfo> sharedLibs = mPM.getSharedLibraries(
+                    flags, userId);
+            if (sharedLibs == null) {
+                return Collections.emptyList();
+            }
+            return sharedLibs.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    @Override
     public @NonNull String getServicesSystemSharedLibraryPackageName() {
         try {
             return mPM.getServicesSystemSharedLibraryPackageName();
@@ -1386,7 +1423,7 @@
         }
     }
 
-    ApplicationPackageManager(ContextImpl context,
+    protected ApplicationPackageManager(ContextImpl context,
                               IPackageManager pm) {
         mContext = context;
         mPM = pm;
@@ -1820,6 +1857,12 @@
     @Override
     public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) {
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        return getPackageCurrentVolume(app, storage);
+    }
+
+    @VisibleForTesting
+    protected @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app,
+            StorageManager storage) {
         if (app.isInternal()) {
             return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
         } else if (app.isExternalAsec()) {
@@ -1831,25 +1874,43 @@
 
     @Override
     public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) {
-        final StorageManager storage = mContext.getSystemService(StorageManager.class);
-        final VolumeInfo currentVol = getPackageCurrentVolume(app);
-        final List<VolumeInfo> vols = storage.getVolumes();
+        final StorageManager storageManager = mContext.getSystemService(StorageManager.class);
+        return getPackageCandidateVolumes(app, storageManager, mPM);
+    }
+
+    @VisibleForTesting
+    protected @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app,
+            StorageManager storageManager, IPackageManager pm) {
+        final VolumeInfo currentVol = getPackageCurrentVolume(app, storageManager);
+        final List<VolumeInfo> vols = storageManager.getVolumes();
         final List<VolumeInfo> candidates = new ArrayList<>();
         for (VolumeInfo vol : vols) {
-            if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(mContext, app, vol)) {
+            if (Objects.equals(vol, currentVol)
+                    || isPackageCandidateVolume(mContext, app, vol, pm)) {
                 candidates.add(vol);
             }
         }
         return candidates;
     }
 
-    private boolean isPackageCandidateVolume(
-            ContextImpl context, ApplicationInfo app, VolumeInfo vol) {
-        final boolean forceAllowOnExternal = Settings.Global.getInt(
+    @VisibleForTesting
+    protected boolean isForceAllowOnExternal(Context context) {
+        return Settings.Global.getInt(
                 context.getContentResolver(), Settings.Global.FORCE_ALLOW_ON_EXTERNAL, 0) != 0;
-        // Private internal is always an option
+    }
+
+    @VisibleForTesting
+    protected boolean isAllow3rdPartyOnInternal(Context context) {
+        return context.getResources().getBoolean(
+                com.android.internal.R.bool.config_allow3rdPartyAppOnInternal);
+    }
+
+    private boolean isPackageCandidateVolume(
+            ContextImpl context, ApplicationInfo app, VolumeInfo vol, IPackageManager pm) {
+        final boolean forceAllowOnExternal = isForceAllowOnExternal(context);
+
         if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
-            return true;
+            return app.isSystemApp() || isAllow3rdPartyOnInternal(context);
         }
 
         // System apps and apps demanding internal storage can't be moved
@@ -1875,7 +1936,7 @@
 
         // Some apps can't be moved. (e.g. device admins)
         try {
-            if (mPM.isPackageDeviceAdminOnAnyUser(app.packageName)) {
+            if (pm.isPackageDeviceAdminOnAnyUser(app.packageName)) {
                 return false;
             }
         } catch (RemoteException e) {
@@ -1952,10 +2013,11 @@
     }
 
     @Override
-    public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
-            int userId) {
+    public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer,
+            int flags, int userId) {
         try {
-            mPM.deletePackageAsUser(packageName, observer, userId, flags);
+            mPM.deletePackageAsUser(packageName, PackageManager.VERSION_CODE_HIGHEST,
+                    observer, userId, flags);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2278,7 +2340,7 @@
         synchronized (mLock) {
             if (mInstaller == null) {
                 try {
-                    mInstaller = new PackageInstaller(mContext, this, mPM.getPackageInstaller(),
+                    mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
                             mContext.getPackageName(), mContext.getUserId());
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
@@ -2518,4 +2580,13 @@
             return false;
         }
     }
+
+    @Override
+    public boolean canRequestPackageInstalls() {
+        try {
+            return mPM.canRequestPackageInstalls(mContext.getPackageName(), mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index d08bee5..9db2b92 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -33,6 +32,7 @@
 import android.content.ReceiverCallNotAllowedException;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
@@ -59,21 +59,27 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.IStorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
+import dalvik.system.PathClassLoader;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -81,6 +87,8 @@
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Objects;
 
 class ReceiverRestrictedContext extends ContextWrapper {
@@ -148,6 +156,7 @@
 
     final ActivityThread mMainThread;
     final LoadedApk mPackageInfo;
+    private ClassLoader mClassLoader;
 
     private final IBinder mActivityToken;
 
@@ -273,8 +282,7 @@
 
     @Override
     public ClassLoader getClassLoader() {
-        return mPackageInfo != null ?
-                mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader();
+        return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
     }
 
     @Override
@@ -1342,8 +1350,8 @@
         }
         try {
             final Intent intent = ActivityManager.getService().registerReceiver(
-                    mMainThread.getApplicationThread(), mBasePackageName,
-                    rd, filter, broadcastPermission, userId);
+                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
+                    broadcastPermission, userId);
             if (intent != null) {
                 intent.setExtrasClassLoader(getClassLoader());
                 intent.prepareToEnterProcess();
@@ -1385,7 +1393,14 @@
     @Override
     public ComponentName startService(Intent service) {
         warnIfCallingFromSystemProcess();
-        return startServiceCommon(service, mUser);
+        return startServiceCommon(service, -1, null, mUser);
+    }
+
+    @Override
+    public ComponentName startServiceInForeground(Intent service,
+            int id, Notification notification) {
+        warnIfCallingFromSystemProcess();
+        return startServiceCommon(service, id, notification, mUser);
     }
 
     @Override
@@ -1396,16 +1411,24 @@
 
     @Override
     public ComponentName startServiceAsUser(Intent service, UserHandle user) {
-        return startServiceCommon(service, user);
+        return startServiceCommon(service, -1, null, user);
     }
 
-    private ComponentName startServiceCommon(Intent service, UserHandle user) {
+    @Override
+    public ComponentName startServiceInForegroundAsUser(Intent service,
+            int id, Notification notification, UserHandle user) {
+        return startServiceCommon(service, id, notification, user);
+    }
+
+    private ComponentName startServiceCommon(Intent service, int id, Notification notification,
+            UserHandle user) {
         try {
             validateServiceIntent(service);
             service.prepareToLeaveProcess(this);
             ComponentName cn = ActivityManager.getService().startService(
                 mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
-                            getContentResolver()), getOpPackageName(), user.getIdentifier());
+                            getContentResolver()), id, notification, getOpPackageName(),
+                            user.getIdentifier());
             if (cn != null) {
                 if (cn.getPackageName().equals("!")) {
                     throw new SecurityException(
@@ -1415,6 +1438,9 @@
                     throw new SecurityException(
                             "Unable to start service " + service
                             + ": " + cn.getClassName());
+                } else if (cn.getPackageName().equals("?")) {
+                    throw new IllegalStateException(
+                            "Not allowed to start service " + service + ": " + cn.getClassName());
                 }
             }
             return cn;
@@ -1573,9 +1599,20 @@
             throw new IllegalArgumentException("permission is null");
         }
 
+        final IActivityManager am = ActivityManager.getService();
+        if (am == null) {
+            // Well this is super awkward; we somehow don't have an active
+            // ActivityManager instance. If we're testing a root or system
+            // UID, then they totally have whatever permission this is.
+            final int appId = UserHandle.getAppId(uid);
+            if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
+                Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
+                return PackageManager.PERMISSION_GRANTED;
+            }
+        }
+
         try {
-            return ActivityManager.getService().checkPermission(
-                    permission, pid, uid);
+            return am.checkPermission(permission, pid, uid);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1859,7 +1896,7 @@
                 pi.getResDir(),
                 pi.getSplitResDirs(),
                 pi.getOverlayDirs(),
-                pi.getApplicationInfo().sharedLibraryFiles,
+                pi.getSharedLibraries(),
                 displayId,
                 overrideConfig,
                 compatInfo,
@@ -1873,7 +1910,8 @@
                 flags | CONTEXT_REGISTER_PACKAGE);
         if (pi != null) {
             ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
-                    new UserHandle(UserHandle.getUserId(application.uid)), flags);
+                    new UserHandle(UserHandle.getUserId(application.uid)), flags,
+                    null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
@@ -1902,13 +1940,15 @@
         if (packageName.equals("system") || packageName.equals("android")) {
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
-            return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, user, flags);
+            return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, user, flags,
+                    null);
         }
 
         LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
         if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, user, flags);
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, user, flags,
+                    null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
@@ -1926,13 +1966,42 @@
     }
 
     @Override
+    public Context createContextForSplit(String splitName) throws NameNotFoundException {
+        if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+            // All Splits are always loaded.
+            return this;
+        }
+
+        final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
+        final String[] paths = mPackageInfo.getSplitPaths(splitName);
+
+        final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
+                mActivityToken, mUser, mFlags, classLoader);
+
+        final int displayId = mDisplay != null
+                ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+
+        context.mResources = ResourcesManager.getInstance().getResources(
+                mActivityToken,
+                mPackageInfo.getResDir(),
+                paths,
+                mPackageInfo.getOverlayDirs(),
+                mPackageInfo.getApplicationInfo().sharedLibraryFiles,
+                displayId,
+                null,
+                mPackageInfo.getCompatibilityInfo(),
+                classLoader);
+        return context;
+    }
+
+    @Override
     public Context createConfigurationContext(Configuration overrideConfiguration) {
         if (overrideConfiguration == null) {
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
-                mUser, mFlags);
+                mUser, mFlags, mClassLoader);
 
         final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
         context.mResources = createResources(mActivityToken, mPackageInfo, displayId,
@@ -1947,7 +2016,7 @@
         }
 
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
-                mUser, mFlags);
+                mUser, mFlags, mClassLoader);
 
         final int displayId = display.getDisplayId();
         context.mResources = createResources(mActivityToken, mPackageInfo, displayId, null,
@@ -1960,14 +2029,16 @@
     public Context createDeviceProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags);
+        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags,
+                mClassLoader);
     }
 
     @Override
     public Context createCredentialProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags);
+        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags,
+                mClassLoader);
     }
 
     @Override
@@ -2054,8 +2125,9 @@
 
     static ContextImpl createSystemContext(ActivityThread mainThread) {
         LoadedApk packageInfo = new LoadedApk(mainThread);
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0);
-        context.mResources = packageInfo.getResources(mainThread);
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0,
+                null);
+        context.mResources = packageInfo.getResources();
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                 context.mResourcesManager.getDisplayMetrics());
         return context;
@@ -2063,18 +2135,35 @@
 
     static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0);
-        context.mResources = packageInfo.getResources(mainThread);
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0,
+                null);
+        context.mResources = packageInfo.getResources();
         return context;
     }
 
     static ContextImpl createActivityContext(ActivityThread mainThread,
-            LoadedApk packageInfo, IBinder activityToken, int displayId,
+            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
             Configuration overrideConfiguration) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
 
+        String[] splitDirs = packageInfo.getSplitResDirs();
+        ClassLoader classLoader = packageInfo.getClassLoader();
+
+        if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
+            try {
+                classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
+                splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
+            } catch (NameNotFoundException e) {
+                // Nothing above us can handle a NameNotFoundException, better crash.
+                throw new RuntimeException(e);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+            }
+        }
+
         ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityToken, null,
-                0);
+                0, classLoader);
 
         // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
         displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
@@ -2089,20 +2178,21 @@
         // will be rebased upon.
         context.mResources = resourcesManager.createBaseActivityResources(activityToken,
                 packageInfo.getResDir(),
-                packageInfo.getSplitResDirs(),
+                splitDirs,
                 packageInfo.getOverlayDirs(),
-                packageInfo.getApplicationInfo().sharedLibraryFiles,
+                packageInfo.getSharedLibraries(),
                 displayId,
                 overrideConfiguration,
                 compatInfo,
-                packageInfo.getClassLoader());
+                classLoader);
         context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
                 context.mResources.getDisplayAdjustments());
         return context;
     }
 
     private ContextImpl(ContextImpl container, ActivityThread mainThread,
-            LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags) {
+            LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,
+            ClassLoader classLoader) {
         mOuterContext = this;
 
         // If creator didn't specify which storage to use, use the default
@@ -2127,6 +2217,7 @@
         mUser = user;
 
         mPackageInfo = packageInfo;
+        mClassLoader = classLoader;
         mResourcesManager = ResourcesManager.getInstance();
 
         if (container != null) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 10ab2bc..73b96f1 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -102,15 +102,19 @@
         mSavedFragmentState = in.readBundle();
     }
 
-    public Fragment instantiate(FragmentHostCallback host, Fragment parent,
-            FragmentManagerNonConfig childNonConfig) {
+    public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
+            Fragment parent, FragmentManagerNonConfig childNonConfig) {
         if (mInstance == null) {
             final Context context = host.getContext();
             if (mArguments != null) {
                 mArguments.setClassLoader(context.getClassLoader());
             }
 
-            mInstance = Fragment.instantiate(context, mClassName, mArguments);
+            if (container != null) {
+                mInstance = container.instantiate(context, mClassName, mArguments);
+            } else {
+                mInstance = Fragment.instantiate(context, mClassName, mArguments);
+            }
 
             if (mSavedFragmentState != null) {
                 mSavedFragmentState.setClassLoader(context.getClassLoader());
@@ -1110,7 +1114,7 @@
      *
      * @param intent The intent to start.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      */
     public void startActivity(Intent intent, Bundle options) {
diff --git a/core/java/android/app/FragmentContainer.java b/core/java/android/app/FragmentContainer.java
index b2e0300..6ed54dc 100644
--- a/core/java/android/app/FragmentContainer.java
+++ b/core/java/android/app/FragmentContainer.java
@@ -18,6 +18,8 @@
 
 import android.annotation.IdRes;
 import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
 import android.view.View;
 
 /**
@@ -35,4 +37,13 @@
      * Return {@code true} if the container holds any view.
      */
     public abstract boolean onHasView();
+
+    /**
+     * Creates an instance of the specified fragment, can be overridden to construct fragments
+     * with dependencies, or change the fragment being constructed. By default just calls
+     * {@link Fragment#instantiate(Context, String, Bundle)}.
+     */
+    public Fragment instantiate(Context context, String className, Bundle arguments) {
+        return Fragment.instantiate(context, className, arguments);
+    }
 }
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index efd2b69..44f1322 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1984,11 +1984,13 @@
                 if (startIndex != recordNum) {
                     executeOpsTogether(records, isRecordPop, startIndex, recordNum);
                 }
-                // execute all unoptimized together
-                int optimizeEnd;
-                for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
-                    if (records.get(optimizeEnd).mAllowOptimization) {
-                        break;
+                // execute all unoptimized pop operations together or one add operation
+                int optimizeEnd = recordNum + 1;
+                if (isRecordPop.get(recordNum)) {
+                    while (optimizeEnd < numRecords
+                            && isRecordPop.get(optimizeEnd)
+                            && !records.get(optimizeEnd).mAllowOptimization) {
+                        optimizeEnd++;
                     }
                 }
                 executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
@@ -2651,7 +2653,7 @@
                 if (childNonConfigs != null && i < childNonConfigs.size()) {
                     childNonConfig = childNonConfigs.get(i);
                 }
-                Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
+                Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
                 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                 mActive.add(f);
                 // Now that the fragment is instantiated (or came from being
@@ -3270,7 +3272,7 @@
                 + Integer.toHexString(id) + " fname=" + fname
                 + " existing=" + fragment);
         if (fragment == null) {
-            fragment = Fragment.instantiate(context, fname);
+            fragment = mContainer.instantiate(context, fname, null);
             fragment.mFromLayout = true;
             fragment.mFragmentId = id != 0 ? id : containerId;
             fragment.mContainerId = containerId;
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index 6d57cd4..80a5aac 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -188,7 +188,10 @@
     private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
-        ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        ViewGroup sceneRoot = null;
+        if (fragmentManager.mContainer.onHasView()) {
+            sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        }
         if (sceneRoot == null) {
             return;
         }
@@ -257,7 +260,10 @@
     private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
-        ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        ViewGroup sceneRoot = null;
+        if (fragmentManager.mContainer.onHasView()) {
+            sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        }
         if (sceneRoot == null) {
             return;
         }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 21ae853..0a2f804 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -31,10 +31,10 @@
 import android.app.ITaskStackListener;
 import android.app.IUiAutomationConnection;
 import android.app.IUidObserver;
-
 import android.app.IUserSwitchObserver;
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.app.PictureInPictureArgs;
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
 import android.app.assist.AssistContent;
@@ -129,7 +129,8 @@
     void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
     PendingIntent getRunningServiceControlPanel(in ComponentName service);
     ComponentName startService(in IApplicationThread caller, in Intent service,
-            in String resolvedType, in String callingPackage, int userId);
+            in String resolvedType, int id, in Notification notification,
+            in String callingPackage, int userId);
     int stopService(in IApplicationThread caller, in Intent service,
             in String resolvedType, int userId);
     int bindService(in IApplicationThread caller, in IBinder token, in Intent service,
@@ -263,7 +264,7 @@
     boolean isImmersive(in IBinder token);
     void setImmersive(in IBinder token, boolean immersive);
     boolean isTopActivityImmersive();
-    void crashApplication(int uid, int initialPid, in String packageName, in String message);
+    void crashApplication(int uid, int initialPid, in String packageName, int userId, in String message);
     String getProviderMimeType(in Uri uri, int userId);
     IBinder newUriPermissionOwner(in String name);
     void grantUriPermissionFromOwner(in IBinder owner, int fromUid, in String targetPkg,
@@ -474,18 +475,14 @@
     void suppressResizeConfigChanges(boolean suppress);
     void moveTasksToFullscreenStack(int fromStackId, boolean onTop);
     boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
-    int getAppStartMode(int uid, in String packageName);
+    boolean isAppStartModeDisabled(int uid, in String packageName);
     boolean unlockUser(int userid, in byte[] token, in byte[] secret,
             in IProgressListener listener);
     boolean isInMultiWindowMode(in IBinder token);
     boolean isInPictureInPictureMode(in IBinder token);
     void killPackageDependents(in String packageName, int userId);
-    void enterPictureInPictureMode(in IBinder token);
-    void enterPictureInPictureModeWithAspectRatio(in IBinder token, float aspectRatio);
-    void enterPictureInPictureModeOnMoveToBackground(in IBinder token,
-            boolean enterPictureInPictureOnMoveToBg);
-    void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio);
-    void setPictureInPictureActions(in IBinder token, in ParceledListSlice actions);
+    boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureArgs args);
+    void setPictureInPictureArgs(in IBinder token, in PictureInPictureArgs args);
     void activityRelaunched(in IBinder token);
     IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
     /**
@@ -542,7 +539,7 @@
      */
     void swapDockedAndFullscreenStack();
     void notifyLockedProfile(int userId);
-    void startConfirmDeviceCredentialIntent(in Intent intent);
+    void startConfirmDeviceCredentialIntent(in Intent intent, in Bundle options);
     void sendIdleJobTrigger();
     int sendIntentSender(in IIntentSender target, int code, in Intent intent,
             in String resolvedType, in IIntentReceiver finishedReceiver,
@@ -602,6 +599,8 @@
      */
     ActivityManager.TaskSnapshot getTaskSnapshot(int taskId);
 
+    void scheduleApplicationInfoChanged(in List<String> packageNames, int userId);
+
     // WARNING: when these transactions are updated, check if they are any callers on the native
     // side. If so, make sure they are using the correct transaction ids and arguments.
     // If a transaction which will also be used on the native side is being inserted, add it
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 7f168c9..41d1255 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -152,4 +152,5 @@
             IVoiceInteractor voiceInteractor);
     void handleTrustStorageUpdate();
     void attachAgent(String path);
+    void scheduleApplicationInfoChanged(in ApplicationInfo ai);
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 07c21a5..58cd310 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -40,24 +40,29 @@
 {
     void cancelAllNotifications(String pkg, int userId);
 
+    void clearData(String pkg, int uid);
     void enqueueToast(String pkg, ITransientNotification callback, int duration);
     void cancelToast(String pkg, ITransientNotification callback);
     void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
             in Notification notification, inout int[] idReceived, int userId);
     void cancelNotificationWithTag(String pkg, String tag, int id, int userId);
 
+    void setShowBadge(String pkg, int uid, boolean showBadge);
+    boolean canShowBadge(String pkg, int uid);
     void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
     boolean areNotificationsEnabledForPackage(String pkg, int uid);
     boolean areNotificationsEnabled(String pkg);
     int getPackageImportance(String pkg);
 
+    void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
+    ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
     void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
     NotificationChannel getNotificationChannel(String pkg, String channelId);
-    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId);
+    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, String channelId);
     ParceledListSlice getNotificationChannels(String pkg);
-    ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid);
+    ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
@@ -72,8 +77,6 @@
 
     void snoozeNotificationUntilContextFromListener(in INotificationListener token, String key, String snoozeCriterionId);
     void snoozeNotificationUntilFromListener(in INotificationListener token, String key, long until);
-    void snoozeNotificationFromListener(in INotificationListener token, String key);
-    void unsnoozeNotificationFromListener(in INotificationListener token, String key);
 
     void requestBindListener(in ComponentName component);
     void requestUnbindListener(in INotificationListener token);
@@ -83,6 +86,7 @@
     void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);
 
     ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
+    ParceledListSlice getSnoozedNotificationsFromListener(in INotificationListener token, int trim);
     void requestHintsFromListener(in INotificationListener token, int hints);
     int getHintsFromListener(in INotificationListener token);
     void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
@@ -97,6 +101,7 @@
     void updateNotificationChannelFromAssistant(in INotificationListener token, String pkg, in NotificationChannel channel);
     void deleteNotificationChannelFromAssistant(in INotificationListener token, String pkg, String channelId);
     ParceledListSlice getNotificationChannelsFromAssistant(in INotificationListener token, String pkg);
+    void unsnoozeNotificationFromAssistant(in INotificationListener token, String key);
 
     ComponentName getEffectsSuppressor();
     boolean matchesCallFilter(in Bundle extras);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index ef997c9..5e420c0 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -31,8 +31,10 @@
      * Called whenever IActivityManager.startActivity is called on an activity that is already
      * running in the pinned stack and the activity is not actually started, but the task is either
      * brought to the front or a new Intent is delivered to it.
+     *
+     * @param sourceComponent the component name of the activity that initiated the restart attempt
      */
-    void onPinnedActivityRestartAttempt();
+    void onPinnedActivityRestartAttempt(in ComponentName sourceComponent);
 
     /**
      * Called whenever the pinned stack is done animating a resize.
@@ -102,4 +104,9 @@
      * been locked.
      */
     void onTaskProfileLocked(int taskId, int userId);
+
+    /**
+     * Called when a task snapshot got updated.
+     */
+    void onTaskSnapshotChanged(int taskId, in ActivityManager.TaskSnapshot snapshot);
 }
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 848464c..7f0b6fb 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -58,11 +58,16 @@
     void setTheme(String theme);
 
     /**
-     * Gets whith theme overlays to use within /vendor/overlay.
+     * Gets which theme overlays to use within /vendor/overlay.
      */
     String getTheme();
 
     /**
+     * Gets the themes available in /vendor/overlay.
+     */
+    String[] getAvailableThemes();
+
+    /**
      * Tells if UI mode is locked or not.
      */
     boolean isUiModeLocked();
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index cc7981c..b1bdea1 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -443,6 +443,7 @@
         private final String mClass;
         private final ActivityResult mResult;
         private final boolean mBlock;
+        private final boolean mIgnoreMatchingSpecificIntents;
 
 
         // This is protected by 'Instrumentation.this.mSync'.
@@ -454,7 +455,7 @@
         /**
          * Create a new ActivityMonitor that looks for a particular kind of 
          * intent to be started.
-         *  
+         *
          * @param which The set of intents this monitor is responsible for.
          * @param result A canned result to return if the monitor is hit; can 
          *               be null.
@@ -470,6 +471,7 @@
             mClass = null;
             mResult = result;
             mBlock = block;
+            mIgnoreMatchingSpecificIntents = false;
         }
 
         /**
@@ -491,6 +493,34 @@
             mClass = cls;
             mResult = result;
             mBlock = block;
+            mIgnoreMatchingSpecificIntents = false;
+        }
+
+        /**
+         * Create a new ActivityMonitor that can be used for intercepting any activity to be
+         * started.
+         *
+         * <p> When an activity is started, {@link #onMatchIntent(Intent)} will be called on
+         * instances created using this constructor to see if it is a hit.
+         *
+         * @see #onMatchIntent(Intent)
+         */
+        public ActivityMonitor() {
+            mWhich = null;
+            mClass = null;
+            mResult = null;
+            mBlock = false;
+            mIgnoreMatchingSpecificIntents = true;
+        }
+
+        /**
+         * @return true if this monitor is used for intercepting any started activity by calling
+         *         into {@link #onMatchIntent(Intent)}, false if this monitor is only used
+         *         for specific intents corresponding to the intent filter or activity class
+         *         passed in the constructor.
+         */
+        final boolean ignoreMatchingSpecificIntents() {
+            return mIgnoreMatchingSpecificIntents;
         }
 
         /**
@@ -577,10 +607,31 @@
                 }
             }
         }
-        
+
+        /**
+         * Used for intercepting any started activity.
+         *
+         * <p> A non-null return value here will be considered a hit for this monitor.
+         * By default this will return {@code null} and subclasses can override this to return
+         * a non-null value if the intent needs to be intercepted.
+         *
+         * <p> Whenever a new activity is started, this method will be called on instances created
+         * using {@link #Instrumentation.ActivityMonitor()} to check if there is a match. In case
+         * of a match, the activity start will be blocked and the returned result will be used.
+         *
+         * @param intent The intent used for starting the activity.
+         * @return The {@link ActivityResult} that needs to be used in case of a match.
+         */
+        public ActivityResult onMatchIntent(Intent intent) {
+            return null;
+        }
+
         final boolean match(Context who,
                             Activity activity,
                             Intent intent) {
+            if (mIgnoreMatchingSpecificIntents) {
+                return false;
+            }
             synchronized (this) {
                 if (mWhich != null
                     && mWhich.match(who.getContentResolver(), intent,
@@ -1492,7 +1543,14 @@
                 final int N = mActivityMonitors.size();
                 for (int i=0; i<N; i++) {
                     final ActivityMonitor am = mActivityMonitors.get(i);
-                    if (am.match(who, null, intent)) {
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        result = am.onMatchIntent(intent);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return result;
+                    } else if (am.match(who, null, intent)) {
                         am.mHits++;
                         if (am.isBlocking()) {
                             return requestCode >= 0 ? am.getResult() : null;
@@ -1548,7 +1606,14 @@
                 final int N = mActivityMonitors.size();
                 for (int i=0; i<N; i++) {
                     final ActivityMonitor am = mActivityMonitors.get(i);
-                    if (am.match(who, null, intents[0])) {
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        result = am.onMatchIntent(intents[0]);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return;
+                    } else if (am.match(who, null, intents[0])) {
                         am.mHits++;
                         if (am.isBlocking()) {
                             return;
@@ -1611,7 +1676,14 @@
                 final int N = mActivityMonitors.size();
                 for (int i=0; i<N; i++) {
                     final ActivityMonitor am = mActivityMonitors.get(i);
-                    if (am.match(who, null, intent)) {
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        result = am.onMatchIntent(intent);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return result;
+                    } else if (am.match(who, null, intent)) {
                         am.mHits++;
                         if (am.isBlocking()) {
                             return requestCode >= 0 ? am.getResult() : null;
@@ -1671,7 +1743,14 @@
                 final int N = mActivityMonitors.size();
                 for (int i=0; i<N; i++) {
                     final ActivityMonitor am = mActivityMonitors.get(i);
-                    if (am.match(who, null, intent)) {
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        result = am.onMatchIntent(intent);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return result;
+                    } else if (am.match(who, null, intent)) {
                         am.mHits++;
                         if (am.isBlocking()) {
                             return requestCode >= 0 ? am.getResult() : null;
@@ -1710,7 +1789,14 @@
                 final int N = mActivityMonitors.size();
                 for (int i=0; i<N; i++) {
                     final ActivityMonitor am = mActivityMonitors.get(i);
-                    if (am.match(who, null, intent)) {
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        result = am.onMatchIntent(intent);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return result;
+                    } else if (am.match(who, null, intent)) {
                         am.mHits++;
                         if (am.isBlocking()) {
                             return requestCode >= 0 ? am.getResult() : null;
@@ -1748,7 +1834,14 @@
                 final int N = mActivityMonitors.size();
                 for (int i=0; i<N; i++) {
                     final ActivityMonitor am = mActivityMonitors.get(i);
-                    if (am.match(who, null, intent)) {
+                    ActivityResult result = null;
+                    if (am.ignoreMatchingSpecificIntents()) {
+                        result = am.onMatchIntent(intent);
+                    }
+                    if (result != null) {
+                        am.mHits++;
+                        return;
+                    } else if (am.match(who, null, intent)) {
                         am.mHits++;
                         if (am.isBlocking()) {
                             return;
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 036b47c..c0bf0c4 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -24,25 +24,25 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.RemoteException;
 import android.os.IBinder;
-import android.os.IUserManager;
+import android.os.Looper;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.IWindowManager;
 import android.view.IOnKeyguardExitResult;
-import android.view.WindowManager;
+import android.view.IWindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
 
+import java.util.List;
+
 /**
  * Class that can be used to lock and unlock the keyboard. Get an instance of this
  * class by calling {@link android.content.Context#getSystemService(java.lang.String)}
@@ -100,12 +100,9 @@
         Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
         intent.putExtra(EXTRA_TITLE, title);
         intent.putExtra(EXTRA_DESCRIPTION, description);
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
-            intent.setPackage("com.google.android.apps.wearable.settings");
-        } else {
-            // For security reasons, only allow this to come from system settings.
-            intent.setPackage("com.android.settings");
-        }
+
+        // explicitly set the package for security
+        intent.setPackage(getSettingsPackageForIntent(intent));
         return intent;
     }
 
@@ -126,15 +123,23 @@
         intent.putExtra(EXTRA_TITLE, title);
         intent.putExtra(EXTRA_DESCRIPTION, description);
         intent.putExtra(Intent.EXTRA_USER_ID, userId);
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
-            intent.setPackage("com.google.android.apps.wearable.settings");
-        } else {
-            // For security reasons, only allow this to come from system settings.
-            intent.setPackage("com.android.settings");
-        }
+
+        // explicitly set the package for security
+        intent.setPackage(getSettingsPackageForIntent(intent));
+
         return intent;
     }
 
+    private String getSettingsPackageForIntent(Intent intent) {
+        List<ResolveInfo> resolveInfos = mContext.getPackageManager()
+                .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
+        for (int i = 0; i < resolveInfos.size(); i++) {
+            return resolveInfos.get(i).activityInfo.packageName;
+        }
+
+        return "com.android.settings";
+    }
+
     /**
      * @deprecated Use {@link LayoutParams#FLAG_DISMISS_KEYGUARD}
      * and/or {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED}
@@ -322,7 +327,7 @@
      * password.
      */
     public boolean isDeviceLocked() {
-        return isDeviceLocked(UserHandle.getCallingUserId());
+        return isDeviceLocked(UserHandle.myUserId());
     }
 
     /**
@@ -347,7 +352,7 @@
      * @return true if a PIN, pattern or password was set.
      */
     public boolean isDeviceSecure() {
-        return isDeviceSecure(UserHandle.getCallingUserId());
+        return isDeviceSecure(UserHandle.myUserId());
     }
 
     /**
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index db1162a..17f5edd 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -25,9 +27,11 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.split.SplitDependencyLoaderHelper;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -39,23 +43,22 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.ErrnoException;
 import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
+import com.android.internal.util.ArrayUtils;
+
 import dalvik.system.BaseDexClassLoader;
 import dalvik.system.VMRuntime;
 
 import java.io.File;
-import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
@@ -63,13 +66,12 @@
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Objects;
 
-import libcore.io.IoUtils;
-
 final class IntentReceiverLeaked extends AndroidRuntimeException {
     public IntentReceiverLeaked(String msg) {
         super(msg);
@@ -95,8 +97,6 @@
     private ApplicationInfo mApplicationInfo;
     private String mAppDir;
     private String mResDir;
-    private String[] mSplitAppDirs;
-    private String[] mSplitResDirs;
     private String[] mOverlayDirs;
     private String[] mSharedLibraries;
     private String mDataDir;
@@ -114,14 +114,18 @@
     private ClassLoader mClassLoader;
     private Application mApplication;
 
+    private String[] mSplitNames;
+    private String[] mSplitAppDirs;
+    private String[] mSplitResDirs;
+
     private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
-        = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
+        = new ArrayMap<>();
     private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
-        = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
+        = new ArrayMap<>();
     private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
-        = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
+        = new ArrayMap<>();
     private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
-        = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
+        = new ArrayMap<>();
 
     int mClientCount = 0;
 
@@ -262,7 +266,15 @@
         return ai.sharedLibraryFiles;
     }
 
-    public void updateApplicationInfo(ApplicationInfo aInfo, List<String> oldPaths) {
+    /**
+     * Update the ApplicationInfo for an app. If oldPaths is null, all the paths are considered
+     * new.
+     * @param aInfo The new ApplicationInfo to use for this LoadedApk
+     * @param oldPaths The code paths for the old ApplicationInfo object. null means no paths can
+     *                 be reused.
+     */
+    public void updateApplicationInfo(@NonNull ApplicationInfo aInfo,
+            @Nullable List<String> oldPaths) {
         setApplicationInfo(aInfo);
 
         final List<String> newPaths = new ArrayList<>();
@@ -290,9 +302,18 @@
         synchronized (this) {
             createOrUpdateClassLoaderLocked(addedPaths);
             if (mResources != null) {
-                mResources = mActivityThread.getTopLevelResources(mResDir, mSplitResDirs,
-                        mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
-                        this);
+                final String[] splitPaths;
+                try {
+                    splitPaths = getSplitPaths(null);
+                } catch (PackageManager.NameNotFoundException e) {
+                    // This should NEVER fail.
+                    throw new AssertionError("null split not found");
+                }
+
+                mResources = ResourcesManager.getInstance().getResources(null, mResDir,
+                        splitPaths, mOverlayDirs, mSharedLibraries,
+                        Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+                        getClassLoader());
             }
         }
     }
@@ -303,8 +324,6 @@
         mApplicationInfo = aInfo;
         mAppDir = aInfo.sourceDir;
         mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
-        mSplitAppDirs = aInfo.splitSourceDirs;
-        mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
         mOverlayDirs = aInfo.resourceDirs;
         mSharedLibraries = aInfo.sharedLibraryFiles;
         mDataDir = aInfo.dataDir;
@@ -312,19 +331,28 @@
         mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);
         mDeviceProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceProtectedDataDir);
         mCredentialProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialProtectedDataDir);
+
+        mSplitNames = aInfo.splitNames;
+        mSplitAppDirs = aInfo.splitSourceDirs;
+        mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
+
+        if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
+            mSplitLoader = new SplitDependencyLoader(aInfo.splitDependencies);
+        }
     }
 
     public static void makePaths(ActivityThread activityThread, ApplicationInfo aInfo,
             List<String> outZipPaths, List<String> outLibPaths) {
         final String appDir = aInfo.sourceDir;
-        final String[] splitAppDirs = aInfo.splitSourceDirs;
         final String libDir = aInfo.nativeLibraryDir;
         final String[] sharedLibraries = aInfo.sharedLibraryFiles;
 
         outZipPaths.clear();
         outZipPaths.add(appDir);
-        if (splitAppDirs != null) {
-            Collections.addAll(outZipPaths, splitAppDirs);
+
+        // Do not load all available splits if the app requested isolated split loading.
+        if (aInfo.splitSourceDirs != null && !aInfo.requestsIsolatedSplitLoading()) {
+            Collections.addAll(outZipPaths, aInfo.splitSourceDirs);
         }
 
         if (outLibPaths != null) {
@@ -357,13 +385,18 @@
                     || appDir.equals(instrumentedAppDir)) {
                 outZipPaths.clear();
                 outZipPaths.add(instrumentationAppDir);
-                if (instrumentationSplitAppDirs != null) {
-                    Collections.addAll(outZipPaths, instrumentationSplitAppDirs);
-                }
-                if (!instrumentationAppDir.equals(instrumentedAppDir)) {
-                    outZipPaths.add(instrumentedAppDir);
-                    if (instrumentedSplitAppDirs != null) {
-                        Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
+
+                // Only add splits if the app did not request isolated split loading.
+                if (!aInfo.requestsIsolatedSplitLoading()) {
+                    if (instrumentationSplitAppDirs != null) {
+                        Collections.addAll(outZipPaths, instrumentationSplitAppDirs);
+                    }
+
+                    if (!instrumentationAppDir.equals(instrumentedAppDir)) {
+                        outZipPaths.add(instrumentedAppDir);
+                        if (instrumentedSplitAppDirs != null) {
+                            Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
+                        }
                     }
                 }
 
@@ -389,7 +422,7 @@
             // will be added to zipPaths that shouldn't be part of the library path.
             if (aInfo.primaryCpuAbi != null) {
                 // Add fake libs into the library search path if we target prior to N.
-                if (aInfo.targetSdkVersion <= 23) {
+                if (aInfo.targetSdkVersion < Build.VERSION_CODES.N) {
                     outLibPaths.add("/system/fake-libs" +
                         (VMRuntime.is64BitAbi(aInfo.primaryCpuAbi) ? "64" : ""));
                 }
@@ -424,6 +457,116 @@
         }
     }
 
+    private class SplitDependencyLoader
+            extends SplitDependencyLoaderHelper<PackageManager.NameNotFoundException> {
+        private String[] mCachedBaseResourcePath;
+        private final String[][] mCachedResourcePaths;
+        private final ClassLoader[] mCachedSplitClassLoaders;
+
+        SplitDependencyLoader(SparseIntArray dependencies) {
+            super(dependencies);
+            mCachedResourcePaths = new String[mSplitNames.length][];
+            mCachedSplitClassLoaders = new ClassLoader[mSplitNames.length];
+        }
+
+        @Override
+        protected boolean isSplitCached(int splitIdx) {
+            if (splitIdx != -1) {
+                return mCachedSplitClassLoaders[splitIdx] != null;
+            }
+            return mClassLoader != null && mCachedBaseResourcePath != null;
+        }
+
+        private void addAllConfigSplits(String splitName, ArrayList<String> outAssetPaths) {
+            for (int i = 0; i < mSplitNames.length; i++) {
+                if (isConfigurationSplitOf(mSplitNames[i], splitName)) {
+                    outAssetPaths.add(mSplitResDirs[i]);
+                }
+            }
+        }
+
+        @Override
+        protected void constructSplit(int splitIdx, int parentSplitIdx) throws
+                PackageManager.NameNotFoundException {
+            final ArrayList<String> splitPaths = new ArrayList<>();
+            if (splitIdx == -1) {
+                createOrUpdateClassLoaderLocked(null);
+                addAllConfigSplits(null, splitPaths);
+                mCachedBaseResourcePath = splitPaths.toArray(new String[splitPaths.size()]);
+                return;
+            }
+
+            final ClassLoader parent;
+            if (parentSplitIdx == -1) {
+                // The parent is the base APK, so use its ClassLoader as parent
+                // and its configuration splits as part of our own too.
+                parent = mClassLoader;
+                Collections.addAll(splitPaths, mCachedBaseResourcePath);
+            } else {
+                parent = mCachedSplitClassLoaders[parentSplitIdx];
+                Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
+            }
+
+            mCachedSplitClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
+                    mSplitAppDirs[splitIdx], getTargetSdkVersion(), false, null, null, parent);
+
+            splitPaths.add(mSplitResDirs[splitIdx]);
+            addAllConfigSplits(mSplitNames[splitIdx], splitPaths);
+            mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]);
+        }
+
+        private int ensureSplitLoaded(String splitName)
+                throws PackageManager.NameNotFoundException {
+            final int idx;
+            if (splitName == null) {
+                idx = -1;
+            } else {
+                idx = Arrays.binarySearch(mSplitNames, splitName);
+                if (idx < 0) {
+                    throw new PackageManager.NameNotFoundException(
+                            "Split name '" + splitName + "' is not installed");
+                }
+            }
+
+            loadDependenciesForSplit(idx);
+            return idx;
+        }
+
+        ClassLoader getClassLoaderForSplit(String splitName)
+                throws PackageManager.NameNotFoundException {
+            final int idx = ensureSplitLoaded(splitName);
+            if (idx < 0) {
+                return mClassLoader;
+            }
+            return mCachedSplitClassLoaders[idx];
+        }
+
+        String[] getSplitPathsForSplit(String splitName)
+                throws PackageManager.NameNotFoundException {
+            final int idx = ensureSplitLoaded(splitName);
+            if (idx < 0) {
+                return mCachedBaseResourcePath;
+            }
+            return mCachedResourcePaths[idx];
+        }
+    }
+
+    private SplitDependencyLoader mSplitLoader;
+
+    ClassLoader getSplitClassLoader(String splitName) throws PackageManager.NameNotFoundException {
+        if (mSplitLoader == null) {
+            return mClassLoader;
+        }
+        return mSplitLoader.getClassLoaderForSplit(splitName);
+    }
+
+    String[] getSplitPaths(String splitName) throws PackageManager.NameNotFoundException {
+        if (mSplitLoader == null) {
+            return mSplitResDirs;
+        }
+        return mSplitLoader.getSplitPathsForSplit(splitName);
+    }
+
     private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
         if (mPackageName.equals("android")) {
             // Note: This branch is taken for system server and we don't need to setup
@@ -780,6 +923,10 @@
         return mOverlayDirs;
     }
 
+    public String[] getSharedLibraries() {
+        return mSharedLibraries;
+    }
+
     public String getDataDir() {
         return mDataDir;
     }
@@ -796,14 +943,24 @@
         return mCredentialProtectedDataDirFile;
     }
 
-    public AssetManager getAssets(ActivityThread mainThread) {
-        return getResources(mainThread).getAssets();
+    public AssetManager getAssets() {
+        return getResources().getAssets();
     }
 
-    public Resources getResources(ActivityThread mainThread) {
+    public Resources getResources() {
         if (mResources == null) {
-            mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
-                    mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, this);
+            final String[] splitPaths;
+            try {
+                splitPaths = getSplitPaths(null);
+            } catch (PackageManager.NameNotFoundException e) {
+                // This should never fail.
+                throw new AssertionError("null split not found");
+            }
+
+            mResources = ResourcesManager.getInstance().getResources(null, mResDir,
+                    splitPaths, mOverlayDirs, mSharedLibraries,
+                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+                    getClassLoader());
         }
         return mResources;
     }
@@ -860,8 +1017,7 @@
         }
 
         // Rewrite the R 'constants' for all library apks.
-        SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
-                .getAssignedPackageIdentifiers();
+        SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers();
         final int N = packageIdentifiers.size();
         for (int i = 0; i < N; i++) {
             final int id = packageIdentifiers.keyAt(i);
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index 09e95df..d2d7b6d 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -174,7 +174,7 @@
     @Override
     public void setContentDescription(CharSequence contentDescription) {
         super.setContentDescription(contentDescription);
-        setTooltip(contentDescription);
+        setTooltipText(contentDescription);
     }
 
     @Override
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 119b055..7bdf4cc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -46,6 +46,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.text.BidiFormatter;
 import android.text.SpannableStringBuilder;
@@ -971,6 +972,21 @@
     public static final String EXTRA_MESSAGES = "android.messages";
 
     /**
+     * {@link #extras} key: an array of
+     * {@link android.app.Notification.MessagingStyle#addHistoricMessage historic}
+     * {@link android.app.Notification.MessagingStyle.Message} bundles provided by a
+     * {@link android.app.Notification.MessagingStyle} notification. This extra is a parcelable
+     * array of bundles.
+     */
+    public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
+
+    /**
+     * {@link #extras} key: whether the notification should be colorized as
+     * supplied to {@link Builder#setColorized(boolean)}}.
+     */
+    public static final String EXTRA_COLORIZED = "android.colorized";
+
+    /**
      * {@link #extras} key: the user that built the notification.
      *
      * @hide
@@ -987,6 +1003,32 @@
      */
     public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
 
+    /**
+     * {@link #extras} key: the audio contents of this notification.
+     *
+     * This is for use when rendering the notification on an audio-focused interface;
+     * the audio contents are a complete sound sample that contains the contents/body of the
+     * notification. This may be used in substitute of a Text-to-Speech reading of the
+     * notification. For example if the notification represents a voice message this should point
+     * to the audio of that message.
+     *
+     * The data stored under this key should be a String representation of a Uri that contains the
+     * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
+     *
+     * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
+     * has a field for holding data URI. That field can be used for audio.
+     * See {@code Message#setData}.
+     *
+     * Example usage:
+     * <pre>
+     * {@code
+     * Notification.Builder myBuilder = (build your Notification as normal);
+     * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
+     * }
+     * </pre>
+     */
+    public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+
     /** @hide */
     @SystemApi
     public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
@@ -995,6 +1037,7 @@
     private Icon mLargeIcon;
 
     private String mChannelId;
+    private long mTimeout;
 
     /**
      * Structure to encapsulate a named action that can be shown as part of this notification.
@@ -1006,10 +1049,25 @@
      * to attach actions.
      */
     public static class Action implements Parcelable {
+        /**
+         * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
+         * {@link RemoteInput}s.
+         *
+         * This is intended for {@link RemoteInput}s that only accept data, meaning
+         * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
+         * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
+         * empty. These {@link RemoteInput}s will be ignored by devices that do not
+         * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
+         *
+         * You can test if a RemoteInput matches these constraints using
+         * {@link RemoteInput#isDataOnly}.
+         */
+        private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
+
         private final Bundle mExtras;
         private Icon mIcon;
         private final RemoteInput[] mRemoteInputs;
-        private boolean mAllowGeneratedReplies = false;
+        private boolean mAllowGeneratedReplies = true;
 
         /**
          * Small icon representing the action.
@@ -1051,7 +1109,7 @@
          */
         @Deprecated
         public Action(int icon, CharSequence title, PendingIntent intent) {
-            this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, false);
+            this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true);
         }
 
         /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
@@ -1096,20 +1154,35 @@
 
         /**
          * Get the list of inputs to be collected from the user when this action is sent.
-         * May return null if no remote inputs were added.
+         * May return null if no remote inputs were added. Only returns inputs which accept
+         * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
          */
         public RemoteInput[] getRemoteInputs() {
             return mRemoteInputs;
         }
 
         /**
+         * Get the list of inputs to be collected from the user that ONLY accept data when this
+         * action is sent. These remote inputs are guaranteed to return true on a call to
+         * {@link RemoteInput#isDataOnly}.
+         *
+         * May return null if no data-only remote inputs were added.
+         *
+         * This method exists so that legacy RemoteInput collectors that pre-date the addition
+         * of non-textual RemoteInputs do not access these remote inputs.
+         */
+        public RemoteInput[] getDataOnlyRemoteInputs() {
+            return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+        }
+
+        /**
          * Builder class for {@link Action} objects.
          */
         public static final class Builder {
             private final Icon mIcon;
             private final CharSequence mTitle;
             private final PendingIntent mIntent;
-            private boolean mAllowGeneratedReplies;
+            private boolean mAllowGeneratedReplies = true;
             private final Bundle mExtras;
             private ArrayList<RemoteInput> mRemoteInputs;
 
@@ -1131,7 +1204,7 @@
              * @param intent the {@link PendingIntent} to fire when users trigger this action
              */
             public Builder(Icon icon, CharSequence title, PendingIntent intent) {
-                this(icon, title, intent, new Bundle(), null, false);
+                this(icon, title, intent, new Bundle(), null, true);
             }
 
             /**
@@ -1203,7 +1276,7 @@
              * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
              * otherwise
              * @return this object for method chaining
-             * The default value is {@code false}
+             * The default value is {@code true}
              */
             public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
                 mAllowGeneratedReplies = allowGeneratedReplies;
@@ -1225,9 +1298,32 @@
              * @return the built action
              */
             public Action build() {
-                RemoteInput[] remoteInputs = mRemoteInputs != null
-                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
-                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs,
+                ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
+                RemoteInput[] previousDataInputs =
+                    (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+                if (previousDataInputs != null) {
+                    for (RemoteInput input : previousDataInputs) {
+                        dataOnlyInputs.add(input);
+                    }
+                }
+                List<RemoteInput> textInputs = new ArrayList<>();
+                if (mRemoteInputs != null) {
+                    for (RemoteInput input : mRemoteInputs) {
+                        if (input.isDataOnly()) {
+                            dataOnlyInputs.add(input);
+                        } else {
+                            textInputs.add(input);
+                        }
+                    }
+                }
+                if (!dataOnlyInputs.isEmpty()) {
+                    RemoteInput[] dataInputsArr =
+                            dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
+                    mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
+                }
+                RemoteInput[] textInputsArr = textInputs.isEmpty()
+                        ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
+                return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
                         mAllowGeneratedReplies);
             }
         }
@@ -1520,7 +1616,7 @@
             /**
              * Get a hint that this Action should be displayed inline.
              *
-             * @return {@code true} if the Action should be displayed inline, {@code false} 
+             * @return {@code true} if the Action should be displayed inline, {@code false}
              *         otherwise. The default value is {@code false} if this was never set.
              */
             public boolean getHintDisplayActionInline() {
@@ -1686,6 +1782,7 @@
         if (parcel.readInt() != 0) {
             mChannelId = parcel.readString();
         }
+        mTimeout = parcel.readLong();
     }
 
     @Override
@@ -1792,6 +1889,7 @@
         that.color = this.color;
 
         that.mChannelId = this.mChannelId;
+        that.mTimeout = this.mTimeout;
 
         if (!heavy) {
             that.lightenPayload(); // will clean out extras
@@ -2048,6 +2146,7 @@
         } else {
             parcel.writeInt(0);
         }
+        parcel.writeLong(mTimeout);
     }
 
     /**
@@ -2245,6 +2344,13 @@
     }
 
     /**
+     * Returns the time at which this notification should be canceled, if it's not canceled already.
+     */
+    public long getTimeout() {
+        return mTimeout;
+    }
+
+    /**
      * The small icon representing this notification in the status bar and content view.
      *
      * @return the small icon representing this notification.
@@ -2327,6 +2433,9 @@
 
         private static final int MAX_ACTION_BUTTONS = 3;
 
+        private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
+                SystemProperties.getBoolean("notifications.only_title", true);
+
         private Context mContext;
         private Notification mN;
         private Bundle mUserExtras = new Bundle();
@@ -2334,6 +2443,8 @@
         private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
         private ArrayList<String> mPersonList = new ArrayList<String>();
         private NotificationColorUtil mColorUtil;
+        private boolean mIsLegacy;
+        private boolean mIsLegacyInitialized;
         private boolean mColorUtilInited = false;
 
         /**
@@ -2341,6 +2452,21 @@
          */
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
+        /**
+         * Caches a ambient version of {@link #mCachedContrastColorIsFor}.
+         */
+        private int mCachedAmbientColor = COLOR_INVALID;
+        private int mCachedAmbientColorIsFor = COLOR_INVALID;
+
+        /**
+         * Caches an instance of StandardTemplateParams. Note that this may have been used before,
+         * so make sure to call {@link StandardTemplateParams#reset()} before using it.
+         */
+        StandardTemplateParams mParams = new StandardTemplateParams();
+        private int mTextColorsAreForBackground = COLOR_INVALID;
+        private int mPrimaryTextColor = COLOR_INVALID;
+        private int mSecondaryTextColor = COLOR_INVALID;
+        private int mActionBarColor = COLOR_INVALID;
 
         /**
          * Constructs a new Builder with the defaults:
@@ -2360,9 +2486,23 @@
          *            A {@link Context} that will be used by the Builder to construct the
          *            RemoteViews. The Context will not be held past the lifetime of this Builder
          *            object.
+         * @param channelId
+         *            The constructed Notification will be posted on this
+         *            {@link NotificationChannel}. To use a NotificationChannel, it must first be
+         *            created using {@link NotificationManager#createNotificationChannel}.
          */
+        public Builder(Context context, String channelId) {
+            this(context, (Notification) null);
+            mN.mChannelId = channelId;
+        }
+
+        /**
+         * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
+         * instead. All posted Notifications must specify a NotificationChannel Id.
+         */
+        @Deprecated
         public Builder(Context context) {
-            this(context, null);
+            this(context, (Notification) null);
         }
 
         /**
@@ -2425,7 +2565,7 @@
         private NotificationColorUtil getColorUtil() {
             if (!mColorUtilInited) {
                 mColorUtilInited = true;
-                if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
+                if (isLegacy() || isColorized()) {
                     mColorUtil = NotificationColorUtil.getInstance(mContext);
                 }
             }
@@ -2441,6 +2581,15 @@
         }
 
         /**
+         * Specifies the time at which this notification should be canceled, if it is not already
+         * canceled.
+         */
+        public Builder setTimeout(long when) {
+            mN.mTimeout = when;
+            return this;
+        }
+
+        /**
          * Add a timestamp pertaining to the notification (usually the time the event occurred).
          *
          * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
@@ -2901,6 +3050,22 @@
         }
 
         /**
+         * Set whether this notification should be colorized. When set, the color set with
+         * {@link #setColor(int)} will be used as the background color of this notification.
+         * <p>
+         * The coloring will only be applied if the notification is ongoing.
+         * This should only be used for high priority ongoing tasks like navigation, an ongoing
+         * call, or other similarly high-priority events for the user.
+         *
+         * @see Builder#setOngoing(boolean)
+         * @see Builder#setColor(int)
+         */
+        public Builder setColorized(boolean colorize) {
+            mN.extras.putBoolean(EXTRA_COLORIZED, colorize);
+            return this;
+        }
+
+        /**
          * Set this flag if you would only like the sound, vibrate
          * and ticker to be played if the notification is not already showing.
          *
@@ -3265,6 +3430,10 @@
             if (profileBadge != null) {
                 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
                 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
+                if (isColorized()) {
+                    contentView.setDrawableParameters(R.id.profile_badge, false, -1,
+                            getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
+                }
             }
         }
 
@@ -3305,45 +3474,40 @@
         }
 
         private RemoteViews applyStandardTemplate(int resId) {
-            return applyStandardTemplate(resId, true /* hasProgress */);
+            return applyStandardTemplate(resId, mParams.reset().fillTextsFrom(this));
         }
 
         /**
          * @param hasProgress whether the progress bar should be shown and set
          */
         private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
-            final Bundle ex = mN.extras;
-
-            CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
-            CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
-            return applyStandardTemplate(resId, hasProgress, title, text);
+            return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
+                    .fillTextsFrom(this));
         }
 
-        /**
-         * @param hasProgress whether the progress bar should be shown and set
-         */
-        private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
-                CharSequence title, CharSequence text) {
+        private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p) {
             RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
 
             resetStandardTemplate(contentView);
 
             final Bundle ex = mN.extras;
-
-            bindNotificationHeader(contentView);
+            updateBackgroundColor(contentView);
+            bindNotificationHeader(contentView, p.ambient);
             bindLargeIcon(contentView);
-            boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
-            if (title != null) {
+            boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
+            if (p.title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
-                contentView.setTextViewText(R.id.title, title);
+                contentView.setTextViewText(R.id.title, p.title);
+                setTextViewColorPrimary(contentView, R.id.title);
                 contentView.setViewLayoutWidth(R.id.title, showProgress
                         ? ViewGroup.LayoutParams.WRAP_CONTENT
                         : ViewGroup.LayoutParams.MATCH_PARENT);
             }
-            if (text != null) {
+            if (p.text != null) {
                 int textId = showProgress ? com.android.internal.R.id.text_line_1
                         : com.android.internal.R.id.text;
-                contentView.setTextViewText(textId, text);
+                contentView.setTextViewText(textId, p.text);
+                setTextViewColorSecondary(contentView, textId);
                 contentView.setViewVisibility(textId, View.VISIBLE);
             }
 
@@ -3352,6 +3516,52 @@
             return contentView;
         }
 
+        private void setTextViewColorPrimary(RemoteViews contentView, int id) {
+            ensureColors();
+            contentView.setTextColor(id, mPrimaryTextColor);
+        }
+
+        private int getPrimaryTextColor() {
+            ensureColors();
+            return mPrimaryTextColor;
+        }
+
+        private int getActionBarColor() {
+            ensureColors();
+            return mActionBarColor;
+        }
+
+        private void setTextViewColorSecondary(RemoteViews contentView, int id) {
+            ensureColors();
+            contentView.setTextColor(id, mSecondaryTextColor);
+        }
+
+        private void ensureColors() {
+            int backgroundColor = getBackgroundColor();
+            if (mPrimaryTextColor == COLOR_INVALID
+                    || mSecondaryTextColor == COLOR_INVALID
+                    || mActionBarColor == COLOR_INVALID
+                    || mTextColorsAreForBackground != backgroundColor) {
+                mTextColorsAreForBackground = backgroundColor;
+                mPrimaryTextColor = NotificationColorUtil.resolvePrimaryColor(
+                        mContext, backgroundColor);
+                mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(
+                        mContext, backgroundColor);
+                mActionBarColor = NotificationColorUtil.resolveActionBarColor(backgroundColor);
+            }
+        }
+
+        private void updateBackgroundColor(RemoteViews contentView) {
+            if (isColorized()) {
+                contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
+                        getBackgroundColor());
+            } else {
+                // Clear it!
+                contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
+                        0);
+            }
+        }
+
         /**
          * @param remoteView the remote view to update the minheight in
          * @param hasMinHeight does it have a mimHeight
@@ -3405,25 +3615,30 @@
             }
         }
 
-        private void bindNotificationHeader(RemoteViews contentView) {
-            bindSmallIcon(contentView);
-            bindHeaderAppName(contentView);
-            bindHeaderText(contentView);
-            bindHeaderChronometerAndTime(contentView);
-            bindExpandButton(contentView);
-            bindProfileBadge(contentView);
+        private void bindNotificationHeader(RemoteViews contentView, boolean ambient) {
+            bindSmallIcon(contentView, ambient);
+            bindHeaderAppName(contentView, ambient);
+            if (!ambient) {
+                // Ambient view does not have these
+                bindHeaderText(contentView);
+                bindHeaderChronometerAndTime(contentView);
+                bindExpandButton(contentView);
+                bindProfileBadge(contentView);
+            }
         }
 
         private void bindExpandButton(RemoteViews contentView) {
-            contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
+            int color = isColorized() ? getPrimaryTextColor() : resolveContrastColor();
+            contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
                     PorterDuff.Mode.SRC_ATOP, -1);
             contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
-                    resolveContrastColor());
+                    color);
         }
 
         private void bindHeaderChronometerAndTime(RemoteViews contentView) {
             if (showsTimeOrChronometer()) {
                 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
+                setTextViewColorSecondary(contentView, R.id.time_divider);
                 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
                     contentView.setLong(R.id.chronometer, "setBase",
@@ -3431,9 +3646,11 @@
                     contentView.setBoolean(R.id.chronometer, "setStarted", true);
                     boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
                     contentView.setChronometerCountDown(R.id.chronometer, countsDown);
+                    setTextViewColorSecondary(contentView, R.id.chronometer);
                 } else {
                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
                     contentView.setLong(R.id.time, "setTime", mN.when);
+                    setTextViewColorSecondary(contentView, R.id.time);
                 }
             } else {
                 // We still want a time to be set but gone, such that we can show and hide it
@@ -3456,8 +3673,10 @@
             if (headerText != null) {
                 // TODO: Remove the span entirely to only have the string with propper formating.
                 contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
+                setTextViewColorSecondary(contentView, R.id.header_text);
                 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
                 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
+                setTextViewColorSecondary(contentView, R.id.header_text_divider);
             }
         }
 
@@ -3494,19 +3713,24 @@
 
             return String.valueOf(name);
         }
-        private void bindHeaderAppName(RemoteViews contentView) {
+        private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
             contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
-            contentView.setTextColor(R.id.app_name_text, resolveContrastColor());
+            if (isColorized()) {
+                setTextViewColorPrimary(contentView, R.id.app_name_text);
+            } else {
+                contentView.setTextColor(R.id.app_name_text,
+                        ambient ? resolveAmbientColor() : resolveContrastColor());
+            }
         }
 
-        private void bindSmallIcon(RemoteViews contentView) {
+        private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
             if (mN.mSmallIcon == null && mN.icon != 0) {
                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
             }
             contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
             contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
                     -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
-            processSmallIconColor(mN.mSmallIcon, contentView);
+            processSmallIconColor(mN.mSmallIcon, contentView, ambient);
         }
 
         /**
@@ -3535,27 +3759,31 @@
         }
 
         private RemoteViews applyStandardTemplateWithActions(int layoutId) {
-            final Bundle ex = mN.extras;
-
-            CharSequence title = processLegacyText(ex.getCharSequence(EXTRA_TITLE));
-            CharSequence text = processLegacyText(ex.getCharSequence(EXTRA_TEXT));
-            return applyStandardTemplateWithActions(layoutId, true /* hasProgress */, title, text);
+            return applyStandardTemplateWithActions(layoutId, mParams.reset().fillTextsFrom(this));
         }
 
-        private RemoteViews applyStandardTemplateWithActions(int layoutId, boolean hasProgress,
-                CharSequence title, CharSequence text) {
-            RemoteViews big = applyStandardTemplate(layoutId, hasProgress, title, text);
+        private RemoteViews applyStandardTemplateWithActions(int layoutId,
+            StandardTemplateParams p) {
+            RemoteViews big = applyStandardTemplate(layoutId, p);
 
             resetStandardTemplateWithActions(big);
 
             boolean validRemoteInput = false;
 
             int N = mActions.size();
-            boolean emphazisedMode = mN.fullScreenIntent != null;
+            boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
             big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
             if (N > 0) {
                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
+                if (p.ambient) {
+                    big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
+                } else if (isColorized()) {
+                    big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
+                } else {
+                    big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
+                            R.color.notification_action_list));
+                }
                 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
                         R.dimen.notification_action_list_height);
                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
@@ -3564,7 +3792,7 @@
                     validRemoteInput |= hasValidRemoteInput(action);
 
                     final RemoteViews button = generateActionButton(action, emphazisedMode,
-                            i % 2 != 0);
+                            i % 2 != 0, p.ambient);
                     big.addView(R.id.actions, button);
                 }
             } else {
@@ -3572,19 +3800,22 @@
             }
 
             CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
-            if (validRemoteInput && replyText != null
+            if (!p.ambient && validRemoteInput && replyText != null
                     && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])) {
                 big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
                 big.setTextViewText(R.id.notification_material_reply_text_1, replyText[0]);
+                setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
 
                 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])) {
                     big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
                     big.setTextViewText(R.id.notification_material_reply_text_2, replyText[1]);
+                    setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
 
                     if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])) {
                         big.setViewVisibility(
                                 R.id.notification_material_reply_text_3, View.VISIBLE);
                         big.setTextViewText(R.id.notification_material_reply_text_3, replyText[2]);
+                        setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
                     }
                 }
             }
@@ -3644,7 +3875,7 @@
             } else if (mActions.size() != 0) {
                 result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
             }
-            adaptNotificationHeaderForBigContentView(result);
+            makeHeaderExpanded(result);
             return result;
         }
 
@@ -3657,17 +3888,34 @@
             RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
                     R.layout.notification_template_header);
             resetNotificationHeader(header);
-            bindNotificationHeader(header);
+            bindNotificationHeader(header, false /* ambient */);
             return header;
         }
 
+        /**
+         * Construct a RemoteViews for the ambient version of the notification.
+         *
+         * @hide
+         */
+        public RemoteViews makeAmbientNotification() {
+            RemoteViews ambient = applyStandardTemplateWithActions(
+                    R.layout.notification_template_material_ambient,
+                    mParams.reset().fillTextsFrom(this).hasProgress(false).ambient(true));
+            return ambient;
+        }
+
         private void hideLine1Text(RemoteViews result) {
             if (result != null) {
                 result.setViewVisibility(R.id.text_line_1, View.GONE);
             }
         }
 
-        private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
+        /**
+         * Adapt the Notification header if this view is used as an expanded view.
+         *
+         * @hide
+         */
+        public static void makeHeaderExpanded(RemoteViews result) {
             if (result != null) {
                 result.setBoolean(R.id.notification_header, "setExpanded", true);
             }
@@ -3727,10 +3975,60 @@
             return publicView;
         }
 
+        /**
+         * Construct a content view for the display when low - priority
+         *
+         * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
+         *                          a new subtext is created consisting of the content of the
+         *                          notification.
+         * @hide
+         */
+        public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
+            int color = mN.color;
+            mN.color = COLOR_DEFAULT;
+            CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
+            if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
+                CharSequence newSummary = createSummaryText();
+                if (!TextUtils.isEmpty(newSummary)) {
+                    mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
+                }
+            }
+            RemoteViews header = makeNotificationHeader();
+            if (summary != null) {
+                mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
+            } else {
+                mN.extras.remove(EXTRA_SUB_TEXT);
+            }
+            mN.color = color;
+            return header;
+        }
 
+        private CharSequence createSummaryText() {
+            CharSequence titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE);
+            if (USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY) {
+                return titleText;
+            }
+            SpannableStringBuilder summary = new SpannableStringBuilder();
+            if (titleText == null) {
+                titleText = mN.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
+            }
+            BidiFormatter bidi = BidiFormatter.getInstance();
+            if (titleText != null) {
+                summary.append(bidi.unicodeWrap(titleText));
+            }
+            CharSequence contentText = mN.extras.getCharSequence(Notification.EXTRA_TEXT);
+            if (titleText != null && contentText != null) {
+                summary.append(bidi.unicodeWrap(mContext.getText(
+                        R.string.notification_header_divider_symbol_with_spaces)));
+            }
+            if (contentText != null) {
+                summary.append(bidi.unicodeWrap(contentText));
+            }
+            return summary;
+        }
 
         private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
-                boolean oddAction) {
+                boolean oddAction, boolean ambient) {
             final boolean tombstone = (action.actionIntent == null);
             RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
                     emphazisedMode ? getEmphasizedActionLayoutResource()
@@ -3743,6 +4041,7 @@
             if (action.mRemoteInputs != null) {
                 button.setRemoteInputs(R.id.action0, action.mRemoteInputs);
             }
+            // TODO: handle emphasized mode / actions right
             if (emphazisedMode) {
                 // change the background bgColor
                 int bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
@@ -3758,17 +4057,21 @@
                     title = ensureColorSpanContrast(title, bgColor, outResultColor);
                 }
                 button.setTextViewText(R.id.action0, title);
+                setTextViewColorPrimary(button, R.id.action0);
                 if (outResultColor != null && outResultColor[0] != null) {
                     // We need to set the text color as well since changing a text to uppercase
                     // clears its spans.
                     button.setTextColor(R.id.action0, outResultColor[0]);
-                } else if (mN.color != COLOR_DEFAULT) {
+                } else if (mN.color != COLOR_DEFAULT && !isColorized()) {
                     button.setTextColor(R.id.action0,resolveContrastColor());
                 }
             } else {
                 button.setTextViewText(R.id.action0, processLegacyText(action.title));
-                if (mN.color != COLOR_DEFAULT) {
-                    button.setTextColor(R.id.action0, resolveContrastColor());
+                if (isColorized()) {
+                    setTextViewColorPrimary(button, R.id.action0);
+                } else if (mN.color != COLOR_DEFAULT) {
+                    button.setTextColor(R.id.action0,
+                            ambient ? resolveAmbientColor() : resolveContrastColor());
                 }
             }
             return button;
@@ -3885,11 +4188,16 @@
          *         doesn't create material notifications by itself) app.
          */
         private boolean isLegacy() {
-            return getColorUtil() != null;
+            if (!mIsLegacyInitialized) {
+                mIsLegacy = mContext.getApplicationInfo().targetSdkVersion
+                        < Build.VERSION_CODES.LOLLIPOP;
+                mIsLegacyInitialized = true;
+            }
+            return mIsLegacy;
         }
 
         private CharSequence processLegacyText(CharSequence charSequence) {
-            if (isLegacy()) {
+            if (isLegacy() || isColorized()) {
                 return getColorUtil().invertCharSequenceColors(charSequence);
             } else {
                 return charSequence;
@@ -3899,15 +4207,17 @@
         /**
          * Apply any necessariy colors to the small icon
          */
-        private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
+        private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
+                boolean ambient) {
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
+            int color = ambient ? resolveAmbientColor() : resolveContrastColor();
             if (colorable) {
-                contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
+                contentView.setDrawableParameters(R.id.icon, false, -1, color,
                         PorterDuff.Mode.SRC_ATOP, -1);
 
             }
             contentView.setInt(R.id.notification_header, "setOriginalIconColor",
-                    colorable ? resolveContrastColor() : NotificationHeaderView.NO_COLOR);
+                    colorable ? color : NotificationHeaderView.NO_COLOR);
         }
 
         /**
@@ -3940,6 +4250,16 @@
             return mCachedContrastColor = contrasted;
         }
 
+        int resolveAmbientColor() {
+            if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
+                return mCachedAmbientColor;
+            }
+            final int contrasted = NotificationColorUtil.resolveAmbientColor(mContext, mN.color);
+
+            mCachedAmbientColorIsFor = mN.color;
+            return mCachedAmbientColor = contrasted;
+        }
+
         /**
          * Apply the unstyled operations and return a new {@link Notification} object.
          * @hide
@@ -4139,6 +4459,37 @@
         private int getActionTombstoneLayoutResource() {
             return R.layout.notification_material_action_tombstone;
         }
+
+        private int getBackgroundColor() {
+            if (isColorized()) {
+                return mN.color;
+            } else {
+                return COLOR_DEFAULT;
+            }
+        }
+
+        private boolean isColorized() {
+            return mN.isColorized();
+        }
+    }
+
+    /**
+     * @return whether this notification is ongoing and can't be dismissed by the user.
+     */
+    private boolean isOngoing() {
+        final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE
+                | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
+        return (flags & ongoingFlags) != 0;
+    }
+
+    /**
+     * @return true if this notification is colorized. This also factors in wheather the
+     * notification is ongoing.
+     *
+     * @hide
+     */
+    public boolean isColorized() {
+        return extras.getBoolean(EXTRA_COLORIZED) && isOngoing();
     }
 
     private boolean hasLargeIcon() {
@@ -4472,6 +4823,7 @@
             RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
             if (mSummaryTextSet) {
                 contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
+                mBuilder.setTextViewColorSecondary(contentView, R.id.text);
                 contentView.setViewVisibility(R.id.text, View.VISIBLE);
             }
             mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
@@ -4627,6 +4979,7 @@
         static void applyBigTextContentView(Builder builder,
                 RemoteViews contentView, CharSequence bigTextText) {
             contentView.setTextViewText(R.id.big_text, bigTextText);
+            builder.setTextViewColorSecondary(contentView, R.id.big_text);
             contentView.setViewVisibility(R.id.big_text,
                     TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
             contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines(builder));
@@ -4677,6 +5030,7 @@
         CharSequence mUserDisplayName;
         CharSequence mConversationTitle;
         List<Message> mMessages = new ArrayList<>();
+        List<Message> mHistoricMessages = new ArrayList<>();
 
         MessagingStyle() {
         }
@@ -4733,15 +5087,15 @@
          * @return this object for method chaining
          */
         public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
-            mMessages.add(new Message(text, timestamp, sender));
-            if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
-                mMessages.remove(0);
-            }
-            return this;
+            return addMessage(new Message(text, timestamp, sender));
         }
 
         /**
          * Adds a {@link Message} for display in this notification.
+         *
+         * <p>The messages should be added in chronologic order, i.e. the oldest first,
+         * the newest last.
+         *
          * @param message The {@link Message} to be displayed
          * @return this object for method chaining
          */
@@ -4754,6 +5108,27 @@
         }
 
         /**
+         * Adds a {@link Message} for historic context in this notification.
+         *
+         * <p>Messages should be added as historic if they are not the main subject of the
+         * notification but may give context to a conversation. The system may choose to present
+         * them only when relevant, e.g. when replying to a message through a {@link RemoteInput}.
+         *
+         * <p>The messages should be added in chronologic order, i.e. the oldest first,
+         * the newest last.
+         *
+         * @param message The historic {@link Message} to be added
+         * @return this object for method chaining
+         */
+        public MessagingStyle addHistoricMessage(Message message) {
+            mHistoricMessages.add(message);
+            if (mHistoricMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
+                mHistoricMessages.remove(0);
+            }
+            return this;
+        }
+
+        /**
          * Gets the list of {@code Message} objects that represent the notification
          */
         public List<Message> getMessages() {
@@ -4761,6 +5136,13 @@
         }
 
         /**
+         * Gets the list of historic {@code Message}s in the notification.
+         */
+        public List<Message> getHistoricMessages() {
+            return mHistoricMessages;
+        }
+
+        /**
          * @hide
          */
         @Override
@@ -4775,6 +5157,9 @@
             if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
                     Message.getBundleArrayForMessages(mMessages));
             }
+            if (!mHistoricMessages.isEmpty()) { extras.putParcelableArray(EXTRA_HISTORIC_MESSAGES,
+                    Message.getBundleArrayForMessages(mHistoricMessages));
+            }
 
             fixTitleAndTextExtras(extras);
         }
@@ -4814,11 +5199,16 @@
             super.restoreFromExtras(extras);
 
             mMessages.clear();
+            mHistoricMessages.clear();
             mUserDisplayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
             mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
-            Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES);
-            if (parcelables != null && parcelables instanceof Parcelable[]) {
-                mMessages = Message.getMessagesFromBundleArray(parcelables);
+            Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
+            if (messages != null && messages instanceof Parcelable[]) {
+                mMessages = Message.getMessagesFromBundleArray(messages);
+            }
+            Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
+            if (histMessages != null && histMessages instanceof Parcelable[]) {
+                mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
             }
         }
 
@@ -4833,12 +5223,10 @@
                     : (m == null) ? null : m.mSender;
             CharSequence text = (m == null)
                     ? null
-                    : mConversationTitle != null ? makeMessageLine(m) : m.mText;
+                    : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
 
-            return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
-                    false /* hasProgress */,
-                    title,
-                    text);
+            return mBuilder.applyStandardTemplateWithActions(mBuilder.getBaseLayoutResource(),
+                    mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
         }
 
         private Message findLatestIncomingMessage() {
@@ -4873,23 +5261,21 @@
                 CharSequence text;
                 if (hasTitle) {
                     bigTitle = title;
-                    text = makeMessageLine(mMessages.get(0));
+                    text = makeMessageLine(mMessages.get(0), mBuilder);
                 } else {
                     bigTitle = mMessages.get(0).mSender;
                     text = mMessages.get(0).mText;
                 }
                 RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                         mBuilder.getBigTextLayoutResource(),
-                        false /* progress */, bigTitle, null /* text */);
+                        mBuilder.mParams.reset().hasProgress(false).title(bigTitle).text(null));
                 BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
                 return contentView;
             }
 
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
-                    false /* hasProgress */,
-                    title,
-                    null /* text */);
+                    mBuilder.mParams.reset().hasProgress(false).title(title).text(null));
 
             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
                     R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
@@ -4907,13 +5293,13 @@
 
             int contractedChildId = View.NO_ID;
             Message contractedMessage = findLatestIncomingMessage();
-            int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
-            while (firstMessage + i < mMessages.size() && i < rowIds.length) {
-                Message m = mMessages.get(firstMessage + i);
+            int firstHistoricMessage = Math.max(0, mHistoricMessages.size()
+                    - (rowIds.length - mMessages.size()));
+            while (firstHistoricMessage + i < mHistoricMessages.size() && i < rowIds.length) {
+                Message m = mHistoricMessages.get(firstHistoricMessage + i);
                 int rowId = rowIds[i];
 
-                contentView.setViewVisibility(rowId, View.VISIBLE);
-                contentView.setTextViewText(rowId, makeMessageLine(m));
+                contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
 
                 if (contractedMessage == m) {
                     contractedChildId = rowId;
@@ -4921,23 +5307,52 @@
 
                 i++;
             }
+
+            int firstMessage = Math.max(0, mMessages.size() - rowIds.length);
+            while (firstMessage + i < mMessages.size() && i < rowIds.length) {
+                Message m = mMessages.get(firstMessage + i);
+                int rowId = rowIds[i];
+
+                contentView.setViewVisibility(rowId, View.VISIBLE);
+                contentView.setTextViewText(rowId, makeMessageLine(m, mBuilder));
+                mBuilder.setTextViewColorSecondary(contentView, rowId);
+
+                if (contractedMessage == m) {
+                    contractedChildId = rowId;
+                }
+
+                i++;
+            }
+            // Clear the remaining views for reapply. Ensures that historic message views can
+            // reliably be identified as being GONE and having non-null text.
+            while (i < rowIds.length) {
+                int rowId = rowIds[i];
+                contentView.setTextViewText(rowId, null);
+                i++;
+            }
+
             // Record this here to allow transformation between the contracted and expanded views.
             contentView.setInt(R.id.notification_messaging, "setContractedChildId",
                     contractedChildId);
             return contentView;
         }
 
-        private CharSequence makeMessageLine(Message m) {
+        private CharSequence makeMessageLine(Message m, Builder builder) {
             BidiFormatter bidi = BidiFormatter.getInstance();
             SpannableStringBuilder sb = new SpannableStringBuilder();
+            boolean colorize = builder.isColorized();
             if (TextUtils.isEmpty(m.mSender)) {
                 CharSequence replyName = mUserDisplayName == null ? "" : mUserDisplayName;
                 sb.append(bidi.unicodeWrap(replyName),
-                        makeFontColorSpan(mBuilder.resolveContrastColor()),
+                        makeFontColorSpan(colorize
+                                ? builder.getPrimaryTextColor()
+                                : mBuilder.resolveContrastColor()),
                         0 /* flags */);
             } else {
                 sb.append(bidi.unicodeWrap(m.mSender),
-                        makeFontColorSpan(Color.BLACK),
+                        makeFontColorSpan(colorize
+                                ? builder.getPrimaryTextColor()
+                                : Color.BLACK),
                         0 /* flags */);
             }
             CharSequence text = m.mText == null ? "" : m.mText;
@@ -4956,12 +5371,10 @@
                     : (m == null) ? null : m.mSender;
             CharSequence text = (m == null)
                     ? null
-                    : mConversationTitle != null ? makeMessageLine(m) : m.mText;
+                    : mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
 
             return mBuilder.applyStandardTemplateWithActions(mBuilder.getBigBaseLayoutResource(),
-                    false /* hasProgress */,
-                    title,
-                    text);
+                    mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
         }
 
         private static TextAppearanceSpan makeFontColorSpan(int color) {
@@ -5249,6 +5662,7 @@
                 if (!TextUtils.isEmpty(str)) {
                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
                     contentView.setTextViewText(rowIds[i], mBuilder.processLegacyText(str));
+                    mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
                     contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
                     handleInboxImageMargin(contentView, rowIds[i], first);
                     if (first) {
@@ -6948,6 +7362,163 @@
     }
 
     /**
+     * <p>Helper class to add Android TV extensions to notifications. To create a notification
+     * with a TV extension:
+     *
+     * <ol>
+     *  <li>Create an {@link Notification.Builder}, setting any desired properties.
+     *  <li>Create a {@link TvExtender}.
+     *  <li>Set TV-specific properties using the {@code set} methods of
+     *  {@link TvExtender}.
+     *  <li>Call {@link Notification.Builder#extend(Notification.Extender)}
+     *  to apply the extension to a notification.
+     * </ol>
+     *
+     * <pre class="prettyprint">
+     * Notification notification = new Notification.Builder(context)
+     *         ...
+     *         .extend(new TvExtender()
+     *                 .set*(...))
+     *         .build();
+     * </pre>
+     *
+     * <p>TV extensions can be accessed on an existing notification by using the
+     * {@code TvExtender(Notification)} constructor, and then using the {@code get} methods
+     * to access values.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class TvExtender implements Extender {
+        private static final String TAG = "TvExtender";
+
+        private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
+        private static final String EXTRA_FLAGS = "flags";
+        private static final String EXTRA_CONTENT_INTENT = "content_intent";
+        private static final String EXTRA_DELETE_INTENT = "delete_intent";
+        private static final String EXTRA_CHANNEL_ID = "channel_id";
+
+        // Flags bitwise-ored to mFlags
+        private static final int FLAG_AVAILABLE_ON_TV = 0x1;
+
+        private int mFlags;
+        private String mChannelId;
+        private PendingIntent mContentIntent;
+        private PendingIntent mDeleteIntent;
+
+        /**
+         * Create a {@link TvExtender} with default options.
+         */
+        public TvExtender() {
+            mFlags = FLAG_AVAILABLE_ON_TV;
+        }
+
+        /**
+         * Create a {@link TvExtender} from the TvExtender options of an existing Notification.
+         *
+         * @param notif The notification from which to copy options.
+         */
+        public TvExtender(Notification notif) {
+            Bundle bundle = notif.extras == null ?
+                null : notif.extras.getBundle(EXTRA_TV_EXTENDER);
+            if (bundle != null) {
+                mFlags = bundle.getInt(EXTRA_FLAGS);
+                mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
+                mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
+                mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
+            }
+        }
+
+        /**
+         * Apply a TV extension to a notification that is being built. This is typically called by
+         * the {@link Notification.Builder#extend(Notification.Extender)}
+         * method of {@link Notification.Builder}.
+         */
+        @Override
+        public Notification.Builder extend(Notification.Builder builder) {
+            Bundle bundle = new Bundle();
+
+            bundle.putInt(EXTRA_FLAGS, mFlags);
+            bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
+            if (mContentIntent != null) {
+                bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
+            }
+
+            if (mDeleteIntent != null) {
+                bundle.putParcelable(EXTRA_DELETE_INTENT, mDeleteIntent);
+            }
+
+            builder.getExtras().putBundle(EXTRA_TV_EXTENDER, bundle);
+            return builder;
+        }
+
+        /**
+         * Returns true if this notification should be shown on TV. This method return true
+         * if the notification was extended with a TvExtender.
+         */
+        public boolean isAvailableOnTv() {
+            return (mFlags & FLAG_AVAILABLE_ON_TV) != 0;
+        }
+
+        /**
+         * Specifies the channel the notification should be delivered on when shown on TV.
+         * It can be different from the channel that the notification is delivered to when
+         * posting on a non-TV device.
+         */
+        public TvExtender setChannel(String channelId) {
+            mChannelId = channelId;
+            return this;
+        }
+
+        /**
+         * Returns the id of the channel this notification posts to on TV.
+         */
+        public String getChannel() {
+            return mChannelId;
+        }
+
+        /**
+         * Supplies a {@link PendingIntent} to be sent when the notification is selected on TV.
+         * If provided, it is used instead of the content intent specified
+         * at the level of Notification.
+         */
+        public TvExtender setContentIntent(PendingIntent intent) {
+            mContentIntent = intent;
+            return this;
+        }
+
+        /**
+         * Returns the TV-specific content intent.  If this method returns null, the
+         * main content intent on the notification should be used.
+         *
+         * @see {@link Notification#contentIntent}
+         */
+        public PendingIntent getContentIntent() {
+            return mContentIntent;
+        }
+
+        /**
+         * Supplies a {@link PendingIntent} to send when the notification is cleared explicitly
+         * by the user on TV.  If provided, it is used instead of the delete intent specified
+         * at the level of Notification.
+         */
+        public TvExtender setDeleteIntent(PendingIntent intent) {
+            mDeleteIntent = intent;
+            return this;
+        }
+
+        /**
+         * Returns the TV-specific delete intent.  If this method returns null, the
+         * main delete intent on the notification should be used.
+         *
+         * @see {@link Notification#deleteIntent}
+         */
+        public PendingIntent getDeleteIntent() {
+            return mDeleteIntent;
+        }
+    }
+
+    /**
      * Get an array of Notification objects from a parcelable array bundle field.
      * Update the bundle to have a typed array so fetches in the future don't need
      * to do an array copy.
@@ -6982,4 +7553,46 @@
             return brv;
         }
     }
+
+    private static class StandardTemplateParams {
+        boolean hasProgress = true;
+        boolean ambient = false;
+        CharSequence title;
+        CharSequence text;
+
+        final StandardTemplateParams reset() {
+            hasProgress = true;
+            ambient = false;
+            title = null;
+            text = null;
+            return this;
+        }
+
+        final StandardTemplateParams hasProgress(boolean hasProgress) {
+            this.hasProgress = hasProgress;
+            return this;
+        }
+
+        final StandardTemplateParams title(CharSequence title) {
+            this.title = title;
+            return this;
+        }
+
+        final StandardTemplateParams text(CharSequence text) {
+            this.text = text;
+            return this;
+        }
+
+        final StandardTemplateParams ambient(boolean ambient) {
+            this.ambient = ambient;
+            return this;
+        }
+
+        final StandardTemplateParams fillTextsFrom(Builder b) {
+            Bundle extras = b.mN.extras;
+            title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
+            text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
+            return this;
+        }
+    }
 }
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index bdccb8a..26ec418 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -21,6 +21,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.annotation.SystemApi;
+import android.app.NotificationManager;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -44,10 +45,13 @@
     private static final String TAG_CHANNEL = "channel";
     private static final String ATT_NAME = "name";
     private static final String ATT_ID = "id";
+    private static final String ATT_DELETED = "deleted";
     private static final String ATT_PRIORITY = "priority";
     private static final String ATT_VISIBILITY = "visibility";
     private static final String ATT_IMPORTANCE = "importance";
     private static final String ATT_LIGHTS = "lights";
+    //TODO: add support for light colors
+    private static final String ATT_LIGHT_COLOR = "light_color";
     private static final String ATT_VIBRATION = "vibration";
     private static final String ATT_VIBRATION_ENABLED = "vibration_enabled";
     private static final String ATT_SOUND = "sound";
@@ -55,6 +59,7 @@
     private static final String ATT_AUDIO_ATTRIBUTES = "audio_attributes";
     private static final String ATT_SHOW_BADGE = "show_badge";
     private static final String ATT_USER_LOCKED = "locked";
+    private static final String ATT_GROUP = "group";
     private static final String DELIMITER = ",";
 
     /**
@@ -120,7 +125,8 @@
             NotificationManager.VISIBILITY_NO_OVERRIDE;
     private static final int DEFAULT_IMPORTANCE =
             NotificationManager.IMPORTANCE_UNSPECIFIED;
-    private static final boolean DEFAULT_ALLOWED = true;
+    private static final boolean DEFAULT_DELETED = false;
+    private static final boolean DEFAULT_SHOW_BADGE = true;
 
     private final String mId;
     private CharSequence mName;
@@ -132,8 +138,9 @@
     private long[] mVibration;
     private int mUserLockedFields;
     private boolean mVibrationEnabled;
-    private boolean mShowBadge;
-    private boolean mAllowed = DEFAULT_ALLOWED;
+    private boolean mShowBadge = DEFAULT_SHOW_BADGE;
+    private boolean mDeleted = DEFAULT_DELETED;
+    private String mGroup;
 
     /**
      * Creates a notification channel.
@@ -170,7 +177,12 @@
         mUserLockedFields = in.readInt();
         mVibrationEnabled = in.readByte() != 0;
         mShowBadge = in.readByte() != 0;
-        mAllowed = in.readByte() != 0;
+        mDeleted = in.readByte() != 0;
+        if (in.readByte() != 0) {
+            mGroup = in.readString();
+        } else {
+            mGroup = null;
+        }
     }
 
     @Override
@@ -196,7 +208,13 @@
         dest.writeInt(mUserLockedFields);
         dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
         dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0);
-        dest.writeByte(mAllowed ? (byte) 1 : (byte) 0);
+        dest.writeByte(mDeleted ? (byte) 1 : (byte) 0);
+        if (mGroup != null) {
+            dest.writeByte((byte) 1);
+            dest.writeString(mGroup);
+        } else {
+            dest.writeByte((byte) 0);
+        }
     }
 
     /**
@@ -207,6 +225,14 @@
         mUserLockedFields |= field;
     }
 
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void setDeleted(boolean deleted) {
+        mDeleted = deleted;
+    }
+
     // Modifiable by a notification ranker.
 
     /**
@@ -245,6 +271,18 @@
     // Modifiable by apps on channel creation.
 
     /**
+     * Sets what group this channel belongs to.
+     *
+     * Group information is only used for presentation, not for behavior.
+     *
+     * @param groupId the id of a group created by
+     * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.
+     */
+    public void setGroup(String groupId) {
+        this.mGroup = groupId;
+    }
+
+    /**
      * Sets whether notifications posted to this channel can appear as application icon badges
      * in a Launcher.
      *
@@ -359,16 +397,28 @@
     /**
      * Returns whether notifications posted to this channel can appear as badges in a Launcher
      * application.
+     *
+     * Note that badging may be disabled for other reasons.
      */
     public boolean canShowBadge() {
         return mShowBadge;
     }
 
     /**
-     * Returns whether notifications are allowed to post to this channel.
+     * Returns what group this channel belongs to.
+     *
+     * This is used only for visually grouping channels in the UI.
      */
-    public boolean isAllowed() {
-        return mAllowed;
+    public String getGroup() {
+        return mGroup;
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public boolean isDeleted() {
+        return mDeleted;
     }
 
     /**
@@ -393,6 +443,8 @@
         enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
         setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
+        setDeleted(safeBool(parser, ATT_DELETED, false));
+        setGroup(parser.getAttributeValue(null, ATT_GROUP));
         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
     }
 
@@ -434,6 +486,12 @@
         if (canShowBadge()) {
             out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
         }
+        if (isDeleted()) {
+            out.attribute(null, ATT_DELETED, Boolean.toString(isDeleted()));
+        }
+        if (getGroup() != null) {
+            out.attribute(null, ATT_GROUP, getGroup());
+        }
 
         out.endTag(null, TAG_CHANNEL);
     }
@@ -464,6 +522,8 @@
         record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
         record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
         record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
+        record.put(ATT_DELETED, Boolean.toString(isDeleted()));
+        record.put(ATT_GROUP, getGroup());
         return record;
     }
 
@@ -509,10 +569,12 @@
 
     private static String longArrayToString(long[] values) {
         StringBuffer sb = new StringBuffer();
-        for (int i = 0; i < values.length - 1; i++) {
-            sb.append(values[i]).append(DELIMITER);
+        if (values != null) {
+            for (int i = 0; i < values.length - 1; i++) {
+                sb.append(values[i]).append(DELIMITER);
+            }
+            sb.append(values[values.length - 1]);
         }
-        sb.append(values[values.length - 1]);
         return sb.toString();
     }
 
@@ -540,35 +602,41 @@
 
         NotificationChannel that = (NotificationChannel) o;
 
-        if (mImportance != that.mImportance) return false;
+        if (getImportance() != that.getImportance()) return false;
         if (mBypassDnd != that.mBypassDnd) return false;
-        if (mLockscreenVisibility != that.mLockscreenVisibility) return false;
+        if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
         if (mLights != that.mLights) return false;
-        if (mUserLockedFields != that.mUserLockedFields) return false;
+        if (getUserLockedFields() != that.getUserLockedFields()) return false;
         if (mVibrationEnabled != that.mVibrationEnabled) return false;
         if (mShowBadge != that.mShowBadge) return false;
-        if (mAllowed != that.mAllowed) return false;
-        if (mId != null ? !mId.equals(that.mId) : that.mId != null) return false;
-        if (mName != null ? !mName.equals(that.mName) : that.mName != null) return false;
-        if (mSound != null ? !mSound.equals(that.mSound) : that.mSound != null) return false;
-        return Arrays.equals(mVibration, that.mVibration);
+        if (isDeleted() != that.isDeleted()) return false;
+        if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
+        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
+            return false;
+        }
+        if (getSound() != null ? !getSound().equals(that.getSound()) : that.getSound() != null) {
+            return false;
+        }
+        if (!Arrays.equals(mVibration, that.mVibration)) return false;
+        return getGroup() != null ? getGroup().equals(that.getGroup()) : that.getGroup() == null;
 
     }
 
     @Override
     public int hashCode() {
-        int result = mId != null ? mId.hashCode() : 0;
-        result = 31 * result + (mName != null ? mName.hashCode() : 0);
-        result = 31 * result + mImportance;
+        int result = getId() != null ? getId().hashCode() : 0;
+        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+        result = 31 * result + getImportance();
         result = 31 * result + (mBypassDnd ? 1 : 0);
-        result = 31 * result + mLockscreenVisibility;
-        result = 31 * result + (mSound != null ? mSound.hashCode() : 0);
+        result = 31 * result + getLockscreenVisibility();
+        result = 31 * result + (getSound() != null ? getSound().hashCode() : 0);
         result = 31 * result + (mLights ? 1 : 0);
         result = 31 * result + Arrays.hashCode(mVibration);
-        result = 31 * result + mUserLockedFields;
+        result = 31 * result + getUserLockedFields();
         result = 31 * result + (mVibrationEnabled ? 1 : 0);
         result = 31 * result + (mShowBadge ? 1 : 0);
-        result = 31 * result + (mAllowed ? 1 : 0);
+        result = 31 * result + (isDeleted() ? 1 : 0);
+        result = 31 * result + (getGroup() != null ? getGroup().hashCode() : 0);
         return result;
     }
 
@@ -586,7 +654,8 @@
                 ", mUserLockedFields=" + mUserLockedFields +
                 ", mVibrationEnabled=" + mVibrationEnabled +
                 ", mShowBadge=" + mShowBadge +
-                ", mAllowed=" + mAllowed +
+                ", mDeleted=" + mDeleted +
+                ", mGroup='" + mGroup + '\'' +
                 '}';
     }
 }
diff --git a/core/java/android/app/NotificationChannelGroup.aidl b/core/java/android/app/NotificationChannelGroup.aidl
new file mode 100644
index 0000000..c0da037
--- /dev/null
+++ b/core/java/android/app/NotificationChannelGroup.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+parcelable NotificationChannelGroup;
\ No newline at end of file
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
new file mode 100644
index 0000000..3341b91
--- /dev/null
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.notification.NotificationListenerService;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A grouping of related notification channels. e.g., channels that all belong to a single account.
+ */
+public final class NotificationChannelGroup implements Parcelable {
+
+    private static final String TAG_GROUP = "channelGroup";
+    private static final String ATT_NAME = "name";
+    private static final String ATT_ID = "id";
+
+    private final String mId;
+    private CharSequence mName;
+    private List<NotificationChannel> mChannels = new ArrayList<>();
+
+    /**
+     * Creates a notification channel.
+     *
+     * @param id The id of the group. Must be unique per package.
+     * @param name The user visible name of the group.
+     */
+    public NotificationChannelGroup(String id, CharSequence name) {
+        this.mId = id;
+        this.mName = name;
+    }
+
+    protected NotificationChannelGroup(Parcel in) {
+        if (in.readByte() != 0) {
+            mId = in.readString();
+        } else {
+            mId = null;
+        }
+        mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        in.readList(mChannels, NotificationChannel.class.getClassLoader());
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mId != null) {
+            dest.writeByte((byte) 1);
+            dest.writeString(mId);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        TextUtils.writeToParcel(mName, dest, flags);
+        dest.writeParcelableList(mChannels, flags);
+    }
+
+    /**
+     * Returns the id of this channel.
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the user visible name of this channel.
+     */
+    public CharSequence getName() {
+        return mName;
+    }
+
+    /*
+     * Returns the list of channels that belong to this group
+     *
+     * @hide
+     */
+    @SystemApi
+    public List<NotificationChannel> getChannels() {
+        return mChannels;
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void addChannel(NotificationChannel channel) {
+        mChannels.add(channel);
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public void writeXml(XmlSerializer out) throws IOException {
+        out.startTag(null, TAG_GROUP);
+
+        out.attribute(null, ATT_ID, getId());
+        out.attribute(null, ATT_NAME, getName().toString());
+
+        out.endTag(null, TAG_GROUP);
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public JSONObject toJson() throws JSONException {
+        JSONObject record = new JSONObject();
+        record.put(ATT_ID, getId());
+        record.put(ATT_NAME, getName());
+        return record;
+    }
+
+    public static final Creator<NotificationChannelGroup> CREATOR =
+            new Creator<NotificationChannelGroup>() {
+        @Override
+        public NotificationChannelGroup createFromParcel(Parcel in) {
+            return new NotificationChannelGroup(in);
+        }
+
+        @Override
+        public NotificationChannelGroup[] newArray(int size) {
+            return new NotificationChannelGroup[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        NotificationChannelGroup that = (NotificationChannelGroup) o;
+
+        if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
+        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
+            return false;
+        }
+        return getChannels() != null ? getChannels().equals(that.getChannels())
+                : that.getChannels() == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = getId() != null ? getId().hashCode() : 0;
+        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+        result = 31 * result + (getChannels() != null ? getChannels().hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "NotificationChannelGroup{" +
+                "mId='" + mId + '\'' +
+                ", mName=" + mName +
+                '}';
+    }
+}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 3551691..1f4fe67 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -24,6 +24,7 @@
 import android.app.Notification.Builder;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
@@ -382,8 +383,46 @@
     }
 
     /**
+     * Creates a group container for {@link NotificationChannel} objects.
+     *
+     * This is a no-op for groups that already exist.
+     * <p>
+     *     Group information is only used for presentation, not for behavior. Groups are optional
+     *     for channels, and you can have a mix of channels that belong to groups and channels
+     *     that do not.
+     * </p>
+     * <p>
+     *     For example, if your application supports multiple accounts, and those accounts will
+     *     have similar channels, you can create a group for each account with account specific
+     *     labels instead of appending account information to each channel's label.
+     * </p>
+     *
+     * @param group The group to create
+     */
+    public void createNotificationChannelGroup(@NonNull NotificationChannelGroup group) {
+        createNotificationChannelGroups(Arrays.asList(group));
+    }
+
+    /**
+     * Creates multiple notification channel groups.
+     *
+     * @param groups The list of groups to create
+     */
+    public void createNotificationChannelGroups(@NonNull List<NotificationChannelGroup> groups) {
+        INotificationManager service = getService();
+        try {
+            service.createNotificationChannelGroups(mContext.getPackageName(),
+                    new ParceledListSlice(groups));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates a notification channel that notifications can be posted to.
      *
+     * This is a no-op for channels that already exist.
+     *
      * @param channel  the channel to create.  Note that the created channel may differ from this
      *                 value.  If the channel already exists, it will not be modified.
      */
@@ -1085,4 +1124,38 @@
             default: return defValue;
         }
     }
+
+    /**
+     * Start a service directly into the "foreground service" state.  Unlike
+     * {@link android.content.Context#startService(Intent)}, this method
+     * can be used from within background operations like broadcast receivers
+     * or scheduled jobs.
+     *
+     * @param service Description of the service to be stopped.  The Intent must be either
+     *      fully explicit (supplying a component name) or specify a specific package
+     *      name it is targeted to.
+     * @param id The identifier for this notification as per
+     *      {@link #notify(int, Notification) NotificationManager.notify(int, Notification)};
+     *      must not be 0.
+     * @param notification The Notification to be displayed.
+     * @return If the service is being started or is already running, the
+     *      {@link ComponentName} of the actual service that was started is
+     *      returned; else if the service does not exist null is returned.
+     */
+    @Nullable
+    public ComponentName startServiceInForeground(Intent service,
+            int id, Notification notification) {
+        return mContext.startServiceInForeground(service, id, notification);
+    }
+
+    /**
+     * @hide like {@link #startServiceInForeground(Intent, int, Notification)}
+     * but for a specific user.
+     */
+    @Nullable
+    public ComponentName startServiceInForegroundAsUser(Intent service,
+            int id, Notification notification, UserHandle user) {
+        return mContext.startServiceInForegroundAsUser(service, id, notification, user);
+    }
+
 }
diff --git a/core/java/android/app/PictureInPictureArgs.aidl b/core/java/android/app/PictureInPictureArgs.aidl
new file mode 100644
index 0000000..49df39a
--- /dev/null
+++ b/core/java/android/app/PictureInPictureArgs.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+parcelable PictureInPictureArgs;
diff --git a/core/java/android/app/PictureInPictureArgs.java b/core/java/android/app/PictureInPictureArgs.java
new file mode 100644
index 0000000..fbdcbf4
--- /dev/null
+++ b/core/java/android/app/PictureInPictureArgs.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a set of arguments used to initialize the picture-in-picture mode.
+ */
+public final class PictureInPictureArgs implements Parcelable {
+
+    /**
+     * The expected aspect ratio of the picture-in-picture.
+     */
+    @Nullable
+    private Float mAspectRatio;
+
+    /**
+     * The set of actions that are associated with this activity when in picture in picture.
+     */
+    @Nullable
+    private List<RemoteAction> mUserActions;
+
+    PictureInPictureArgs(Parcel in) {
+        if (in.readInt() != 0) {
+            mAspectRatio = in.readFloat();
+        }
+        if (in.readInt() != 0) {
+            mUserActions = new ArrayList<>();
+            in.readParcelableList(mUserActions, RemoteAction.class.getClassLoader());
+        }
+    }
+
+    /**
+     * Creates a new set of picture-in-picture arguments.
+     */
+    public PictureInPictureArgs() {
+        // Empty constructor
+    }
+
+    /**
+     * Creates a new set of picture-in-picture arguments from the given {@param aspectRatio} and
+     * {@param actions}.
+     */
+    public PictureInPictureArgs(float aspectRatio, List<RemoteAction> actions) {
+        mAspectRatio = aspectRatio;
+        if (actions != null) {
+            mUserActions = new ArrayList<>(actions);
+        }
+    }
+
+    /**
+     * Copies the set parameters from the other picture-in-picture args.
+     * @hide
+     */
+    public void copyOnlySet(PictureInPictureArgs otherArgs) {
+        if (otherArgs.hasSetAspectRatio()) {
+            mAspectRatio = otherArgs.mAspectRatio;
+        }
+        if (otherArgs.hasSetActions()) {
+            mUserActions = otherArgs.mUserActions;
+        }
+    }
+
+    /**
+     * Sets the aspect ratio.
+     * @param aspectRatio the new aspect ratio for picture-in-picture.
+     */
+    public void setAspectRatio(float aspectRatio) {
+        mAspectRatio = aspectRatio;
+    }
+
+    /**
+     * @return the aspect ratio. If none is set, return 0.
+     * @hide
+     */
+    public float getAspectRatio() {
+        if (mAspectRatio != null) {
+            return mAspectRatio;
+        }
+        return 0f;
+    }
+
+    /**
+     * @return whether the aspect ratio is set.
+     * @hide
+     */
+    public boolean hasSetAspectRatio() {
+        return mAspectRatio != null;
+    }
+
+    /**
+     * Sets the user actions.
+     * @param actions the new actions to show in the picture-in-picture menu.
+     */
+    public void setActions(List<RemoteAction> actions) {
+        if (mUserActions != null) {
+            mUserActions = null;
+        }
+        if (actions != null) {
+            mUserActions = new ArrayList<>(actions);
+        }
+    }
+
+    /**
+     * @return the set of user actions.
+     * @hide
+     */
+    public List<RemoteAction> getActions() {
+        return mUserActions;
+    }
+
+    /**
+     * @return whether the user actions are set.
+     * @hide
+     */
+    public boolean hasSetActions() {
+        return mUserActions != null;
+    }
+
+    @Override
+    public PictureInPictureArgs clone() {
+        return new PictureInPictureArgs(mAspectRatio, mUserActions);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        if (mAspectRatio != null) {
+            out.writeInt(1);
+            out.writeFloat(mAspectRatio);
+        } else {
+            out.writeInt(0);
+        }
+        if (mUserActions != null) {
+            out.writeInt(1);
+            out.writeParcelableList(mUserActions, 0);
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    public static final Creator<PictureInPictureArgs> CREATOR =
+            new Creator<PictureInPictureArgs>() {
+                public PictureInPictureArgs createFromParcel(Parcel in) {
+                    return new PictureInPictureArgs(in);
+                }
+                public PictureInPictureArgs[] newArray(int size) {
+                    return new PictureInPictureArgs[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java
new file mode 100644
index 0000000..1f015a6
--- /dev/null
+++ b/core/java/android/app/RecoverableSecurityException.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Specialization of {@link SecurityException} that contains additional
+ * information about how to involve the end user to recover from the exception.
+ * <p>
+ * This exception is only appropriate where there is a concrete action the user
+ * can take to recover and make forward progress, such as confirming or entering
+ * authentication credentials.
+ * <p class="note">
+ * Note: legacy code that receives this exception may treat it as a general
+ * {@link SecurityException}, and thus there is no guarantee that the messages
+ * contained will be shown to the end user.
+ * </p>
+ */
+public final class RecoverableSecurityException extends SecurityException implements Parcelable {
+    private static final String TAG = "RecoverableSecurityException";
+
+    private final CharSequence mUserMessage;
+    private final CharSequence mUserActionTitle;
+    private final PendingIntent mUserAction;
+
+    /** {@hide} */
+    public RecoverableSecurityException(Parcel in) {
+        this(new SecurityException(in.readString()), in.readCharSequence(), in.readCharSequence(),
+                PendingIntent.CREATOR.createFromParcel(in));
+    }
+
+    /**
+     * Create an instance ready to be thrown.
+     *
+     * @param cause original cause with details designed for engineering
+     *            audiences.
+     * @param userMessage short message describing the issue for end user
+     *            audiences, which may be shown in a notification or dialog.
+     *            This should be less than 64 characters. For example: <em>PIN
+     *            required to access Document.pdf</em>
+     * @param userActionTitle short title describing the primary action. This
+     *            should be less than 24 characters. For example: <em>Enter
+     *            PIN</em>
+     * @param userAction primary action that will initiate the recovery. This
+     *            must launch an activity that is expected to set
+     *            {@link Activity#setResult(int)} before finishing to
+     *            communicate the final status of the recovery. For example,
+     *            apps that observe {@link Activity#RESULT_OK} may choose to
+     *            immediately retry their operation.
+     */
+    public RecoverableSecurityException(Throwable cause, CharSequence userMessage,
+            CharSequence userActionTitle, PendingIntent userAction) {
+        super(cause.getMessage());
+        mUserMessage = Preconditions.checkNotNull(userMessage);
+        mUserActionTitle = Preconditions.checkNotNull(userActionTitle);
+        mUserAction = Preconditions.checkNotNull(userAction);
+    }
+
+    /**
+     * Return short message describing the issue for end user audiences, which
+     * may be shown in a notification or dialog.
+     */
+    public CharSequence getUserMessage() {
+        return mUserMessage;
+    }
+
+    /**
+     * Return short title describing the primary action.
+     */
+    public CharSequence getUserActionTitle() {
+        return mUserActionTitle;
+    }
+
+    /**
+     * Return primary action that will initiate the recovery.
+     */
+    public PendingIntent getUserAction() {
+        return mUserAction;
+    }
+
+    /**
+     * Convenience method that will show a very simple notification populated
+     * with the details from this exception.
+     * <p>
+     * If you want more flexibility over retrying your original operation once
+     * the user action has finished, consider presenting your own UI that uses
+     * {@link Activity#startIntentSenderForResult} to launch the
+     * {@link PendingIntent#getIntentSender()} from {@link #getUserAction()}
+     * when requested. If the result of that activity is
+     * {@link Activity#RESULT_OK}, you should consider retrying.
+     * <p>
+     * This method will only display the most recent exception from any single
+     * remote UID; notifications from older exceptions will always be replaced.
+     */
+    public void showAsNotification(Context context) {
+        final Notification.Builder builder = new Notification.Builder(context)
+                .setSmallIcon(com.android.internal.R.drawable.ic_print_error)
+                .setContentTitle(mUserActionTitle)
+                .setContentText(mUserMessage)
+                .setContentIntent(mUserAction)
+                .setCategory(Notification.CATEGORY_ERROR);
+
+        final NotificationManager nm = context.getSystemService(NotificationManager.class);
+        nm.notify(TAG, mUserAction.getCreatorUid(), builder.build());
+    }
+
+    /**
+     * Convenience method that will show a very simple dialog populated with the
+     * details from this exception.
+     * <p>
+     * If you want more flexibility over retrying your original operation once
+     * the user action has finished, consider presenting your own UI that uses
+     * {@link Activity#startIntentSenderForResult} to launch the
+     * {@link PendingIntent#getIntentSender()} from {@link #getUserAction()}
+     * when requested. If the result of that activity is
+     * {@link Activity#RESULT_OK}, you should consider retrying.
+     * <p>
+     * This method will only display the most recent exception from any single
+     * remote UID; dialogs from older exceptions will always be replaced.
+     */
+    public void showAsDialog(Activity activity) {
+        final LocalDialog dialog = new LocalDialog();
+        final Bundle args = new Bundle();
+        args.putParcelable(TAG, this);
+        dialog.setArguments(args);
+
+        final String tag = TAG + "_" + mUserAction.getCreatorUid();
+        final FragmentManager fm = activity.getFragmentManager();
+        final FragmentTransaction ft = fm.beginTransaction();
+        final Fragment old = fm.findFragmentByTag(tag);
+        if (old != null) {
+            ft.remove(old);
+        }
+        ft.add(dialog, tag);
+        ft.commitAllowingStateLoss();
+    }
+
+    /** {@hide} */
+    public static class LocalDialog extends DialogFragment {
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final RecoverableSecurityException e = getArguments().getParcelable(TAG);
+            return new AlertDialog.Builder(getActivity())
+                    .setMessage(e.mUserMessage)
+                    .setPositiveButton(e.mUserActionTitle, (dialog, which) -> {
+                        try {
+                            e.mUserAction.send();
+                        } catch (PendingIntent.CanceledException ignored) {
+                        }
+                    })
+                    .setNegativeButton(android.R.string.cancel, null)
+                    .create();
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(getMessage());
+        dest.writeCharSequence(mUserMessage);
+        dest.writeCharSequence(mUserActionTitle);
+        mUserAction.writeToParcel(dest, flags);
+    }
+
+    public static final Creator<RecoverableSecurityException> CREATOR =
+            new Creator<RecoverableSecurityException>() {
+        @Override
+        public RecoverableSecurityException createFromParcel(Parcel source) {
+            return new RecoverableSecurityException(source);
+        }
+
+        @Override
+        public RecoverableSecurityException[] newArray(int size) {
+            return new RecoverableSecurityException[size];
+        }
+    };
+}
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 11420c5..d1dc859 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -19,9 +19,14 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArraySet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * A {@code RemoteInput} object specifies input to be collected from a user to be passed along with
@@ -61,9 +66,13 @@
     /** Label used to denote the clip data type used for remote input transport */
     public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
 
-    /** Extra added to a clip data intent object to hold the results bundle. */
+    /** Extra added to a clip data intent object to hold the text results bundle. */
     public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
 
+    /** Extra added to a clip data intent object to hold the data results bundle. */
+    private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
+            "android.remoteinput.dataTypeResultsData";
+
     // Flags bitwise-ored to mFlags
     private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
 
@@ -75,14 +84,16 @@
     private final CharSequence[] mChoices;
     private final int mFlags;
     private final Bundle mExtras;
+    private final ArraySet<String> mAllowedDataTypes;
 
     private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
-            int flags, Bundle extras) {
+            int flags, Bundle extras, ArraySet<String> allowedDataTypes) {
         this.mResultKey = resultKey;
         this.mLabel = label;
         this.mChoices = choices;
         this.mFlags = flags;
         this.mExtras = extras;
+        this.mAllowedDataTypes = allowedDataTypes;
     }
 
     /**
@@ -107,6 +118,21 @@
         return mChoices;
     }
 
+    public Set<String> getAllowedDataTypes() {
+        return mAllowedDataTypes;
+    }
+
+    /**
+     * Returns true if the input only accepts data, meaning {@link #getAllowFreeFormInput}
+     * is false, {@link #getChoices} is null or empty, and {@link #getAllowedDataTypes is
+     * non-null and not empty.
+     */
+    public boolean isDataOnly() {
+        return !getAllowFreeFormInput()
+                && (getChoices() == null || getChoices().length == 0)
+                && !getAllowedDataTypes().isEmpty();
+    }
+
     /**
      * Get whether or not users can provide an arbitrary value for
      * input. If you set this to {@code false}, users must select one of the
@@ -133,6 +159,7 @@
         private CharSequence[] mChoices;
         private int mFlags = DEFAULT_FLAGS;
         private Bundle mExtras = new Bundle();
+        private final ArraySet<String> mAllowedDataTypes = new ArraySet<>();
 
         /**
          * Create a builder object for {@link RemoteInput} objects.
@@ -177,14 +204,34 @@
         /**
          * Specifies whether the user can provide arbitrary values.
          *
-         * @param allowFreeFormInput The default is {@code true}.
-         *         If you specify {@code false}, you must provide a non-null
-         *         and non-empty array to {@link #setChoices} or an
+         * @param mimeType A mime type that results are allowed to come in.
+         *         Be aware that text results (see {@link #setAllowFreeFormInput}
+         *         are allowed by default. If you do not want text results you will have to
+         *         pass false to {@code setAllowFreeFormInput}.
+         * @param doAllow Whether the mime type should be allowed or not.
+         * @return this object for method chaining
+         */
+        public Builder setAllowDataType(String mimeType, boolean doAllow) {
+            if (doAllow) {
+                mAllowedDataTypes.add(mimeType);
+            } else {
+                mAllowedDataTypes.remove(mimeType);
+            }
+            return this;
+        }
+
+        /**
+         * Specifies whether the user can provide arbitrary text values.
+         *
+         * @param allowFreeFormTextInput The default is {@code true}.
+         *         If you specify {@code false}, you must either provide a non-null
+         *         and non-empty array to {@link #setChoices}, or enable a data result
+         *         in {@code setAllowDataType}. Otherwise an
          *         {@link IllegalArgumentException} is thrown.
          * @return this object for method chaining
          */
-        public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
-            setFlag(mFlags, allowFreeFormInput);
+        public Builder setAllowFreeFormInput(boolean allowFreeFormTextInput) {
+            setFlag(mFlags, allowFreeFormTextInput);
             return this;
         }
 
@@ -224,7 +271,8 @@
          * object.
          */
         public RemoteInput build() {
-            return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mExtras);
+            return new RemoteInput(
+                    mResultKey, mLabel, mChoices, mFlags, mExtras, mAllowedDataTypes);
         }
     }
 
@@ -234,32 +282,68 @@
         mChoices = in.readCharSequenceArray();
         mFlags = in.readInt();
         mExtras = in.readBundle();
+        mAllowedDataTypes = (ArraySet<String>) in.readArraySet(null);
     }
 
     /**
-     * Get the remote input results bundle from an intent. The returned Bundle will
-     * contain a key/value for every result key populated by remote input collector.
-     * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value.
+     * Similar as {@link #getResultsFromIntent} but retrieves data results for a
+     * specific RemoteInput result. To retrieve a value use:
+     * <pre>
+     * {@code
+     * Map<String, Uri> results =
+     *     RemoteInput.getDataResultsFromIntent(intent, REMOTE_INPUT_KEY);
+     * if (results != null) {
+     *   Uri data = results.get(MIME_TYPE_OF_INTEREST);
+     * }
+     * }
+     * </pre>
+     * @param intent The intent object that fired in response to an action or content intent
+     *               which also had one or more remote input requested.
+     * @param remoteInputResultKey The result key for the RemoteInput you want results for.
+     */
+    public static Map<String, Uri> getDataResultsFromIntent(
+            Intent intent, String remoteInputResultKey) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            return null;
+        }
+        Map<String, Uri> results = new HashMap<>();
+        Bundle extras = clipDataIntent.getExtras();
+        for (String key : extras.keySet()) {
+          if (key.startsWith(EXTRA_DATA_TYPE_RESULTS_DATA)) {
+              String mimeType = key.substring(EXTRA_DATA_TYPE_RESULTS_DATA.length());
+              if (mimeType == null || mimeType.isEmpty()) {
+                  continue;
+              }
+              Bundle bundle = clipDataIntent.getBundleExtra(key);
+              String uriStr = bundle.getString(remoteInputResultKey);
+              if (uriStr == null || uriStr.isEmpty()) {
+                  continue;
+              }
+              results.put(mimeType, Uri.parse(uriStr));
+          }
+        }
+        return results.isEmpty() ? null : results;
+    }
+
+    /**
+     * Get the remote input text results bundle from an intent. The returned Bundle will
+     * contain a key/value for every result key populated with text by remote input collector.
+     * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value. For non-text
+     * results use {@link #getDataResultsFromIntent}.
      * @param intent The intent object that fired in response to an action or content intent
      *               which also had one or more remote input requested.
      */
     public static Bundle getResultsFromIntent(Intent intent) {
-        ClipData clipData = intent.getClipData();
-        if (clipData == null) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
             return null;
         }
-        ClipDescription clipDescription = clipData.getDescription();
-        if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
-            return null;
-        }
-        if (clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
-            return clipData.getItemAt(0).getIntent().getExtras().getParcelable(EXTRA_RESULTS_DATA);
-        }
-        return null;
+        return clipDataIntent.getExtras().getParcelable(EXTRA_RESULTS_DATA);
     }
 
     /**
-     * Populate an intent object with the results gathered from remote input. This method
+     * Populate an intent object with the text results gathered from remote input. This method
      * should only be called by remote input collection services when sending results to a
      * pending intent.
      * @param remoteInputs The remote inputs for which results are being provided
@@ -267,20 +351,61 @@
      *               field of the intent will be modified to contain the results.
      * @param results A bundle holding the remote input results. This bundle should
      *                be populated with keys matching the result keys specified in
-     *                {@code remoteInputs} with values being the result per key.
+     *                {@code remoteInputs} with values being the CharSequence results per key.
      */
     public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
             Bundle results) {
-        Bundle resultsBundle = new Bundle();
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            clipDataIntent = new Intent();  // First time we've added a result.
+        }
+        Bundle resultsBundle = clipDataIntent.getBundleExtra(EXTRA_RESULTS_DATA);
+        if (resultsBundle == null) {
+            resultsBundle = new Bundle();
+        }
         for (RemoteInput remoteInput : remoteInputs) {
             Object result = results.get(remoteInput.getResultKey());
             if (result instanceof CharSequence) {
                 resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result);
             }
         }
-        Intent clipIntent = new Intent();
-        clipIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
-        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipIntent));
+        clipDataIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
+        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
+    }
+
+    /**
+     * Same as {@link #addResultsToIntent} but for setting data results.
+     * @param remoteInput The remote input for which results are being provided
+     * @param intent The intent to add remote input results to. The {@link ClipData}
+     *               field of the intent will be modified to contain the results.
+     * @param results A map of mime type to the Uri result for that mime type.
+     */
+    public static void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
+            Map<String, Uri> results) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            clipDataIntent = new Intent();  // First time we've added a result.
+        }
+        for (Map.Entry<String, Uri> entry : results.entrySet()) {
+            String mimeType = entry.getKey();
+            Uri uri = entry.getValue();
+            if (mimeType == null) {
+                continue;
+            }
+            Bundle resultsBundle =
+                    clipDataIntent.getBundleExtra(getExtraResultsKeyForData(mimeType));
+            if (resultsBundle == null) {
+                resultsBundle = new Bundle();
+            }
+            resultsBundle.putString(remoteInput.getResultKey(), uri.toString());
+
+            clipDataIntent.putExtra(getExtraResultsKeyForData(mimeType), resultsBundle);
+        }
+        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
+    }
+
+    private static String getExtraResultsKeyForData(String mimeType) {
+        return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType;
     }
 
     @Override
@@ -295,6 +420,7 @@
         out.writeCharSequenceArray(mChoices);
         out.writeInt(mFlags);
         out.writeBundle(mExtras);
+        out.writeArraySet(mAllowedDataTypes);
     }
 
     public static final Creator<RemoteInput> CREATOR = new Creator<RemoteInput>() {
@@ -308,4 +434,19 @@
             return new RemoteInput[size];
         }
     };
+
+    private static Intent getClipDataIntentFromIntent(Intent intent) {
+        ClipData clipData = intent.getClipData();
+        if (clipData == null) {
+            return null;
+        }
+        ClipDescription clipDescription = clipData.getDescription();
+        if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
+            return null;
+        }
+        if (!clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
+            return null;
+        }
+        return clipData.getItemAt(0).getIntent();
+    }
 }
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 45831a3..94a8990 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -916,44 +916,85 @@
                 }
             }
 
-            // Bail early if there is no work to do.
-            if (updatedResourceKeys.isEmpty()) {
-                return;
+            redirectResourcesToNewImplLocked(updatedResourceKeys);
+        }
+    }
+
+    // TODO(adamlesinski): Make this accept more than just overlay directories.
+    final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
+            @NonNull final String[] newResourceDirs) {
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                    "ResourcesManager#applyNewResourceDirsLocked");
+
+            final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+            final int implCount = mResourceImpls.size();
+            for (int i = 0; i < implCount; i++) {
+                final ResourcesKey key = mResourceImpls.keyAt(i);
+                final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+                final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+                if (impl != null && key.mResDir != null && key.mResDir.equals(baseCodePath)) {
+                    updatedResourceKeys.put(impl, new ResourcesKey(
+                            key.mResDir,
+                            key.mSplitResDirs,
+                            newResourceDirs,
+                            key.mLibDirs,
+                            key.mDisplayId,
+                            key.mOverrideConfiguration,
+                            key.mCompatInfo));
+                }
             }
 
-            // Update any references to ResourcesImpl that require reloading.
-            final int resourcesCount = mResourceReferences.size();
-            for (int i = 0; i < resourcesCount; i++) {
-                final Resources r = mResourceReferences.get(i).get();
+            invalidatePath("/");
+
+            redirectResourcesToNewImplLocked(updatedResourceKeys);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
+    }
+
+    private void redirectResourcesToNewImplLocked(
+            @NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) {
+        // Bail early if there is no work to do.
+        if (updatedResourceKeys.isEmpty()) {
+            return;
+        }
+
+        // Update any references to ResourcesImpl that require reloading.
+        final int resourcesCount = mResourceReferences.size();
+        for (int i = 0; i < resourcesCount; i++) {
+            final WeakReference<Resources> ref = mResourceReferences.get(i);
+            final Resources r = ref != null ? ref.get() : null;
+            if (r != null) {
+                final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+                if (key != null) {
+                    final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+                    if (impl == null) {
+                        throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
+                    }
+                    r.setImpl(impl);
+                }
+            }
+        }
+
+        // Update any references to ResourcesImpl that require reloading for each Activity.
+        for (ActivityResources activityResources : mActivityResourceReferences.values()) {
+            final int resCount = activityResources.activityResources.size();
+            for (int i = 0; i < resCount; i++) {
+                final WeakReference<Resources> ref = activityResources.activityResources.get(i);
+                final Resources r = ref != null ? ref.get() : null;
                 if (r != null) {
                     final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
                     if (key != null) {
                         final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
                         if (impl == null) {
-                            throw new Resources.NotFoundException("failed to load " + libAsset);
+                            throw new Resources.NotFoundException(
+                                    "failed to redirect ResourcesImpl");
                         }
                         r.setImpl(impl);
                     }
                 }
             }
-
-            // Update any references to ResourcesImpl that require reloading for each Activity.
-            for (ActivityResources activityResources : mActivityResourceReferences.values()) {
-                final int resCount = activityResources.activityResources.size();
-                for (int i = 0; i < resCount; i++) {
-                    final Resources r = activityResources.activityResources.get(i).get();
-                    if (r != null) {
-                        final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
-                        if (key != null) {
-                            final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
-                            if (impl == null) {
-                                throw new Resources.NotFoundException("failed to load " + libAsset);
-                            }
-                            r.setImpl(impl);
-                        }
-                    }
-                }
-            }
         }
     }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 9387019..5a75a67 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -112,13 +112,14 @@
 import android.os.storage.StorageManager;
 import android.print.IPrintManager;
 import android.print.PrintManager;
+import android.service.autofill.IAutoFillManagerService;
 import android.service.persistentdata.IPersistentDataBlockService;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.text.TextClassificationManager;
+import android.text.FontManager;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
@@ -126,13 +127,16 @@
 import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.CaptioningManager;
+import android.view.autofill.AutoFillManager;
 import android.view.inputmethod.InputMethodManager;
+import android.view.textclassifier.TextClassificationManager;
 import android.view.textservice.TextServicesManager;
 
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.app.ISoundTriggerService;
 import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.font.IFontManager;
 import com.android.internal.os.IDropBoxManagerService;
 import com.android.internal.policy.PhoneLayoutInflater;
 
@@ -226,10 +230,10 @@
             }});
 
         registerService(Context.TEXT_CLASSIFICATION_SERVICE, TextClassificationManager.class,
-                new StaticServiceFetcher<TextClassificationManager>() {
+                new CachedServiceFetcher<TextClassificationManager>() {
             @Override
-            public TextClassificationManager createService() {
-                return new TextClassificationManager();
+            public TextClassificationManager createService(ContextImpl ctx) {
+                return new TextClassificationManager(ctx);
             }});
 
         registerService(Context.CLIPBOARD_SERVICE, ClipboardManager.class,
@@ -793,6 +797,23 @@
             public IncidentManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                 return new IncidentManager(ctx);
             }});
+
+        registerService(Context.FONT_SERVICE, FontManager.class,
+                new CachedServiceFetcher<FontManager>() {
+                    @Override
+                    public FontManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(Context.FONT_SERVICE);
+                        return new FontManager(IFontManager.Stub.asInterface(b));
+                    }});
+        registerService(Context.AUTO_FILL_MANAGER_SERVICE, AutoFillManager.class,
+                new CachedServiceFetcher<AutoFillManager>() {
+            @Override
+            public AutoFillManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                IBinder b = ServiceManager.getServiceOrThrow(Context.AUTO_FILL_MANAGER_SERVICE);
+                IAutoFillManagerService service = IAutoFillManagerService.Stub.asInterface(b);
+                return new AutoFillManager(ctx, service);
+            }});
     }
 
     /**
diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java
index 0077db1..bab993f 100644
--- a/core/java/android/app/TaskStackBuilder.java
+++ b/core/java/android/app/TaskStackBuilder.java
@@ -226,7 +226,7 @@
      * Start the task stack constructed by this builder.
      *
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      */
     public void startActivities(Bundle options) {
@@ -259,7 +259,7 @@
      *              {@link Intent#fillIn(Intent, int)} to control which unspecified parts of the
      *              intent that can be supplied when the actual send happens.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @return The obtained PendingIntent
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index ad5e69b..35c67d3 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.app.ActivityManager.TaskSnapshot;
 import android.content.ComponentName;
 import android.os.RemoteException;
 
@@ -34,7 +35,7 @@
     }
 
     @Override
-    public void onPinnedActivityRestartAttempt() throws RemoteException {
+    public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) throws RemoteException {
     }
 
     @Override
@@ -78,4 +79,9 @@
     @Override
     public void onTaskProfileLocked(int taskId, int userId) {
     }
+
+    @Override
+    public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
+            throws RemoteException {
+    }
 }
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 2572a20..af41a7d 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -271,6 +271,21 @@
     }
 
     /**
+     * Gets the valid inputs to {@link #setTheme(String)}.
+     * @hide
+     */
+    public String[] getAvailableThemes() {
+        if (mService != null) {
+            try {
+                return mService.getAvailableThemes();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the currently configured night mode.
      * <p>
      * May be one of:
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index a248bce..34a0c30 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -707,17 +707,24 @@
     }
 
     /**
-     * Allows the receiver to be notified when information about a pending system update is
+     * Called when the information about a pending system update is available.
+     *
+     * <p>Allows the receiver to be notified when information about a pending system update is
      * available from the system update service. The same pending system update can trigger multiple
      * calls to this method, so it is necessary to examine the incoming parameters for details about
      * the update.
-     * <p>
-     * This callback is only applicable to device owners.
+     *
+     * <p>This callback is only applicable to device owners and profile owners.
+     *
+     * <p>To get further information about a pending system update (for example, whether or not the
+     * update is a security patch), the device owner or profile owner can call
+     * {@link DevicePolicyManager#getPendingSystemUpdate}.
      *
      * @param context The running context as per {@link #onReceive}.
      * @param intent The received intent as per {@link #onReceive}.
      * @param receivedTime The time as given by {@link System#currentTimeMillis()} indicating when
      *        the current pending update was first available. -1 if no pending update is available.
+     * @see DevicePolicyManager#getPendingSystemUpdate
      */
     public void onSystemUpdatePending(Context context, Intent intent, long receivedTime) {
     }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c95e011..0ff8550 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -28,7 +27,6 @@
 import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.Activity;
-import android.app.admin.PasswordMetrics;
 import android.app.IServiceConnection;
 import android.app.admin.SecurityLog.SecurityEvent;
 import android.content.ComponentName;
@@ -1093,6 +1091,30 @@
     public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
 
     /**
+     * Constant to indicate the feature of disabling the camera. Used as argument to
+     * {@link #createAdminSupportIntent(String)}.
+     * @see #setCameraDisabled(ComponentName, boolean)
+     */
+    public static final String POLICY_DISABLE_CAMERA = "policy_disable_camera";
+
+    /**
+     * Constant to indicate the feature of disabling screen captures. Used as argument to
+     * {@link #createAdminSupportIntent(String)}.
+     * @see #setScreenCaptureDisabled(ComponentName, boolean)
+     */
+    public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
+
+    /**
+     * A String indicating a specific restricted feature. Can be a user restriction from the
+     * {@link UserManager}, e.g. {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the values
+     * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+     * @see #createAdminSupportIntent(String)
+     * @hide
+     */
+    @TestApi
+    public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
+
+    /**
      * Activity action: have the user enter a new password. This activity should
      * be launched after using {@link #setPasswordQuality(ComponentName, int)},
      * or {@link #setPasswordMinimumLength(ComponentName, int)} to have the user
@@ -1134,6 +1156,23 @@
             = "android.app.action.SHOW_DEVICE_MONITORING_DIALOG";
 
     /**
+     * Broadcast Action: Sent after application delegation scopes are changed. The new list of
+     * delegation scopes will be sent in an extra identified by the {@link #EXTRA_DELEGATION_SCOPES}
+     * key.
+     *
+     * <p class=”note”> Note: This is a protected intent that can only be sent by the system.</p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED =
+            "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
+
+    /**
+     * A list of Strings corresponding to the delegation scopes given to an app in the
+     * {@link #ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED} broadcast.
+     */
+    public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
+
+    /**
      * Flag used by {@link #addCrossProfileIntentFilter} to allow activities in
      * the parent profile to access intents sent from the managed profile.
      * That is, when an app in the managed profile calls
@@ -1196,6 +1235,53 @@
     public static final int PERMISSION_GRANT_STATE_DENIED = 2;
 
     /**
+     * Delegation of certificate installation and management. This scope grants access to the
+     * {@link #getInstalledCaCerts}, {@link #hasCaCertInstalled}, {@link #installCaCert},
+     * {@link #uninstallCaCert}, {@link #uninstallAllUserCaCerts} and {@link #installKeyPair} APIs.
+     */
+    public static final String DELEGATION_CERT_INSTALL = "delegation-cert-install";
+
+    /**
+     * Delegation of application restrictions management. This scope grants access to the
+     * {@link #setApplicationRestrictions} and {@link #getApplicationRestrictions} APIs.
+     */
+    public static final String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions";
+
+    /**
+     * Delegation of application uninstall block. This scope grants access to the
+     * {@link #setUninstallBlocked} API.
+     */
+    public static final String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall";
+
+    /**
+     * Delegation of permission policy and permission grant state. This scope grants access to the
+     * {@link #setPermissionPolicy}, {@link #getPermissionGrantState},
+     * and {@link #setPermissionGrantState} APIs.
+     */
+    public static final String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
+
+    /**
+     * Delegation of package access state. This scope grants access to the
+     * {@link #isApplicationHidden}, {@link #setApplicationHidden}, {@link #isPackageSuspended}, and
+     * {@link #setPackagesSuspended} APIs.
+     */
+    public static final String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
+
+    /**
+     * Delegation for enabling system apps. This scope grants access to the {@link #enableSystemApp}
+     * API.
+     */
+    public static final String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
+
+    /**
+     * Delegation of management of uninstalled packages. This scope grants access to the
+     * {@code #setKeepUninstalledPackages} and {@code #getKeepUninstalledPackages} APIs.
+     * @hide
+     */
+    public static final String DELEGATION_KEEP_UNINSTALLED_PACKAGES =
+            "delegation-keep-uninstalled-packages";
+
+    /**
      * No management for current user in-effect. This is the default.
      * @hide
      */
@@ -1440,6 +1526,7 @@
         }
         return false;
     }
+
     /**
      * Return true if the given administrator component is currently being removed
      * for the user.
@@ -1456,7 +1543,6 @@
         return false;
     }
 
-
     /**
      * Return a list of all currently active device administrators' component
      * names.  If there are no administrators {@code null} may be
@@ -1487,6 +1573,7 @@
      * or uninstalled.
      * @hide
      */
+    @SystemApi
     public boolean packageHasActiveAdmins(String packageName) {
         return packageHasActiveAdmins(packageName, myUserId());
     }
@@ -3247,6 +3334,10 @@
     /**
      * Installs the given certificate as a user CA.
      *
+     * The caller must be a profile or device owner on that user, or a delegate package given the
+     * {@link #DELEGATION_CERT_INSTALL} scope via {@link #setDelegatedScopes}; otherwise a
+     * security exception will be thrown.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
      *              {@code null} if calling from a delegated certificate installer.
      * @param certBuffer encoded form of the certificate to install.
@@ -3255,12 +3346,14 @@
      *         interrupted, true otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_CERT_INSTALL
      */
     public boolean installCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
         throwIfParentInstance("installCaCert");
         if (mService != null) {
             try {
-                return mService.installCaCert(admin, certBuffer);
+                return mService.installCaCert(admin, mContext.getPackageName(), certBuffer);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -3271,18 +3364,24 @@
     /**
      * Uninstalls the given certificate from trusted user CAs, if present.
      *
+     * The caller must be a profile or device owner on that user, or a delegate package given the
+     * {@link #DELEGATION_CERT_INSTALL} scope via {@link #setDelegatedScopes}; otherwise a
+     * security exception will be thrown.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
      *              {@code null} if calling from a delegated certificate installer.
      * @param certBuffer encoded form of the certificate to remove.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_CERT_INSTALL
      */
     public void uninstallCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
         throwIfParentInstance("uninstallCaCert");
         if (mService != null) {
             try {
                 final String alias = getCaCertAlias(certBuffer);
-                mService.uninstallCaCerts(admin, new String[] {alias});
+                mService.uninstallCaCerts(admin, mContext.getPackageName(), new String[] {alias});
             } catch (CertificateException e) {
                 Log.w(TAG, "Unable to parse certificate", e);
             } catch (RemoteException e) {
@@ -3307,7 +3406,7 @@
         throwIfParentInstance("getInstalledCaCerts");
         if (mService != null) {
             try {
-                mService.enforceCanManageCaCerts(admin);
+                mService.enforceCanManageCaCerts(admin, mContext.getPackageName());
                 final TrustedCertificateStore certStore = new TrustedCertificateStore();
                 for (String alias : certStore.userAliases()) {
                     try {
@@ -3336,8 +3435,8 @@
         throwIfParentInstance("uninstallAllUserCaCerts");
         if (mService != null) {
             try {
-                mService.uninstallCaCerts(admin, new TrustedCertificateStore().userAliases()
-                        .toArray(new String[0]));
+                mService.uninstallCaCerts(admin, mContext.getPackageName(),
+                        new TrustedCertificateStore().userAliases() .toArray(new String[0]));
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -3357,7 +3456,7 @@
         throwIfParentInstance("hasCaCertInstalled");
         if (mService != null) {
             try {
-                mService.enforceCanManageCaCerts(admin);
+                mService.enforceCanManageCaCerts(admin, mContext.getPackageName());
                 return getCaCertAlias(certBuffer) != null;
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
@@ -3389,6 +3488,8 @@
      * @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.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_CERT_INSTALL
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
             @NonNull Certificate cert, @NonNull String alias) {
@@ -3420,6 +3521,8 @@
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner.
      * @see android.security.KeyChain#getCertificateChain
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_CERT_INSTALL
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
             @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
@@ -3432,8 +3535,8 @@
             }
             final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm())
                     .getKeySpec(privKey, PKCS8EncodedKeySpec.class).getEncoded();
-            return mService.installKeyPair(admin, pkcs8Key, pemCert, pemChain, alias,
-                    requestAccess);
+            return mService.installKeyPair(admin, mContext.getPackageName(), pkcs8Key, pemCert,
+                    pemChain, alias, requestAccess);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
@@ -3454,11 +3557,13 @@
      * @return {@code true} if the private key alias no longer exists, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_CERT_INSTALL
      */
     public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
         throwIfParentInstance("removeKeyPair");
         try {
-            return mService.removeKeyPair(admin, alias);
+            return mService.removeKeyPair(admin, mContext.getPackageName(), alias);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3494,7 +3599,11 @@
      * @param installerPackage The package name of the certificate installer which will be given
      *            access. If {@code null} is given the current package will be cleared.
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
+     *
+     * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #setDelegatedScopes}
+     * with the {@link #DELEGATION_CERT_INSTALL} scope instead.
      */
+    @Deprecated
     public void setCertInstallerPackage(@NonNull ComponentName admin, @Nullable String
             installerPackage) throws SecurityException {
         throwIfParentInstance("setCertInstallerPackage");
@@ -3508,14 +3617,19 @@
     }
 
     /**
-     * Called by a profile owner or device owner to retrieve the certificate installer for the user.
-     * null if none is set.
+     * Called by a profile owner or device owner to retrieve the certificate installer for the user,
+     * or {@code null} if none is set. If there are multiple delegates this function will return one
+     * of them.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return The package name of the current delegated certificate installer, or {@code null} if
      *         none is set.
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
+     *
+     * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #getDelegatePackages}
+     * with the {@link #DELEGATION_CERT_INSTALL} scope instead.
      */
+    @Deprecated
     public @Nullable String getCertInstallerPackage(@NonNull ComponentName admin)
             throws SecurityException {
         throwIfParentInstance("getCertInstallerPackage");
@@ -3530,6 +3644,83 @@
     }
 
     /**
+     * Called by a profile owner or device owner to grant access to privileged APIs to another app.
+     * Granted APIs are determined by {@code scopes}, which is a list of the {@code DELEGATION_*}
+     * constants.
+     * <p>
+     * Delegated scopes are a per-user state. The delegated access is persistent until it is later
+     * cleared by calling this method with an empty {@code scopes} list or uninstalling the
+     * {@code delegatePackage}.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param delegatePackage The package name of the app which will be given access.
+     * @param scopes The groups of privileged APIs whose access should be granted to
+     *            {@code delegatedPackage}.
+     * @throws SecurityException if {@code admin} is not a device or a profile owner.
+     */
+     public void setDelegatedScopes(@NonNull ComponentName admin, @NonNull String delegatePackage,
+            @NonNull List<String> scopes) {
+        throwIfParentInstance("setDelegatedScopes");
+        if (mService != null) {
+            try {
+                mService.setDelegatedScopes(admin, delegatePackage, scopes);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Called by a profile owner or device owner to retrieve a list of the scopes given to a
+     * delegate package. Other apps can use this method to retrieve their own delegated scopes by
+     * passing {@code null} for {@code admin} and their own package name as
+     * {@code delegatedPackage}.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if the caller is {@code delegatedPackage}.
+     * @param delegatedPackage The package name of the app whose scopes should be retrieved.
+     * @return A list containing the scopes given to {@code delegatedPackage}.
+     * @throws SecurityException if {@code admin} is not a device or a profile owner.
+     */
+     @NonNull
+     public List<String> getDelegatedScopes(@NonNull ComponentName admin,
+             @NonNull String delegatedPackage) {
+         throwIfParentInstance("getDelegatedScopes");
+         if (mService != null) {
+             try {
+                 return mService.getDelegatedScopes(admin, delegatedPackage);
+             } catch (RemoteException e) {
+                 throw e.rethrowFromSystemServer();
+             }
+         }
+         return null;
+    }
+
+    /**
+     * Called by a profile owner or device owner to retrieve a list of delegate packages that were
+     * granted a delegation scope.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param delegationScope The scope whose delegates should be retrieved.
+     * @return A list of package names of the current delegated packages for
+               {@code delegationScope}.
+     * @throws SecurityException if {@code admin} is not a device or a profile owner.
+     */
+     @Nullable
+     public List<String> getDelegatePackages(@NonNull ComponentName admin,
+             @NonNull String delegationScope) {
+        throwIfParentInstance("getDelegatePackages");
+        if (mService != null) {
+            try {
+                return mService.getDelegatePackages(admin, delegationScope);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Called by a device or profile owner to configure an always-on VPN connection through a
      * specific application for the current user.
      *
@@ -3736,13 +3927,13 @@
     }
 
     /**
-     * Called by a device owner to set whether auto time is required. If auto time is required the
-     * user cannot set the date and time, but has to use network date and time.
+     * Called by a device or profile owner to set whether auto time is required. If auto time is
+     * required, no user will be able set the date and time and network date and time will be used.
      * <p>
      * Note: if auto time is required the user can still manually set the time zone.
      * <p>
-     * The calling device admin must be a device owner. If it is not, a security exception will be
-     * thrown.
+     * The calling device admin must be a device or profile owner. If it is not, a security
+     * exception will be thrown.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param required Whether auto time is set required or not.
@@ -4434,7 +4625,9 @@
     }
 
     /**
-     * Called by device or profile owners to suspend packages for this user.
+     * Called by device or profile owners to suspend packages for this user. This function can be
+     * called by a device owner, profile owner, or by a delegate given the
+     * {@link #DELEGATION_PACKAGE_ACCESS} scope via {@link #setDelegatedScopes}.
      * <p>
      * A suspended package will not be able to start activities. Its notifications will be hidden,
      * it will not show up in recents, will not be able to show toasts or dialogs or ring the
@@ -4444,20 +4637,24 @@
      * package will no longer be suspended. The admin can block this by using
      * {@link #setUninstallBlocked}.
      *
-     * @param admin The name of the admin component to check.
+     * @param admin The name of the admin component to check, or {@code null} if the caller is a
+     *            package access delegate.
      * @param packageNames The package names to suspend or unsuspend.
      * @param suspended If set to {@code true} than the packages will be suspended, if set to
      *            {@code false} the packages will be unsuspended.
      * @return an array of package names for which the suspended status is not set as requested in
      *         this method.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PACKAGE_ACCESS
      */
     public @NonNull String[] setPackagesSuspended(@NonNull ComponentName admin,
             @NonNull String[] packageNames, boolean suspended) {
         throwIfParentInstance("setPackagesSuspended");
         if (mService != null) {
             try {
-                return mService.setPackagesSuspended(admin, packageNames, suspended);
+                return mService.setPackagesSuspended(admin, mContext.getPackageName(), packageNames,
+                        suspended);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -4466,21 +4663,26 @@
     }
 
     /**
-     * Called by device or profile owners to determine if a package is suspended.
+     * Determine if a package is suspended. This function can be called by a device owner, profile
+     * owner, or by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
+     * {@link #setDelegatedScopes}.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if the caller is a package access delegate.
      * @param packageName The name of the package to retrieve the suspended status of.
      * @return {@code true} if the package is suspended or {@code false} if the package is not
      *         suspended, could not be found or an error occurred.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      * @throws NameNotFoundException if the package could not be found.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PACKAGE_ACCESS
      */
     public boolean isPackageSuspended(@NonNull ComponentName admin, String packageName)
             throws NameNotFoundException {
         throwIfParentInstance("isPackageSuspended");
         if (mService != null) {
             try {
-                return mService.isPackageSuspended(admin, packageName);
+                return mService.isPackageSuspended(admin, mContext.getPackageName(), packageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             } catch (IllegalArgumentException ex) {
@@ -4687,7 +4889,11 @@
      *            APIs. If {@code null} is given the current package will be cleared.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      * @throws NameNotFoundException if {@code packageName} is not found
+     *
+     * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #setDelegatedScopes}
+     * with the {@link #DELEGATION_APP_RESTRICTIONS} scope instead.
      */
+    @Deprecated
     public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
             @Nullable String packageName) throws NameNotFoundException {
         throwIfParentInstance("setApplicationRestrictionsManagingPackage");
@@ -4704,14 +4910,20 @@
 
     /**
      * Called by a profile owner or device owner to retrieve the application restrictions managing
-     * package for the current user, or {@code null} if none is set.
+     * package for the current user, or {@code null} if none is set. If there are multiple
+     * delegates this function will return one of them.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return The package name allowed to manage application restrictions on the current user, or
      *         {@code null} if none is set.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
+     *
+     * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #getDelegatePackages}
+     * with the {@link #DELEGATION_APP_RESTRICTIONS} scope instead.
      */
-    public @Nullable String getApplicationRestrictionsManagingPackage(
+    @Deprecated
+    @Nullable
+    public String getApplicationRestrictionsManagingPackage(
             @NonNull ComponentName admin) {
         throwIfParentInstance("getApplicationRestrictionsManagingPackage");
         if (mService != null) {
@@ -4731,12 +4943,17 @@
      *
      * <p>This is done by comparing the calling Linux uid with the uid of the package specified by
      * that method.
+     *
+     * @deprecated From {@link android.os.Build.VERSION_CODES#O}. Use {@link #getDelegatedScopes}
+     * instead.
      */
+    @Deprecated
     public boolean isCallerApplicationRestrictionsManagingPackage() {
         throwIfParentInstance("isCallerApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
-                return mService.isCallerApplicationRestrictionsManagingPackage();
+                return mService.isCallerApplicationRestrictionsManagingPackage(
+                        mContext.getPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -4748,8 +4965,8 @@
      * Sets the application restrictions for a given target application running in the calling user.
      * <p>
      * The caller must be a profile or device owner on that user, or the package allowed to manage
-     * application restrictions via {@link #setApplicationRestrictionsManagingPackage}; otherwise a
-     * security exception will be thrown.
+     * application restrictions via {@link #setDelegatedScopes} with the
+     * {@link #DELEGATION_APP_RESTRICTIONS} scope; otherwise a security exception will be thrown.
      * <p>
      * The provided {@link Bundle} consists of key-value pairs, where the types of values may be:
      * <ul>
@@ -4776,7 +4993,8 @@
      * @param settings A {@link Bundle} to be parsed by the receiving application, conveying a new
      *            set of active restrictions.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
-     * @see #setApplicationRestrictionsManagingPackage
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_APP_RESTRICTIONS
      * @see UserManager#KEY_RESTRICTIONS_PENDING
      */
     @WorkerThread
@@ -4785,7 +5003,8 @@
         throwIfParentInstance("setApplicationRestrictions");
         if (mService != null) {
             try {
-                mService.setApplicationRestrictions(admin, packageName, settings);
+                mService.setApplicationRestrictions(admin, mContext.getPackageName(), packageName,
+                        settings);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5333,19 +5552,24 @@
     }
 
     /**
-     * Called by a device owner to get the list of apps to keep around as APKs even if no user has
-     * currently installed it.
+     * Get the list of apps to keep around as APKs even if no user has currently installed it. This
+     * function can be called by a device owner or by a delegate given the
+     * {@link #DELEGATION_KEEP_UNINSTALLED_PACKAGES} scope via {@link #setDelegatedScopes}.
+     * <p>
+     * Please note that packages returned in this method are not automatically pre-cached.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if the caller is a keep uninstalled packages delegate.
      * @return List of package names to keep cached.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_KEEP_UNINSTALLED_PACKAGES
      * @hide
      */
-    public @Nullable List<String> getKeepUninstalledPackages(@NonNull ComponentName admin) {
+    public @Nullable List<String> getKeepUninstalledPackages(@Nullable ComponentName admin) {
         throwIfParentInstance("getKeepUninstalledPackages");
         if (mService != null) {
             try {
-                return mService.getKeepUninstalledPackages(admin);
+                return mService.getKeepUninstalledPackages(admin, mContext.getPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5354,23 +5578,27 @@
     }
 
     /**
-     * Called by a device owner to set a list of apps to keep around as APKs even if no user has
-     * currently installed it.
+     * Set a list of apps to keep around as APKs even if no user has currently installed it. This
+     * function can be called by a device owner or by a delegate given the
+     * {@link #DELEGATION_KEEP_UNINSTALLED_PACKAGES} scope via {@link #setDelegatedScopes}.
      *
      * <p>Please note that setting this policy does not imply that specified apps will be
      * automatically pre-cached.</p>
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if the caller is a keep uninstalled packages delegate.
      * @param packageNames List of package names to keep cached.
      * @throws SecurityException if {@code admin} is not a device owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_KEEP_UNINSTALLED_PACKAGES
      * @hide
      */
-    public void setKeepUninstalledPackages(@NonNull ComponentName admin,
+    public void setKeepUninstalledPackages(@Nullable ComponentName admin,
             @NonNull List<String> packageNames) {
         throwIfParentInstance("setKeepUninstalledPackages");
         if (mService != null) {
             try {
-                mService.setKeepUninstalledPackages(admin, packageNames);
+                mService.setKeepUninstalledPackages(admin, mContext.getPackageName(), packageNames);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5524,8 +5752,8 @@
      * user.
      * <p>
      * The caller must be a profile or device owner on that user, or the package allowed to manage
-     * application restrictions via {@link #setApplicationRestrictionsManagingPackage}; otherwise a
-     * security exception will be thrown.
+     * application restrictions via {@link #setDelegatedScopes} with the
+     * {@link #DELEGATION_APP_RESTRICTIONS} scope; otherwise a security exception will be thrown.
      *
      * <p>NOTE: The method performs disk I/O and shouldn't be called on the main thread
      *
@@ -5536,7 +5764,8 @@
      *         {@link DevicePolicyManager#setApplicationRestrictions} was called, or an empty
      *         {@link Bundle} if no restrictions have been set.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
-     * @see {@link #setApplicationRestrictionsManagingPackage}
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_APP_RESTRICTIONS
      */
     @WorkerThread
     public @NonNull Bundle getApplicationRestrictions(
@@ -5544,7 +5773,8 @@
         throwIfParentInstance("getApplicationRestrictions");
         if (mService != null) {
             try {
-                return mService.getApplicationRestrictions(admin, packageName);
+                return mService.getApplicationRestrictions(admin, mContext.getPackageName(),
+                        packageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5621,22 +5851,55 @@
     }
 
     /**
-     * Called by profile or device owners to hide or unhide packages. When a package is hidden it is
-     * unavailable for use, but the data and actual package file remain.
+     * Called by any app to display a support dialog when a feature was disabled by an admin.
+     * This returns an intent that can be used with {@link Context#startActivity(Intent)} to
+     * display the dialog. It will tell the user that the feature indicated by {@code restriction}
+     * was disabled by an admin, and include a link for more information. The default content of
+     * the dialog can be changed by the restricting admin via
+     * {@link #setShortSupportMessage(ComponentName, CharSequence)}. If the restriction is not
+     * set (i.e. the feature is available), then the return value will be {@code null}.
+     * @param restriction Indicates for which feature the dialog should be displayed. Can be a
+     *            user restriction from {@link UserManager}, e.g.
+     *            {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
+     *            {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+     * @return Intent An intent to be used to start the dialog-activity if the restriction is
+     *            set by an admin, or null if the restriction does not exist or no admin set it.
+     */
+    public Intent createAdminSupportIntent(@NonNull String restriction) {
+        throwIfParentInstance("createAdminSupportIntent");
+        if (mService != null) {
+            try {
+                return mService.createAdminSupportIntent(restriction);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Hide or unhide packages. When a package is hidden it is unavailable for use, but the data and
+     * actual package file remain. This function can be called by a device owner, profile owner, or
+     * by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
+     * {@link #setDelegatedScopes}.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if the caller is a package access delegate.
      * @param packageName The name of the package to hide or unhide.
      * @param hidden {@code true} if the package should be hidden, {@code false} if it should be
      *            unhidden.
      * @return boolean Whether the hidden setting of the package was successfully updated.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PACKAGE_ACCESS
      */
     public boolean setApplicationHidden(@NonNull ComponentName admin, String packageName,
             boolean hidden) {
         throwIfParentInstance("setApplicationHidden");
         if (mService != null) {
             try {
-                return mService.setApplicationHidden(admin, packageName, hidden);
+                return mService.setApplicationHidden(admin, mContext.getPackageName(), packageName,
+                        hidden);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5645,18 +5908,23 @@
     }
 
     /**
-     * Called by profile or device owners to determine if a package is hidden.
+     * Determine if a package is hidden. This function can be called by a device owner, profile
+     * owner, or by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
+     * {@link #setDelegatedScopes}.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if the caller is a package access delegate.
      * @param packageName The name of the package to retrieve the hidden status of.
      * @return boolean {@code true} if the package is hidden, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PACKAGE_ACCESS
      */
     public boolean isApplicationHidden(@NonNull ComponentName admin, String packageName) {
         throwIfParentInstance("isApplicationHidden");
         if (mService != null) {
             try {
-                return mService.isApplicationHidden(admin, packageName);
+                return mService.isApplicationHidden(admin, mContext.getPackageName(), packageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5665,18 +5933,22 @@
     }
 
     /**
-     * Called by profile or device owners to re-enable a system app that was disabled by default
-     * when the user was initialized.
+     * Re-enable a system app that was disabled by default when the user was initialized. This
+     * function can be called by a device owner, profile owner, or by a delegate given the
+     * {@link #DELEGATION_ENABLE_SYSTEM_APP} scope via {@link #setDelegatedScopes}.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if the caller is an enable system app delegate.
      * @param packageName The package to be re-enabled in the calling profile.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PACKAGE_ACCESS
      */
     public void enableSystemApp(@NonNull ComponentName admin, String packageName) {
         throwIfParentInstance("enableSystemApp");
         if (mService != null) {
             try {
-                mService.enableSystemApp(admin, packageName);
+                mService.enableSystemApp(admin, mContext.getPackageName(), packageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5684,20 +5956,24 @@
     }
 
     /**
-     * Called by profile or device owners to re-enable system apps by intent that were disabled by
-     * default when the user was initialized.
+     * Re-enable system apps by intent that were disabled by default when the user was initialized.
+     * This function can be called by a device owner, profile owner, or by a delegate given the
+     * {@link #DELEGATION_ENABLE_SYSTEM_APP} scope via {@link #setDelegatedScopes}.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if the caller is an enable system app delegate.
      * @param intent An intent matching the app(s) to be installed. All apps that resolve for this
      *            intent will be re-enabled in the calling profile.
      * @return int The number of activities that matched the intent and were installed.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PACKAGE_ACCESS
      */
     public int enableSystemApp(@NonNull ComponentName admin, Intent intent) {
         throwIfParentInstance("enableSystemApp");
         if (mService != null) {
             try {
-                return mService.enableSystemAppWithIntent(admin, intent);
+                return mService.enableSystemAppWithIntent(admin, mContext.getPackageName(), intent);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5975,19 +6251,25 @@
     }
 
     /**
-     * Called by profile or device owners to change whether a user can uninstall a package.
+     * Change whether a user can uninstall a package. This function can be called by a device owner,
+     * profile owner, or by a delegate given the {@link #DELEGATION_BLOCK_UNINSTALL} scope via
+     * {@link #setDelegatedScopes}.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *             {@code null} if the caller is a block uninstall delegate.
      * @param packageName package to change.
      * @param uninstallBlocked true if the user shouldn't be able to uninstall the package.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_BLOCK_UNINSTALL
      */
-    public void setUninstallBlocked(@NonNull ComponentName admin, String packageName,
+    public void setUninstallBlocked(@Nullable ComponentName admin, String packageName,
             boolean uninstallBlocked) {
         throwIfParentInstance("setUninstallBlocked");
         if (mService != null) {
             try {
-                mService.setUninstallBlocked(admin, packageName, uninstallBlocked);
+                mService.setUninstallBlocked(admin, mContext.getPackageName(), packageName,
+                    uninstallBlocked);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -6200,12 +6482,18 @@
     }
 
     /**
-     * Callable by the system update service to notify device owners about pending updates.
-     * The caller must hold {@link android.Manifest.permission#NOTIFY_PENDING_SYSTEM_UPDATE}
-     * permission.
+     * Called by the system update service to notify device and profile owners of pending system
+     * updates.
      *
-     * @param updateReceivedTime The time as given by {@link System#currentTimeMillis()} indicating
-     *        when the current pending update was first available. -1 if no update is available.
+     * The caller must hold {@link android.Manifest.permission#NOTIFY_PENDING_SYSTEM_UPDATE}
+     * permission. This method should only be used when it is unknown whether the pending system
+     * update is a security patch. Otherwise, use
+     * {@link #notifyPendingSystemUpdate(long, boolean)}.
+     *
+     * @param updateReceivedTime The time as given by {@link System#currentTimeMillis()}
+     *         indicating when the current pending update was first available. {@code -1} if no
+     *         update is available.
+     * @see #notifyPendingSystemUpdate(long, boolean)
      * @hide
      */
     @SystemApi
@@ -6213,7 +6501,7 @@
         throwIfParentInstance("notifyPendingSystemUpdate");
         if (mService != null) {
             try {
-                mService.notifyPendingSystemUpdate(updateReceivedTime);
+                mService.notifyPendingSystemUpdate(SystemUpdateInfo.of(updateReceivedTime));
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -6221,12 +6509,60 @@
     }
 
     /**
-     * Called by profile or device owners to set the default response for future runtime permission
-     * requests by applications. The policy can allow for normal operation which prompts the user to
-     * grant a permission, or can allow automatic granting or denying of runtime permission requests
-     * by an application. This also applies to new permissions declared by app updates. When a
-     * permission is denied or granted this way, the effect is equivalent to setting the permission
-     * grant state via {@link #setPermissionGrantState}.
+     * Called by the system update service to notify device and profile owners of pending system
+     * updates.
+     *
+     * The caller must hold {@link android.Manifest.permission#NOTIFY_PENDING_SYSTEM_UPDATE}
+     * permission. This method should be used instead of {@link #notifyPendingSystemUpdate(long)}
+     * when it is known whether the pending system update is a security patch.
+     *
+     * @param updateReceivedTime The time as given by {@link System#currentTimeMillis()}
+     *         indicating when the current pending update was first available. {@code -1} if no
+     *         update is available.
+     * @param isSecurityPatch {@code true} if this system update is purely a security patch;
+     *         {@code false} if not.
+     * @see #notifyPendingSystemUpdate(long)
+     * @hide
+     */
+    @SystemApi
+    public void notifyPendingSystemUpdate(long updateReceivedTime, boolean isSecurityPatch) {
+        throwIfParentInstance("notifyPendingSystemUpdate");
+        if (mService != null) {
+            try {
+                mService.notifyPendingSystemUpdate(SystemUpdateInfo.of(updateReceivedTime,
+                        isSecurityPatch));
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Called by device or profile owners to get information about a pending system update.
+     *
+     * @param admin Which profile or device owner this request is associated with.
+     * @return Information about a pending system update or {@code null} if no update pending.
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @see DeviceAdminReceiver#onSystemUpdatePending(Context, Intent, long)
+     */
+    public @Nullable SystemUpdateInfo getPendingSystemUpdate(@NonNull ComponentName admin) {
+        throwIfParentInstance("getPendingSystemUpdate");
+        try {
+            return mService.getPendingSystemUpdate(admin);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the default response for future runtime permission requests by applications. This
+     * function can be called by a device owner, profile owner, or by a delegate given the
+     * {@link #DELEGATION_PERMISSION_GRANT} scope via {@link #setDelegatedScopes}.
+     * The policy can allow for normal operation which prompts the user to grant a permission, or
+     * can allow automatic granting or denying of runtime permission requests by an application.
+     * This also applies to new permissions declared by app updates. When a permission is denied or
+     * granted this way, the effect is equivalent to setting the permission * grant state via
+     * {@link #setPermissionGrantState}.
      * <p/>
      * As this policy only acts on runtime permission requests, it only applies to applications
      * built with a {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#M} or later.
@@ -6236,11 +6572,13 @@
      *            {@link #PERMISSION_POLICY_AUTO_GRANT} and {@link #PERMISSION_POLICY_AUTO_DENY}.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      * @see #setPermissionGrantState
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PERMISSION_GRANT
      */
     public void setPermissionPolicy(@NonNull ComponentName admin, int policy) {
         throwIfParentInstance("setPermissionPolicy");
         try {
-            mService.setPermissionPolicy(admin, policy);
+            mService.setPermissionPolicy(admin, mContext.getPackageName(), policy);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -6249,6 +6587,7 @@
     /**
      * Returns the current runtime permission policy set by the device or profile owner. The
      * default is {@link #PERMISSION_POLICY_PROMPT}.
+     *
      * @param admin Which profile or device owner this request is associated with.
      * @return the current policy for future permission requests.
      */
@@ -6268,7 +6607,8 @@
      * cannot manage it through the UI, and {@link #PERMISSION_GRANT_STATE_GRANTED granted} in which
      * the permission is granted and the user cannot manage it through the UI. This might affect all
      * permissions in a group that the runtime permission belongs to. This method can only be called
-     * by a profile or device owner.
+     * by a profile owner, device owner, or a delegate given the
+     * {@link #DELEGATION_PERMISSION_GRANT} scope via {@link #setDelegatedScopes}.
      * <p/>
      * Setting the grant state to {@link #PERMISSION_GRANT_STATE_DEFAULT default} does not revoke
      * the permission. It retains the previous grant, if any.
@@ -6287,21 +6627,27 @@
      * @see #PERMISSION_GRANT_STATE_DENIED
      * @see #PERMISSION_GRANT_STATE_DEFAULT
      * @see #PERMISSION_GRANT_STATE_GRANTED
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PERMISSION_GRANT
      */
     public boolean setPermissionGrantState(@NonNull ComponentName admin, String packageName,
             String permission, int grantState) {
         throwIfParentInstance("setPermissionGrantState");
         try {
-            return mService.setPermissionGrantState(admin, packageName, permission, grantState);
+            return mService.setPermissionGrantState(admin, mContext.getPackageName(), packageName,
+                    permission, grantState);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Returns the current grant state of a runtime permission for a specific application.
+     * Returns the current grant state of a runtime permission for a specific application. This
+     * function can be called by a device owner, profile owner, or by a delegate given the
+     * {@link #DELEGATION_PERMISSION_GRANT} scope via {@link #setDelegatedScopes}.
      *
-     * @param admin Which profile or device owner this request is associated with.
+     * @param admin Which profile or device owner this request is associated with, or {@code null}
+     *            if the caller is a permission grant delegate.
      * @param packageName The application to check the grant state for.
      * @param permission The permission to check for.
      * @return the current grant state specified by device policy. If the profile or device owner
@@ -6316,12 +6662,15 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      * @see #setPermissionGrantState(ComponentName, String, String, int)
      * @see PackageManager#checkPermission(String, String)
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_PERMISSION_GRANT
      */
-    public int getPermissionGrantState(@NonNull ComponentName admin, String packageName,
+    public int getPermissionGrantState(@Nullable ComponentName admin, String packageName,
             String permission) {
         throwIfParentInstance("getPermissionGrantState");
         try {
-            return mService.getPermissionGrantState(admin, packageName, permission);
+            return mService.getPermissionGrantState(admin, mContext.getPackageName(), packageName,
+                    permission);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -7122,15 +7471,14 @@
     }
 
     /**
-     * @hide
-     * Enable backup service.
-     * <p>This includes all backup and restore mechanisms.
-     * Setting this to {@code false} will make backup service no-op or return empty results.
+     * Allows the device owner to enable or disable the backup service.
      *
-     * <p>There must be only one user on the device, managed by the device owner.
-     * Otherwise a {@link SecurityException} will be thrown.
+     * <p> Backup service manages all backup and restore mechanisms on the device. Setting this to
+     * false will prevent data from being backed up or restored.
      *
-     * <p>Backup service is off by default when device owner is present.
+     * <p> Backup service is off by default when device owner is present.
+     *
+     * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setBackupServiceEnabled(@NonNull ComponentName admin, boolean enabled) {
         try {
@@ -7141,8 +7489,12 @@
     }
 
     /**
-     * @hide
+     * Return whether the backup service is enabled by the device owner.
+     *
+     * <p> Backup service manages all backup and restore mechanisms on the device.
+     *
      * @return {@code true} if backup service is enabled, {@code false} otherwise.
+     * @see #setBackupServiceEnabled
      */
     public boolean isBackupServiceEnabled(@NonNull ComponentName admin) {
         try {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 66185d5..79fe10e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -20,6 +20,7 @@
 import android.app.admin.NetworkEvent;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
+import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.PasswordMetrics;
 import android.content.ComponentName;
@@ -149,20 +150,24 @@
     void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo);
     CharSequence getDeviceOwnerLockScreenInfo();
 
-    String[] setPackagesSuspended(in ComponentName admin, in String[] packageNames, boolean suspended);
-    boolean isPackageSuspended(in ComponentName admin, String packageName);
+    String[] setPackagesSuspended(in ComponentName admin, in String callerPackage, in String[] packageNames, boolean suspended);
+    boolean isPackageSuspended(in ComponentName admin, in String callerPackage, String packageName);
 
-    boolean installCaCert(in ComponentName admin, in byte[] certBuffer);
-    void uninstallCaCerts(in ComponentName admin, in String[] aliases);
-    void enforceCanManageCaCerts(in ComponentName admin);
+    boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer);
+    void uninstallCaCerts(in ComponentName admin, String callerPackage, in String[] aliases);
+    void enforceCanManageCaCerts(in ComponentName admin, in String callerPackage);
     boolean approveCaCert(in String alias, int userHandle, boolean approval);
     boolean isCaCertApproved(in String alias, int userHandle);
 
-    boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer,
-            in byte[] certChainBuffer, String alias, boolean requestAccess);
-    boolean removeKeyPair(in ComponentName who, String alias);
+    boolean installKeyPair(in ComponentName who, in String callerPackage, in byte[] privKeyBuffer,
+            in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess);
+    boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
     void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
 
+    void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes);
+    List<String> getDelegatedScopes(in ComponentName who, String delegatePackage);
+    List<String> getDelegatePackages(in ComponentName who, String scope);
+
     void setCertInstallerPackage(in ComponentName who, String installerPackage);
     String getCertInstallerPackage(in ComponentName who);
 
@@ -172,11 +177,11 @@
     void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
     void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
 
-    void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
-    Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+    void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings);
+    Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName);
     boolean setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName);
     String getApplicationRestrictionsManagingPackage(in ComponentName admin);
-    boolean isCallerApplicationRestrictionsManagingPackage();
+    boolean isCallerApplicationRestrictionsManagingPackage(in String callerPackage);
 
     void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
     ComponentName getRestrictionsProvider(int userHandle);
@@ -196,15 +201,16 @@
     List getPermittedInputMethodsForCurrentUser();
     boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId);
 
-    boolean setApplicationHidden(in ComponentName admin, in String packageName, boolean hidden);
-    boolean isApplicationHidden(in ComponentName admin, in String packageName);
+    Intent createAdminSupportIntent(in String restriction);
+    boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden);
+    boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName);
 
     UserHandle createAndManageUser(in ComponentName who, in String name, in ComponentName profileOwner, in PersistableBundle adminExtras, in int flags);
     boolean removeUser(in ComponentName who, in UserHandle userHandle);
     boolean switchUser(in ComponentName who, in UserHandle userHandle);
 
-    void enableSystemApp(in ComponentName admin, in String packageName);
-    int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
+    void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
+    int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
 
     void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
     String[] getAccountTypesWithManagementDisabled();
@@ -222,7 +228,7 @@
 
     void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userId);
 
-    void setUninstallBlocked(in ComponentName admin, in String packageName, boolean uninstallBlocked);
+    void setUninstallBlocked(in ComponentName admin, in String callerPackage, in String packageName, boolean uninstallBlocked);
     boolean isUninstallBlocked(in ComponentName admin, in String packageName);
 
     void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
@@ -263,17 +269,18 @@
     boolean setStatusBarDisabled(in ComponentName who, boolean disabled);
     boolean getDoNotAskCredentialsOnBoot();
 
-    void notifyPendingSystemUpdate(in long updateReceivedTime);
+    void notifyPendingSystemUpdate(in SystemUpdateInfo info);
+    SystemUpdateInfo getPendingSystemUpdate(in ComponentName admin);
 
-    void setPermissionPolicy(in ComponentName admin, int policy);
+    void setPermissionPolicy(in ComponentName admin, in String callerPackage, int policy);
     int  getPermissionPolicy(in ComponentName admin);
-    boolean setPermissionGrantState(in ComponentName admin, String packageName,
+    boolean setPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName,
             String permission, int grantState);
-    int getPermissionGrantState(in ComponentName admin, String packageName, String permission);
+    int getPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName, String permission);
     boolean isProvisioningAllowed(String action, String packageName);
     int checkProvisioningPreCondition(String action, String packageName);
-    void setKeepUninstalledPackages(in ComponentName admin,in List<String> packageList);
-    List<String> getKeepUninstalledPackages(in ComponentName admin);
+    void setKeepUninstalledPackages(in ComponentName admin, in String callerPackage, in List<String> packageList);
+    List<String> getKeepUninstalledPackages(in ComponentName admin, in String callerPackage);
     boolean isManagedProfile(in ComponentName admin);
     boolean isSystemOnlyUser(in ComponentName admin);
     String getWifiMacAddress(in ComponentName admin);
diff --git a/core/java/android/app/admin/SystemUpdateInfo.aidl b/core/java/android/app/admin/SystemUpdateInfo.aidl
new file mode 100644
index 0000000..6d14904
--- /dev/null
+++ b/core/java/android/app/admin/SystemUpdateInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app.admin;
+
+parcelable SystemUpdateInfo;
diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java
new file mode 100644
index 0000000..6bb9f2d
--- /dev/null
+++ b/core/java/android/app/admin/SystemUpdateInfo.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A class containing information about a pending system update.
+ */
+public final class SystemUpdateInfo implements Parcelable {
+
+    /**
+     * Represents it is unknown whether the system update is a security patch.
+     */
+    public static final int SECURITY_PATCH_STATE_UNKNOWN = 0;
+
+    /**
+     * Represents the system update is not a security patch.
+     */
+    public static final int SECURITY_PATCH_STATE_FALSE = 1;
+
+    /**
+     * Represents the system update is a security patch.
+     */
+    public static final int SECURITY_PATCH_STATE_TRUE = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SECURITY_PATCH_STATE_FALSE, SECURITY_PATCH_STATE_TRUE, SECURITY_PATCH_STATE_UNKNOWN})
+    public @interface SecurityPatchState {}
+
+    private static final String ATTR_RECEIVED_TIME = "received-time";
+    private static final String ATTR_SECURITY_PATCH_STATE = "security-patch-state";
+    // Tag used to store original build fingerprint to detect when the update is applied.
+    private static final String ATTR_ORIGINAL_BUILD = "original-build";
+
+    private final long mReceivedTime;
+    @SecurityPatchState
+    private final int mSecurityPatchState;
+
+    private SystemUpdateInfo(long receivedTime, @SecurityPatchState int securityPatchState) {
+        this.mReceivedTime = receivedTime;
+        this.mSecurityPatchState = securityPatchState;
+    }
+
+    private SystemUpdateInfo(Parcel in) {
+        mReceivedTime = in.readLong();
+        mSecurityPatchState = in.readInt();
+    }
+
+    /** @hide */
+    @Nullable
+    public static SystemUpdateInfo of(long receivedTime) {
+        return receivedTime == -1
+                ? null : new SystemUpdateInfo(receivedTime, SECURITY_PATCH_STATE_UNKNOWN);
+    }
+
+    /** @hide */
+    @Nullable
+    public static SystemUpdateInfo of(long receivedTime, boolean isSecurityPatch) {
+        return receivedTime == -1 ? null : new SystemUpdateInfo(receivedTime,
+                isSecurityPatch ? SECURITY_PATCH_STATE_TRUE : SECURITY_PATCH_STATE_FALSE);
+    }
+
+    /**
+     * Gets time when the update was first available.
+     * @return Time as given by {@link System#currentTimeMillis()}
+     */
+    public long getReceivedTime() {
+        return mReceivedTime;
+    }
+
+    /**
+     * Gets whether the update is a security patch.
+     * @return {@link #SECURITY_PATCH_STATE_FALSE}, {@link #SECURITY_PATCH_STATE_TRUE}, or
+     *         {@link #SECURITY_PATCH_STATE_UNKNOWN}.
+     */
+    @SecurityPatchState
+    public int getSecurityPatchState() {
+        return mSecurityPatchState;
+    }
+
+    public static final Creator<SystemUpdateInfo> CREATOR =
+            new Creator<SystemUpdateInfo>() {
+                @Override
+                public SystemUpdateInfo createFromParcel(Parcel in) {
+                    return new SystemUpdateInfo(in);
+                }
+
+                @Override
+                public SystemUpdateInfo[] newArray(int size) {
+                    return new SystemUpdateInfo[size];
+                }
+            };
+
+    /** @hide */
+    public void writeToXml(XmlSerializer out, String tag) throws IOException {
+        out.startTag(null, tag);
+        out.attribute(null, ATTR_RECEIVED_TIME, String.valueOf(mReceivedTime));
+        out.attribute(null, ATTR_SECURITY_PATCH_STATE, String.valueOf(mSecurityPatchState));
+        out.attribute(null, ATTR_ORIGINAL_BUILD , Build.FINGERPRINT);
+        out.endTag(null, tag);
+    }
+
+    /** @hide */
+    @Nullable
+    public static SystemUpdateInfo readFromXml(XmlPullParser parser) {
+        // If an OTA has been applied (build fingerprint has changed), discard stale info.
+        final String buildFingerprint = parser.getAttributeValue(null, ATTR_ORIGINAL_BUILD );
+        if (!Build.FINGERPRINT.equals(buildFingerprint)) {
+            return null;
+        }
+        final long receivedTime =
+                Long.parseLong(parser.getAttributeValue(null, ATTR_RECEIVED_TIME));
+        final int securityPatchState =
+                Integer.parseInt(parser.getAttributeValue(null, ATTR_SECURITY_PATCH_STATE));
+        return new SystemUpdateInfo(receivedTime, securityPatchState);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(getReceivedTime());
+        dest.writeInt(getSecurityPatchState());
+    }
+
+    @Override
+    public String toString() {
+        return String.format("SystemUpdateInfo (receivedTime = %d, securityPatchState = %s)",
+                mReceivedTime, securityPatchStateToString(mSecurityPatchState));
+    }
+
+    private static String securityPatchStateToString(@SecurityPatchState int state) {
+        switch (state) {
+            case SECURITY_PATCH_STATE_FALSE:
+                return "false";
+            case SECURITY_PATCH_STATE_TRUE:
+                return "true";
+            case SECURITY_PATCH_STATE_UNKNOWN:
+                return "unknown";
+            default:
+                throw new IllegalArgumentException("Unrecognized security patch state: " + state);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SystemUpdateInfo that = (SystemUpdateInfo) o;
+        return mReceivedTime == that.mReceivedTime
+                && mSecurityPatchState == that.mSecurityPatchState;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mReceivedTime, mSecurityPatchState);
+    }
+}
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index bad6325..45d9fb7 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -493,7 +493,7 @@
      * <p class="note">Attempting to back up files in directories that are ignored by
      * the backup system will have no effect.  For example, if the app calls this method
      * with a file inside the {@link #getNoBackupFilesDir()} directory, it will be ignored.
-     * See {@link #onFullBackup(FullBackupDataOutput) for details on what directories
+     * See {@link #onFullBackup(FullBackupDataOutput)} for details on what directories
      * are excluded from backups.
      *
      * @param file The file to be backed up.  The file must exist and be readable by
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 80bc136..f0abe33 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -17,6 +17,7 @@
 package android.app.backup;
 
 import android.annotation.SystemApi;
+import android.content.ComponentName;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Message;
@@ -136,6 +137,46 @@
      */
     public static final String EXTRA_BACKUP_SERVICES_AVAILABLE = "backup_services_available";
 
+    /**
+     * If this flag is passed to {@link #requestBackup(String[], BackupObserver, int)},
+     * BackupManager will pass a blank old state to BackupAgents of requested packages.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_NON_INCREMENTAL_BACKUP = 1;
+
+    /**
+     * Use with {@link #requestBackup} to force backup of
+     * package meta data. Typically you do not need to explicitly request this be backed up as it is
+     * handled internally by the BackupManager. If you are requesting backups with
+     * FLAG_NON_INCREMENTAL, this package won't automatically be backed up and you have to
+     * explicitly request for its backup.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+
+
+    /**
+     * This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)}
+     * if the requested transport is unavailable.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_TRANSPORT_UNAVAILABLE = -1;
+
+    /**
+     * This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)} if the
+     * requested transport is not a valid BackupTransport.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_TRANSPORT_INVALID = -2;
+
     private Context mContext;
     private static IBackupManager sService;
 
@@ -369,17 +410,20 @@
     }
 
     /**
-     * Specify the current backup transport.  Callers must hold the
-     * android.permission.BACKUP permission to use this method.
+     * Specify the current backup transport.
+     *
+     * <p> Callers must hold the android.permission.BACKUP permission to use this method.
      *
      * @param transport The name of the transport to select.  This should be one
-     *   of the names returned by {@link #listAllTransports()}.
+     *   of the names returned by {@link #listAllTransports()}. This is the String returned by
+     *   {@link BackupTransport#name()} for the particular transport.
      * @return The name of the previously selected transport.  If the given transport
      *   name is not one of the currently available transports, no change is made to
      *   the current transport setting and the method returns null.
      *
      * @hide
      */
+    @Deprecated
     @SystemApi
     public String selectBackupTransport(String transport) {
         checkServiceBinder();
@@ -394,6 +438,34 @@
     }
 
     /**
+     * Specify the current backup transport and get notified when the transport is ready to be used.
+     * This method is async because BackupManager might need to bind to the specified transport
+     * which is in a separate process.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     *
+     * @param transport ComponentName of the service hosting the transport. This is different from
+     *                  the transport's name that is returned by {@link BackupTransport#name()}.
+     * @param listener A listener object to get a callback on the transport being selected.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void selectBackupTransport(ComponentName transport,
+            SelectBackupTransportCallback listener) {
+        checkServiceBinder();
+        if (sService != null) {
+            try {
+                SelectTransportListenerWrapper wrapper = listener == null ?
+                        null : new SelectTransportListenerWrapper(mContext, listener);
+                sService.selectBackupTransportAsync(transport, wrapper);
+            } catch (RemoteException e) {
+                Log.e(TAG, "selectBackupTransportAsync() couldn't connect");
+            }
+        }
+    }
+
+    /**
      * Schedule an immediate backup attempt for all pending key/value updates.  This
      * is primarily intended for transports to use when they detect a suitable
      * opportunity for doing a backup pass.  If there are no pending updates to
@@ -484,13 +556,34 @@
      */
     @SystemApi
     public int requestBackup(String[] packages, BackupObserver observer) {
+        return requestBackup(packages, observer, 0);
+    }
+
+    /**
+     * Request an immediate backup, providing an observer to which results of the backup operation
+     * will be published. The Android backup system will decide for each package whether it will
+     * be full app data backup or key/value-pair-based backup.
+     *
+     * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
+     * provided packages using the remote transport.
+     *
+     * @param packages List of package names to backup.
+     * @param observer The {@link BackupObserver} to receive callbacks during the backup
+     *                 operation. Could be {@code null}.
+     * @param flags    {@link #FLAG_NON_INCREMENTAL_BACKUP}.
+     * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
+     * @throws IllegalArgumentException on null or empty {@code packages} param.
+     * @hide
+     */
+    @SystemApi
+    public int requestBackup(String[] packages, BackupObserver observer, int flags) {
         checkServiceBinder();
         if (sService != null) {
             try {
                 BackupObserverWrapper observerWrapper = observer == null
                         ? null
                         : new BackupObserverWrapper(mContext, observer);
-                return sService.requestBackup(packages, observerWrapper);
+                return sService.requestBackup(packages, observerWrapper, flags);
             } catch (RemoteException e) {
                 Log.e(TAG, "requestBackup() couldn't connect");
             }
@@ -556,4 +649,35 @@
                 mHandler.obtainMessage(MSG_FINISHED, status, 0));
         }
     }
+
+    private class SelectTransportListenerWrapper extends ISelectBackupTransportCallback.Stub {
+
+        private final Handler mHandler;
+        private final SelectBackupTransportCallback mListener;
+
+        SelectTransportListenerWrapper(Context context, SelectBackupTransportCallback listener) {
+            mHandler = new Handler(context.getMainLooper());
+            mListener = listener;
+        }
+
+        @Override
+        public void onSuccess(final String transportName) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mListener.onSuccess(transportName);
+                }
+            });
+        }
+
+        @Override
+        public void onFailure(final int reason) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mListener.onFailure(reason);
+                }
+            });
+        }
+    }
 }
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index a320bbf..1657e2e 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -19,8 +19,10 @@
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
 import android.os.ParcelFileDescriptor;
 import android.content.Intent;
+import android.content.ComponentName;
 
 /**
  * Direct interface to the Backup Manager Service that applications invoke on.  The only
@@ -217,6 +219,8 @@
      */
     String[] listAllTransports();
 
+    ComponentName[] listAllTransportComponents();
+
     /**
      * Retrieve the list of whitelisted transport components.  Callers do </i>not</i> need
      * any special permission.
@@ -238,6 +242,21 @@
     String selectBackupTransport(String transport);
 
     /**
+     * Specify the current backup transport and get notified when the transport is ready to be used.
+     * This method is async because BackupManager might need to bind to the specified transport
+     * which is in a separate process.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     *
+     * @param transport ComponentName of the service hosting the transport. This is different from
+     *                  the transport's name that is returned by {@link BackupTransport#name()}.
+     * @param listener A listener object to get a callback on the transport being selected.
+     *
+     * @hide
+     */
+    void selectBackupTransportAsync(in ComponentName transport, ISelectBackupTransportCallback listener);
+
+    /**
      * Get the configuration Intent, if any, from the given transport.  Callers must
      * hold the android.permission.BACKUP permission in order to use this method.
      *
@@ -357,7 +376,9 @@
      * @param observer The {@link BackupObserver} to receive callbacks during the backup
      * operation.
      *
+     * @param flags {@link BackupManager#FLAG_NON_INCREMENTAL_BACKUP}.
+     *
      * @return Zero on success; nonzero on error.
      */
-    int requestBackup(in String[] packages, IBackupObserver observer);
+    int requestBackup(in String[] packages, IBackupObserver observer, int flags);
 }
diff --git a/core/java/android/app/backup/ISelectBackupTransportCallback.aidl b/core/java/android/app/backup/ISelectBackupTransportCallback.aidl
new file mode 100644
index 0000000..5de7c5e
--- /dev/null
+++ b/core/java/android/app/backup/ISelectBackupTransportCallback.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.backup;
+
+/**
+ * Callback class for receiving success or failure callbacks on selecting a backup transport. These
+ * methods will all be called on your application's main thread.
+ *
+ * @hide
+ */
+oneway interface ISelectBackupTransportCallback {
+
+    /**
+     * Called when BackupManager has successfully bound to the requested transport.
+     *
+     * @param transportName Name of the selected transport. This is the String returned by
+     *        {@link BackupTransport#name()}.
+     */
+    void onSuccess(String transportName);
+
+    /**
+     * Called when BackupManager fails to bind to the requested transport.
+     *
+     * @param reason Error code denoting reason for failure.
+     */
+    void onFailure(int reason);
+}
diff --git a/core/java/android/app/backup/SelectBackupTransportCallback.java b/core/java/android/app/backup/SelectBackupTransportCallback.java
new file mode 100644
index 0000000..0c8a0dc
--- /dev/null
+++ b/core/java/android/app/backup/SelectBackupTransportCallback.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.backup;
+
+import android.annotation.SystemApi;
+
+/**
+ * Callback class for receiving success or failure callbacks on selecting a backup transport. These
+ * methods will all be called on your application's main thread.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SelectBackupTransportCallback {
+
+    /**
+     * Called when BackupManager has successfully bound to the requested transport.
+     *
+     * @param transportName Name of the selected transport. This is the String returned by
+     *        {@link BackupTransport#name()}.
+     */
+    public void onSuccess(String transportName){}
+
+    /**
+     * Called when BackupManager fails to bind to the requested transport.
+     *
+     * @param reason Error code denoting reason for failure.
+     */
+    public void onFailure(int reason){}
+}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index d3d02e5..a10de45 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -25,6 +25,7 @@
  */
 interface ITrustManager {
     void reportUnlockAttempt(boolean successful, int userId);
+    void reportUnlockLockout(int timeoutMs, int userId);
     void reportEnabledTrustAgentsChanged(int userId);
     void registerTrustListener(in ITrustListener trustListener);
     void unregisterTrustListener(in ITrustListener trustListener);
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 0f5cb6f..a64a023 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -81,6 +81,26 @@
     }
 
     /**
+     * Reports that user {@param userId} has entered a temporary device lockout.
+     *
+     * This generally occurs when  the user has unsuccessfully tried to unlock the device too many
+     * times. The user will then be unable to unlock the device until a set amount of time has
+     * elapsed.
+     *
+     * @param timeout The amount of time that needs to elapse, in milliseconds, until the user may
+     *    attempt to unlock the device again.
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     */
+    public void reportUnlockLockout(int timeoutMs, int userId) {
+        try {
+            mService.reportUnlockLockout(timeoutMs, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Reports that the list of enabled trust agents changed for user {@param userId}.
      *
      * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index cd14469..6ba52b7 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -36,7 +36,6 @@
 import android.os.ServiceManager;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
-import android.util.TypedValue;
 import android.widget.RemoteViews;
 import android.widget.RemoteViews.OnClickHandler;
 
@@ -381,15 +380,7 @@
         // Convert complex to dp -- we are getting the AppWidgetProviderInfo from the
         // AppWidgetService, which doesn't have our context, hence we need to do the
         // conversion here.
-        appWidget.minWidth =
-            TypedValue.complexToDimensionPixelSize(appWidget.minWidth, mDisplayMetrics);
-        appWidget.minHeight =
-            TypedValue.complexToDimensionPixelSize(appWidget.minHeight, mDisplayMetrics);
-        appWidget.minResizeWidth =
-            TypedValue.complexToDimensionPixelSize(appWidget.minResizeWidth, mDisplayMetrics);
-        appWidget.minResizeHeight =
-            TypedValue.complexToDimensionPixelSize(appWidget.minResizeHeight, mDisplayMetrics);
-
+        appWidget.updateDimensions(mDisplayMetrics);
         synchronized (mViews) {
             v = mViews.get(appWidgetId);
         }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 31e779f..9980e966 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -31,7 +31,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
-import android.util.TypedValue;
 import android.widget.RemoteViews;
 
 import com.android.internal.appwidget.IAppWidgetService;
@@ -760,7 +759,7 @@
             }
             for (AppWidgetProviderInfo info : providers.getList()) {
                 // Converting complex to dp.
-                convertSizesToPixels(info);
+                info.updateDimensions(mDisplayMetrics);
             }
             return providers.getList();
         } catch (RemoteException e) {
@@ -782,7 +781,7 @@
             AppWidgetProviderInfo info = mService.getAppWidgetInfo(mPackageName, appWidgetId);
             if (info != null) {
                 // Converting complex to dp.
-                convertSizesToPixels(info);
+                info.updateDimensions(mDisplayMetrics);
             }
             return info;
         } catch (RemoteException e) {
@@ -1072,18 +1071,6 @@
         }
     }
 
-    private void convertSizesToPixels(AppWidgetProviderInfo info) {
-        // Converting complex to dp.
-        info.minWidth = TypedValue.complexToDimensionPixelSize(info.minWidth,
-                mDisplayMetrics);
-        info.minHeight = TypedValue.complexToDimensionPixelSize(info.minHeight,
-                mDisplayMetrics);
-        info.minResizeWidth = TypedValue.complexToDimensionPixelSize(info.minResizeWidth,
-                mDisplayMetrics);
-        info.minResizeHeight = TypedValue.complexToDimensionPixelSize(info.minResizeHeight,
-                mDisplayMetrics);
-    }
-
     /**
      * Request to pin an app widget on the current launcher. It's up to the launcher to accept this
      * request (optionally showing a user confirmation). If the request is accepted, the caller will
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index b4d79b4..06fdb32 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -26,6 +26,8 @@
 import android.os.Parcelable;
 import android.content.ComponentName;
 import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
 
 /**
  * Describes the meta data for an installed AppWidget provider.  The fields in this class
@@ -379,6 +381,17 @@
     }
 
     /**
+     * @hide
+     */
+    public void updateDimensions(DisplayMetrics displayMetrics) {
+        // Converting complex to dp.
+        minWidth = TypedValue.complexToDimensionPixelSize(minWidth, displayMetrics);
+        minHeight = TypedValue.complexToDimensionPixelSize(minHeight, displayMetrics);
+        minResizeWidth = TypedValue.complexToDimensionPixelSize(minResizeWidth, displayMetrics);
+        minResizeHeight = TypedValue.complexToDimensionPixelSize(minResizeHeight, displayMetrics);
+    }
+
+    /**
      * Parcelable.Creator that instantiates AppWidgetProviderInfo objects
      */
     public static final Parcelable.Creator<AppWidgetProviderInfo> CREATOR
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index c7c64c4..544b3b95 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -965,38 +965,6 @@
     }
 
     /**
-     * Accept the incoming connection.
-     */
-    public boolean acceptIncomingConnect(BluetoothDevice device) {
-        if (DBG) log("acceptIncomingConnect");
-        if (mService != null && isEnabled()) {
-            try {
-                return mService.acceptIncomingConnect(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return false;
-    }
-
-    /**
-     * Reject the incoming connection.
-     */
-    public boolean rejectIncomingConnect(BluetoothDevice device) {
-        if (DBG) log("rejectIncomingConnect");
-        if (mService != null) {
-            try {
-                return mService.rejectIncomingConnect(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return false;
-    }
-
-    /**
      * Returns current audio state of Audio Gateway.
      *
      * Note: This is an internal function and shouldn't be exposed
@@ -1017,13 +985,15 @@
     /**
      * Sets whether audio routing is allowed.
      *
+     * @param device    remote device
+     * @param allowed   if routing is allowed to the device
      * Note: This is an internal function and shouldn't be exposed
      */
-    public void setAudioRouteAllowed(boolean allowed) {
+    public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
         if (VDBG) log("setAudioRouteAllowed");
         if (mService != null && isEnabled()) {
             try {
-                mService.setAudioRouteAllowed(allowed);
+                mService.setAudioRouteAllowed(device, allowed);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
         } else {
             Log.w(TAG, "Proxy not attached to service");
@@ -1033,14 +1003,15 @@
 
     /**
      * Returns whether audio routing is allowed.
-     *
+     * @param device    remote device
+     * @return whether the command succeeded
      * Note: This is an internal function and shouldn't be exposed
      */
-    public boolean getAudioRouteAllowed() {
+    public boolean getAudioRouteAllowed(BluetoothDevice device) {
         if (VDBG) log("getAudioRouteAllowed");
         if (mService != null && isEnabled()) {
             try {
-                return mService.getAudioRouteAllowed();
+                return mService.getAudioRouteAllowed(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
         } else {
             Log.w(TAG, "Proxy not attached to service");
@@ -1054,15 +1025,16 @@
      *
      * It setup SCO channel with remote connected Handsfree AG device.
      *
+     * @param device    remote device
      * @return          <code>true</code> if command has been issued successfully;
      *                   <code>false</code> otherwise;
      *                   upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED}
      *                   intent;
      */
-    public boolean connectAudio() {
+    public boolean connectAudio(BluetoothDevice device) {
         if (mService != null && isEnabled()) {
             try {
-                return mService.connectAudio();
+                return mService.connectAudio(device);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
@@ -1078,15 +1050,16 @@
      *
      * It tears down the SCO channel from remote AG device.
      *
+     * @param   device  remote device
      * @return          <code>true</code> if command has been issued successfully;
      *                   <code>false</code> otherwise;
      *                   upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED}
      *                   intent;
      */
-    public boolean disconnectAudio() {
+    public boolean disconnectAudio(BluetoothDevice device) {
         if (mService != null && isEnabled()) {
             try {
-                return mService.disconnectAudio();
+                return mService.disconnectAudio(device);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
diff --git a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
index a351bd2..e571b00 100644
--- a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
@@ -29,9 +29,6 @@
     boolean connect(in BluetoothDevice device);
     boolean disconnect(in BluetoothDevice device);
 
-    boolean acceptIncomingConnect(in BluetoothDevice device);
-    boolean rejectIncomingConnect(in BluetoothDevice device);
-
     List<BluetoothDevice> getConnectedDevices();
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
@@ -58,10 +55,10 @@
     boolean getLastVoiceTagNumber(in BluetoothDevice device);
 
     int getAudioState(in BluetoothDevice device);
-    boolean connectAudio();
-    boolean disconnectAudio();
-    void setAudioRouteAllowed(boolean allowed);
-    boolean getAudioRouteAllowed();
+    boolean connectAudio(in BluetoothDevice device);
+    boolean disconnectAudio(in BluetoothDevice device);
+    void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed);
+    boolean getAudioRouteAllowed(in BluetoothDevice device);
 
     Bundle getCurrentAgFeatures(in BluetoothDevice device);
 }
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index bc5e986..7096771 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -32,6 +32,8 @@
 import android.text.style.URLSpan;
 import android.util.Log;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -762,6 +764,18 @@
     static public ClipData newUri(ContentResolver resolver, CharSequence label,
             Uri uri) {
         Item item = new Item(uri);
+        String[] mimeTypes = getMimeTypes(resolver, uri);
+        return new ClipData(label, mimeTypes, item);
+    }
+
+    /**
+     * Finds all applicable MIME types for a given URI.
+     *
+     * @param resolver ContentResolver used to get information about the URI.
+     * @param uri The URI.
+     * @return Returns an array of MIME types.
+     */
+    private static String[] getMimeTypes(ContentResolver resolver, Uri uri) {
         String[] mimeTypes = null;
         if ("content".equals(uri.getScheme())) {
             String realType = resolver.getType(uri);
@@ -769,7 +783,7 @@
             if (realType != null) {
                 if (mimeTypes == null) {
                     mimeTypes = new String[] { realType };
-                } else {
+                } else if (!ArrayUtils.contains(mimeTypes, realType)) {
                     String[] tmp = new String[mimeTypes.length + 1];
                     tmp[0] = realType;
                     System.arraycopy(mimeTypes, 0, tmp, 1, mimeTypes.length);
@@ -780,7 +794,7 @@
         if (mimeTypes == null) {
             mimeTypes = MIMETYPES_TEXT_URILIST;
         }
-        return new ClipData(label, mimeTypes, item);
+        return mimeTypes;
     }
 
     /**
@@ -811,8 +825,8 @@
      * Add a new Item to the overall ClipData container.
      * <p> This method will <em>not</em> update the list of available MIME types in the
      * {@link ClipDescription}. It should be used only when adding items which do not add new
-     * MIME types to this clip. If this is not the case, {@link #ClipData(CharSequence, String[],
-     * Item)} should be used with a complete list of MIME types.
+     * MIME types to this clip. If this is not the case, use {@link #addItem(Item, ContentResolver)}
+     * or call {@link #ClipData(CharSequence, String[], Item)} with a complete list of MIME types.
      * @param item Item to be added.
      */
     public void addItem(Item item) {
@@ -822,6 +836,32 @@
         mItems.add(item);
     }
 
+    /**
+     * Add a new Item to the overall ClipData container.
+     * <p> Unlike {@link #addItem(Item)}, this method will update the list of available MIME types
+     * in the {@link ClipDescription}.
+     * @param item Item to be added.
+     * @param resolver ContentResolver used to get information about the URI possibly contained in
+     * the item.
+     */
+    public void addItem(Item item, ContentResolver resolver) {
+        addItem(item);
+
+        if (item.getHtmlText() != null) {
+            mClipDescription.addMimeTypes(MIMETYPES_TEXT_HTML);
+        } else if (item.getText() != null) {
+            mClipDescription.addMimeTypes(MIMETYPES_TEXT_PLAIN);
+        }
+
+        if (item.getIntent() != null) {
+            mClipDescription.addMimeTypes(MIMETYPES_TEXT_INTENT);
+        }
+
+        if (item.getUri() != null) {
+            mClipDescription.addMimeTypes(getMimeTypes(resolver, item.getUri()));
+        }
+    }
+
     /** @hide */
     public Bitmap getIcon() {
         return mIcon;
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index 461d1e0..b33a915a 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -22,6 +22,7 @@
 import android.text.TextUtils;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Meta-data describing the contents of a {@link ClipData}.  Provides enough
@@ -89,7 +90,7 @@
 
 
     final CharSequence mLabel;
-    final String[] mMimeTypes;
+    private final ArrayList<String> mMimeTypes;
     private PersistableBundle mExtras;
 
     /**
@@ -103,7 +104,7 @@
             throw new NullPointerException("mimeTypes is null");
         }
         mLabel = label;
-        mMimeTypes = mimeTypes;
+        mMimeTypes = new ArrayList<String>(Arrays.asList(mimeTypes));
     }
 
     /**
@@ -111,7 +112,7 @@
      */
     public ClipDescription(ClipDescription o) {
         mLabel = o.mLabel;
-        mMimeTypes = o.mMimeTypes;
+        mMimeTypes = new ArrayList<String>(o.mMimeTypes);
     }
 
     /**
@@ -155,8 +156,9 @@
      * matches the desired MIME type, else false.
      */
     public boolean hasMimeType(String mimeType) {
-        for (int i=0; i<mMimeTypes.length; i++) {
-            if (compareMimeTypes(mMimeTypes[i], mimeType)) {
+        final int size = mMimeTypes.size();
+        for (int i=0; i<size; i++) {
+            if (compareMimeTypes(mMimeTypes.get(i), mimeType)) {
                 return true;
             }
         }
@@ -173,12 +175,13 @@
      */
     public String[] filterMimeTypes(String mimeType) {
         ArrayList<String> array = null;
-        for (int i=0; i<mMimeTypes.length; i++) {
-            if (compareMimeTypes(mMimeTypes[i], mimeType)) {
+        final int size = mMimeTypes.size();
+        for (int i=0; i<size; i++) {
+            if (compareMimeTypes(mMimeTypes.get(i), mimeType)) {
                 if (array == null) {
                     array = new ArrayList<String>();
                 }
-                array.add(mMimeTypes[i]);
+                array.add(mMimeTypes.get(i));
             }
         }
         if (array == null) {
@@ -193,14 +196,26 @@
      * Return the number of MIME types the clip is available in.
      */
     public int getMimeTypeCount() {
-        return mMimeTypes.length;
+        return mMimeTypes.size();
     }
 
     /**
      * Return one of the possible clip MIME types.
      */
     public String getMimeType(int index) {
-        return mMimeTypes[index];
+        return mMimeTypes.get(index);
+    }
+
+    /**
+     * Add MIME types to the clip description.
+     */
+    void addMimeTypes(String[] mimeTypes) {
+        for (int i=0; i!=mimeTypes.length; i++) {
+            final String mimeType = mimeTypes[i];
+            if (!mMimeTypes.contains(mimeType)) {
+                mMimeTypes.add(mimeType);
+            }
+        }
     }
 
     /**
@@ -229,11 +244,12 @@
         if (mMimeTypes == null) {
             throw new NullPointerException("null mime types");
         }
-        if (mMimeTypes.length <= 0) {
+        final int size = mMimeTypes.size();
+        if (size <= 0) {
             throw new IllegalArgumentException("must have at least 1 mime type");
         }
-        for (int i=0; i<mMimeTypes.length; i++) {
-            if (mMimeTypes[i] == null) {
+        for (int i=0; i<size; i++) {
+            if (mMimeTypes.get(i) == null) {
                 throw new NullPointerException("mime type at " + i + " is null");
             }
         }
@@ -275,12 +291,13 @@
     /** @hide */
     public boolean toShortStringTypesOnly(StringBuilder b) {
         boolean first = true;
-        for (int i=0; i<mMimeTypes.length; i++) {
+        final int size = mMimeTypes.size();
+        for (int i=0; i<size; i++) {
             if (!first) {
                 b.append(' ');
             }
             first = false;
-            b.append(mMimeTypes[i]);
+            b.append(mMimeTypes.get(i));
         }
         return !first;
     }
@@ -293,13 +310,13 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         TextUtils.writeToParcel(mLabel, dest, flags);
-        dest.writeStringArray(mMimeTypes);
+        dest.writeStringList(mMimeTypes);
         dest.writePersistableBundle(mExtras);
     }
 
     ClipDescription(Parcel in) {
         mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-        mMimeTypes = in.createStringArray();
+        mMimeTypes = in.createStringArrayList();
         mExtras = in.readPersistableBundle();
     }
 
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index 7ed827c..3a87cb3 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -414,7 +414,11 @@
             return (Boolean) value;
         } catch (ClassCastException e) {
             if (value instanceof CharSequence) {
-                return Boolean.valueOf(value.toString());
+                // Note that we also check against 1 here because SQLite's internal representation
+                // for booleans is an integer with a value of 0 or 1. Without this check, boolean
+                // values obtained via DatabaseUtils#cursorRowToContentValues will always return
+                // false.
+                return Boolean.valueOf(value.toString()) || "1".equals(value);
             } else if (value instanceof Number) {
                 return ((Number) value).intValue() != 0;
             } else {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index af5e643..c7c680f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -34,8 +34,7 @@
 import android.annotation.UserIdInt;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
-import android.app.LoadedApk;
-import android.app.admin.DevicePolicyManager;
+import android.app.Notification;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
@@ -63,6 +62,7 @@
 import android.view.DisplayAdjustments;
 import android.view.ViewDebug;
 import android.view.WindowManager;
+import android.view.textclassifier.TextClassificationManager;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -1672,7 +1672,7 @@
      *
      * @param intents An array of Intents to be started.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @throws ActivityNotFoundException &nbsp;
@@ -1700,7 +1700,7 @@
      * @param intents An array of Intents to be started.
      * @param options Additional options for how the Activity should be started.
      * @param userHandle The user for whom to launch the activities
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
      * @throws ActivityNotFoundException &nbsp;
@@ -1749,7 +1749,7 @@
      * <var>flagsMask</var>
      * @param extraFlags Always set to 0.
      * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)
+     * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.  If options
      * have also been supplied by the IntentSender, options given here will
      * override any that conflict with those given by the IntentSender.
@@ -2508,7 +2508,10 @@
      * {@link ComponentName} of the actual service that was started is
      * returned; else if the service does not exist null is returned.
      *
-     * @throws SecurityException &nbsp;
+     * @throws SecurityException If the caller does not permission to access the service
+     * or the service can not be found.
+     * @throws IllegalStateException If the application is in a state where the service
+     * can not be started (such as not in the foreground in a state when services are allowed).
      *
      * @see #stopService
      * @see #bindService
@@ -2517,6 +2520,17 @@
     public abstract ComponentName startService(Intent service);
 
     /**
+     * Start a service directly into the "foreground service" state.  Unlike {@link #startService},
+     * this method can be used from within background operations like broadcast receivers
+     * or scheduled jobs.  The API entry point for this is in NotificationManager in order to
+     * preserve appropriate public package layering.
+     * @hide
+     */
+    @Nullable
+    public abstract ComponentName startServiceInForeground(Intent service,
+            int id, Notification notification);
+
+    /**
      * Request that a given application service be stopped.  If the service is
      * not running, nothing happens.  Otherwise it is stopped.  Note that calls
      * to startService() are not counted -- this stops the service no matter
@@ -2538,7 +2552,10 @@
      * @return If there is a service matching the given Intent that is already
      * running, then it is stopped and {@code true} is returned; else {@code false} is returned.
      *
-     * @throws SecurityException &nbsp;
+     * @throws SecurityException If the caller does not permission to access the service
+     * or the service can not be found.
+     * @throws IllegalStateException If the application is in a state where the service
+     * can not be started (such as not in the foreground in a state when services are allowed).
      *
      * @see #startService
      */
@@ -2547,9 +2564,18 @@
     /**
      * @hide like {@link #startService(Intent)} but for a specific user.
      */
+    @Nullable
     public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
 
     /**
+     * @hide like {@link #startServiceInForeground(Intent, int, Notification)}
+     * but for a specific user.
+     */
+    @Nullable
+    public abstract ComponentName startServiceInForegroundAsUser(Intent service,
+            int id, Notification notification, UserHandle user);
+
+    /**
      * @hide like {@link #stopService(Intent)} but for a specific user.
      */
     public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
@@ -2591,7 +2617,8 @@
      *         {@code false} is returned if the connection is not made so you will not
      *         receive the service object.
      *
-     * @throws SecurityException &nbsp;
+     * @throws SecurityException If the caller does not permission to access the service
+     * or the service can not be found.
      *
      * @see #unbindService
      * @see #startService
@@ -3320,10 +3347,10 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
-     * {@link android.text.TextClassificationManager} for text classification services.
+     * {@link TextClassificationManager} for text classification services.
      *
      * @see #getSystemService
-     * @see android.text.TextClassificationManager
+     * @see TextClassificationManager
      */
     public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
 
@@ -3716,6 +3743,11 @@
     public static final String DEVICE_IDENTIFIERS_SERVICE = "device_identifiers";
 
     /**
+     * Service that provides System font data.
+     */
+    public static final String FONT_SERVICE = "font";
+
+    /**
      * Service to report a system health "incident"
      * @hide
      */
@@ -4221,6 +4253,20 @@
             int flags) throws PackageManager.NameNotFoundException;
 
     /**
+     * Return a new Context object for the given split name. The new Context has a ClassLoader and
+     * Resources object that can access the split's and all of its dependencies' code/resources.
+     * Each call to this method returns a new instance of a Context object;
+     * Context objects are not shared, however common state (ClassLoader, other Resources for
+     * the same split) may be so the Context itself can be fairly lightweight.
+     *
+     * @param splitName The name of the split to include, as declared in the split's
+     *                  <code>AndroidManifest.xml</code>.
+     * @return A {@link Context} with the given split's code and/or resources loaded.
+     */
+    public abstract Context createContextForSplit(String splitName)
+            throws PackageManager.NameNotFoundException;
+
+    /**
      * Get the userId associated with this context
      * @return user id
      *
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 4b6076b..546bfc4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -16,10 +16,10 @@
 
 package android.content;
 
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
+import android.app.Notification;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
@@ -623,6 +623,13 @@
         return mBase.startService(service);
     }
 
+    /** @hide */
+    @Override
+    public ComponentName startServiceInForeground(Intent service,
+            int id, Notification notification) {
+        return mBase.startServiceInForeground(service, id, notification);
+    }
+
     @Override
     public boolean stopService(Intent name) {
         return mBase.stopService(name);
@@ -636,6 +643,13 @@
 
     /** @hide */
     @Override
+    public ComponentName startServiceInForegroundAsUser(Intent service,
+            int id, Notification notification, UserHandle user) {
+        return mBase.startServiceInForegroundAsUser(service, id, notification, user);
+    }
+
+    /** @hide */
+    @Override
     public boolean stopServiceAsUser(Intent name, UserHandle user) {
         return mBase.stopServiceAsUser(name, user);
     }
@@ -653,6 +667,13 @@
         return mBase.bindServiceAsUser(service, conn, flags, user);
     }
 
+    /** @hide */
+    @Override
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            Handler handler, UserHandle user) {
+        return mBase.bindServiceAsUser(service, conn, flags, handler, user);
+    }
+
     @Override
     public void unbindService(ServiceConnection conn) {
         mBase.unbindService(conn);
@@ -804,6 +825,13 @@
 
     /** @hide */
     @Override
+    public Context createContextForSplit(String splitName)
+            throws PackageManager.NameNotFoundException {
+        return mBase.createContextForSplit(splitName);
+    }
+
+    /** @hide */
+    @Override
     public int getUserId() {
         return mBase.getUserId();
     }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d8358f9..249001b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -715,11 +715,13 @@
     /**
      * Activity Action: Creates a shortcut.
      * <p>Input: Nothing.</p>
-     * <p>Output: An Intent representing the shortcut. The intent must contain three
+     * <p>Output: An Intent representing the {@link android.content.pm.ShortcutInfo} result.</p>
+     * <p>For compatibility with older versions of android the intent may also contain three
      * extras: SHORTCUT_INTENT (value: Intent), SHORTCUT_NAME (value: String),
      * and SHORTCUT_ICON (value: Bitmap) or SHORTCUT_ICON_RESOURCE
      * (value: ShortcutIconResource).</p>
      *
+     * @see android.content.pm.ShortcutManager#createShortcutResultIntent
      * @see #EXTRA_SHORTCUT_INTENT
      * @see #EXTRA_SHORTCUT_NAME
      * @see #EXTRA_SHORTCUT_ICON
@@ -733,26 +735,34 @@
      * The name of the extra used to define the Intent of a shortcut.
      *
      * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
      */
+    @Deprecated
     public static final String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
     /**
      * The name of the extra used to define the name of a shortcut.
      *
      * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
      */
+    @Deprecated
     public static final String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     /**
      * The name of the extra used to define the icon, as a Bitmap, of a shortcut.
      *
      * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
      */
+    @Deprecated
     public static final String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
     /**
      * The name of the extra used to define the icon, as a ShortcutIconResource, of a shortcut.
      *
      * @see #ACTION_CREATE_SHORTCUT
      * @see android.content.Intent.ShortcutIconResource
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
      */
+    @Deprecated
     public static final String EXTRA_SHORTCUT_ICON_RESOURCE =
             "android.intent.extra.shortcut.ICON_RESOURCE";
 
@@ -1618,6 +1628,19 @@
     public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
 
     /**
+     * Activity Action: Launch application uninstaller.
+     * <p>
+     * Input: The data must be a package: URI whose scheme specific part is
+     * the package name of the current installed package to be uninstalled.
+     * You can optionally supply {@link #EXTRA_RETURN_RESULT}.
+     * <p>
+     * Output: Nothing.
+     * </p>
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CLEAR_PACKAGE = "android.intent.action.CLEAR_PACKAGE";
+
+    /**
      * Specify whether the package should be uninstalled for all users.
      * @hide because these should not be part of normal application flow.
      */
@@ -3956,6 +3979,12 @@
     public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN";
 
     /**
+     * The version code of the app to install components from.
+     * @hide
+     */
+    public static final String EXTRA_VERSION_CODE = "android.intent.extra.VERSION_CODE";
+
+    /**
      * A Bundle forming a mapping of potential target package names to different extras Bundles
      * to add to the default intent extras in {@link #EXTRA_INTENT} when used with
      * {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not
@@ -4832,6 +4861,10 @@
      * or not running) apps, regardless of whether that would be done by default.  By
      * default they will only receive broadcasts if the broadcast has specified an
      * explicit component or package name.
+     *
+     * NOTE: dumpstate uses this flag numerically, so when its value is changed
+     * the broadcast code there must also be changed to match.
+     *
      * @hide
      */
     public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
@@ -8324,7 +8357,7 @@
      * @return Returns a bit mask of {@link #FILL_IN_ACTION},
      * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE},
      * {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS},
-     * {@link #FILL_IN_SELECTOR} and {@link #FILL_IN_CLIP_DATA indicating which fields were
+     * {@link #FILL_IN_SELECTOR} and {@link #FILL_IN_CLIP_DATA} indicating which fields were
      * changed.
      */
     @FillInFlags
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index 541ebbd..dd53eac 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -175,7 +175,7 @@
     }
 
     /**
-     * Builder class for a @link SyncRequest. As you build your SyncRequest this class will also
+     * Builder class for a {@link SyncRequest}. As you build your SyncRequest this class will also
      * perform validation.
      */
     public static class Builder {
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 44dff00..92cb709 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -177,10 +177,14 @@
      */
     public static final int RESIZE_MODE_RESIZEABLE = 2;
     /**
-     * Activity is resizeable and supported picture-in-picture mode.
+     * Activity is resizeable and supported picture-in-picture mode.  This flag is now deprecated
+     * since activities do not need to be resizeable to support picture-in-picture.
+     * See {@link #FLAG_SUPPORTS_PICTURE_IN_PICTURE}.
+     *
      * @hide
+     * @deprecated
      */
-    public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE = 3;
+    public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED = 3;
     /**
      * Activity does not support resizing, but we are forcing it to be resizeable. Only affects
      * certain pre-N apps where we force them to be resizeable.
@@ -220,6 +224,44 @@
     public String requestedVrComponent;
 
     /**
+     * Value for {@link #colorMode} indicating that the activity should use the
+     * default color mode (sRGB, low dynamic range).
+     *
+     * @see android.R.attr#colorMode
+     */
+    public static final int COLOR_MODE_DEFAULT = 0;
+    /**
+     * Value of {@link #colorMode} indicating that the activity should use a
+     * wide color gamut if the presentation display supports it.
+     *
+     * @see android.R.attr#colorMode
+     */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT = 1;
+    /**
+     * Value of {@link #colorMode} indicating that the activity should use a
+     * high dynamic range if the presentation display supports it.
+     *
+     * @see android.R.attr#colorMode
+     */
+    public static final int COLOR_MODE_HDR = 2;
+
+    /** @hide */
+    @IntDef({
+        COLOR_MODE_DEFAULT,
+        COLOR_MODE_WIDE_COLOR_GAMUT,
+        COLOR_MODE_HDR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ColorMode {}
+
+    /**
+     * The color mode requested by this activity. The target display may not be
+     * able to honor the request.
+     */
+    @ColorMode
+    public int colorMode = COLOR_MODE_DEFAULT;
+
+    /**
      * Bit in {@link #flags} indicating whether this activity is able to
      * run in multiple processes.  If
      * true, the system may instantiate it in the some process as the
@@ -369,6 +411,13 @@
     public static final int FLAG_VISIBLE_TO_EPHEMERAL = 0x100000;
 
     /**
+     * Bit in {@link #flags} indicating if the activity supports picture-in-picture mode.
+     * See {@link android.R.attr#supportsPictureInPicture}.
+     * @hide
+     */
+    public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x200000;
+
+    /**
      * @hide Bit in {@link #flags}: If set, this component will only be seen
      * by the system user.  Only works with broadcast receivers.  Set from the
      * android.R.attr#systemUserOnly attribute.
@@ -566,6 +615,7 @@
                     CONFIG_SMALLEST_SCREEN_SIZE,
                     CONFIG_DENSITY,
                     CONFIG_LAYOUT_DIRECTION,
+                    CONFIG_COLOR_MODE,
                     CONFIG_FONT_SCALE,
             })
     @Retention(RetentionPolicy.SOURCE)
@@ -671,6 +721,20 @@
     public static final int CONFIG_LAYOUT_DIRECTION = 0x2000;
     /**
      * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle the change to the display color gamut or dynamic
+     * range. Set from the {@link android.R.attr#configChanges} attribute.
+     */
+    public static final int CONFIG_COLOR_MODE = 0x4000;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle asset path changes.  Set from the {@link android.R.attr#configChanges}
+     * attribute. This is not a core resource configuration, but a higher-level value, so its
+     * constant starts at the high bits.
+     * @hide We do not want apps handling this yet, but we do need some kind of bit for diffs.
+     */
+    public static final int CONFIG_ASSETS_PATHS = 0x80000000;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
      * can itself handle changes to the font scaling factor.  Set from the
      * {@link android.R.attr#configChanges} attribute.  This is
      * not a core resource configuration, but a higher-level value, so its
@@ -698,6 +762,7 @@
         Configuration.NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,   // SMALLEST SCREEN SIZE
         Configuration.NATIVE_CONFIG_DENSITY,                // DENSITY
         Configuration.NATIVE_CONFIG_LAYOUTDIR,              // LAYOUT DIRECTION
+        Configuration.NATIVE_CONFIG_COLOR_MODE,             // COLOR_MODE
     };
 
     /**
@@ -753,7 +818,8 @@
      * {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN},
      * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
      * {@link #CONFIG_ORIENTATION}, {@link #CONFIG_SCREEN_LAYOUT},
-     * {@link #CONFIG_DENSITY}, and {@link #CONFIG_LAYOUT_DIRECTION}.
+     * {@link #CONFIG_DENSITY}, {@link #CONFIG_LAYOUT_DIRECTION} and
+     * {@link #CONFIG_COLOR_MODE}.
      * Set from the {@link android.R.attr#configChanges} attribute.
      */
     public int configChanges;
@@ -856,6 +922,7 @@
         resizeMode = orig.resizeMode;
         requestedVrComponent = orig.requestedVrComponent;
         rotationAnimation = orig.rotationAnimation;
+        colorMode = orig.colorMode;
     }
 
     /**
@@ -909,10 +976,17 @@
                 || screenOrientation == SCREEN_ORIENTATION_USER_PORTRAIT;
     }
 
+    /**
+     * Returns true if the activity supports picture-in-picture.
+     * @hide
+     */
+    public boolean supportsPictureInPicture() {
+        return (flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0;
+    }
+
     /** @hide */
     public static boolean isResizeableMode(int mode) {
         return mode == RESIZE_MODE_RESIZEABLE
-                || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE
                 || mode == RESIZE_MODE_FORCE_RESIZEABLE
                 || mode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
                 || mode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
@@ -936,8 +1010,6 @@
                 return "RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION";
             case RESIZE_MODE_RESIZEABLE:
                 return "RESIZE_MODE_RESIZEABLE";
-            case RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
-                return "RESIZE_MODE_RESIZEABLE_AND_PIPABLE";
             case RESIZE_MODE_FORCE_RESIZEABLE:
                 return "RESIZE_MODE_FORCE_RESIZEABLE";
             case RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY:
@@ -1038,6 +1110,7 @@
         dest.writeInt(resizeMode);
         dest.writeString(requestedVrComponent);
         dest.writeInt(rotationAnimation);
+        dest.writeInt(colorMode);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -1073,6 +1146,7 @@
         resizeMode = source.readInt();
         requestedVrComponent = source.readString();
         rotationAnimation = source.readInt();
+        colorMode = source.readInt();
     }
 
     /**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 71071e1..3d9ba96 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -31,6 +31,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Printer;
+import android.util.SparseIntArray;
 
 import com.android.internal.util.ArrayUtils;
 
@@ -551,6 +552,21 @@
     public static final int PRIVATE_FLAG_BACKUP_IN_FOREGROUND = 1 << 12;
 
     /**
+     * Value for {@link #privateFlags}: {@code true} means this application
+     * contains a static shared library. Defaults to {@code false} if unspecified.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_STATIC_SHARED_LIBRARY = 1 << 13;
+
+    /**
+     * Value for {@linl #privateFlags}: When set, the application will only have its splits loaded
+     * if they are required to load a component. Splits can be loaded on demand using the
+     * {@link Context#createContextForSplit(String)} API.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ISOLATED_SPLIT_LOADING = 1 << 14;
+
+    /**
      * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
      * {@hide}
      */
@@ -600,8 +616,12 @@
     public String publicSourceDir;
 
     /**
-     * Full paths to zero or more split APKs that, when combined with the base
-     * APK defined in {@link #sourceDir}, form a complete application.
+     * The names of all installed split APKs, ordered lexicographically.
+     */
+    public String[] splitNames;
+
+    /**
+     * Full paths to zero or more split APKs, indexed by the same order as {@link #splitNames}.
      */
     public String[] splitSourceDirs;
 
@@ -609,14 +629,35 @@
      * Full path to the publicly available parts of {@link #splitSourceDirs},
      * including resources and manifest. This may be different from
      * {@link #splitSourceDirs} if an application is forward locked.
+     *
+     * @see #splitSourceDirs
      */
     public String[] splitPublicSourceDirs;
 
     /**
-     * Full paths to the locations of extra resource packages this application
-     * uses. This field is only used if there are extra resource packages,
-     * otherwise it is null.
-     * 
+     * Maps the dependencies between split APKs. All splits implicitly depend on the base APK.
+     *
+     * Available since platform version O.
+     *
+     * Only populated if the application opts in to isolated split loading via the
+     * {@link android.R.attr.isolatedSplits} attribute in the &lt;manifest&gt; tag of the app's
+     * AndroidManifest.xml.
+     *
+     * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs},
+     * and {@link #splitPublicSourceDirs} arrays.
+     * Each key represents a split and its value is its parent split.
+     * Cycles do not exist because they are illegal and screened for during installation.
+     *
+     * May be null if no splits are installed, or if no dependencies exist between them.
+     * @hide
+     */
+    public SparseIntArray splitDependencies;
+
+    /**
+     * Full paths to the locations of extra resource packages (runtime overlays)
+     * this application uses. This field is only used if there are extra resource
+     * packages, otherwise it is null.
+     *
      * {@hide}
      */
     public String[] resourceDirs;
@@ -1051,8 +1092,10 @@
         scanPublicSourceDir = orig.scanPublicSourceDir;
         sourceDir = orig.sourceDir;
         publicSourceDir = orig.publicSourceDir;
+        splitNames = orig.splitNames;
         splitSourceDirs = orig.splitSourceDirs;
         splitPublicSourceDirs = orig.splitPublicSourceDirs;
+        splitDependencies = orig.splitDependencies;
         nativeLibraryDir = orig.nativeLibraryDir;
         secondaryNativeLibraryDir = orig.secondaryNativeLibraryDir;
         nativeLibraryRootDir = orig.nativeLibraryRootDir;
@@ -1091,6 +1134,7 @@
         return 0;
     }
 
+    @SuppressWarnings("unchecked")
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         super.writeToParcel(dest, parcelableFlags);
         dest.writeString(taskAffinity);
@@ -1108,8 +1152,10 @@
         dest.writeString(scanPublicSourceDir);
         dest.writeString(sourceDir);
         dest.writeString(publicSourceDir);
+        dest.writeStringArray(splitNames);
         dest.writeStringArray(splitSourceDirs);
         dest.writeStringArray(splitPublicSourceDirs);
+        dest.writeSparseIntArray(splitDependencies);
         dest.writeString(nativeLibraryDir);
         dest.writeString(secondaryNativeLibraryDir);
         dest.writeString(nativeLibraryRootDir);
@@ -1148,6 +1194,7 @@
         }
     };
 
+    @SuppressWarnings("unchecked")
     private ApplicationInfo(Parcel source) {
         super(source);
         taskAffinity = source.readString();
@@ -1165,8 +1212,10 @@
         scanPublicSourceDir = source.readString();
         sourceDir = source.readString();
         publicSourceDir = source.readString();
+        splitNames = source.readStringArray();
         splitSourceDirs = source.readStringArray();
         splitPublicSourceDirs = source.readStringArray();
+        splitDependencies = source.readSparseIntArray();
         nativeLibraryDir = source.readString();
         secondaryNativeLibraryDir = source.readString();
         nativeLibraryRootDir = source.readString();
@@ -1356,6 +1405,22 @@
     }
 
     /**
+     * Returns true if the app has declared in its manifest that it wants its split APKs to be
+     * loaded into isolated Contexts, with their own ClassLoaders and Resources objects.
+     * @hide
+     */
+    public boolean requestsIsolatedSplitLoading() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isStaticSharedLibrary() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) != 0;
+    }
+
+    /**
      * @hide
      */
     @Override protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 5cd15dd..53be953 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -45,6 +45,12 @@
     public String processName;
 
     /**
+     * The name of the split in which this component is declared.
+     * Null if the component was declared in the base APK.
+     */
+    public String splitName;
+
+    /**
      * A string resource identifier (in the package's resources) containing
      * a user-readable description of the component.  From the "description"
      * attribute or, if not set, 0.
@@ -53,7 +59,7 @@
     
     /**
      * Indicates whether or not this component may be instantiated.  Note that this value can be
-     * overriden by the one in its parent {@link ApplicationInfo}.
+     * overridden by the one in its parent {@link ApplicationInfo}.
      */
     public boolean enabled = true;
 
@@ -83,6 +89,7 @@
         super(orig);
         applicationInfo = orig.applicationInfo;
         processName = orig.processName;
+        splitName = orig.splitName;
         descriptionRes = orig.descriptionRes;
         enabled = orig.enabled;
         exported = orig.exported;
@@ -163,6 +170,9 @@
         if (processName != null && !packageName.equals(processName)) {
             pw.println(prefix + "processName=" + processName);
         }
+        if (splitName != null) {
+            pw.println(prefix + "splitName=" + splitName);
+        }
         pw.println(prefix + "enabled=" + enabled + " exported=" + exported
                 + " directBootAware=" + directBootAware);
         if (descriptionRes != 0) {
@@ -195,6 +205,7 @@
             applicationInfo.writeToParcel(dest, parcelableFlags);
         }
         dest.writeString(processName);
+        dest.writeString(splitName);
         dest.writeInt(descriptionRes);
         dest.writeInt(enabled ? 1 : 0);
         dest.writeInt(exported ? 1 : 0);
@@ -208,6 +219,7 @@
             applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
         }
         processName = source.readString();
+        splitName = source.readString();
         descriptionRes = source.readInt();
         enabled = (source.readInt() != 0);
         exported = (source.readInt() != 0);
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
index f620088..1d7b8f2 100644
--- a/core/java/android/content/pm/EphemeralResolveInfo.java
+++ b/core/java/android/content/pm/EphemeralResolveInfo.java
@@ -43,9 +43,11 @@
     private final String mPackageName;
     /** The filters used to match domain */
     private final List<EphemeralIntentFilter> mFilters;
+    /** The version code of the app that this class resolves to */
+    private final int mVersionCode;
     /** Filters only for legacy clients */
     @Deprecated
-    private List<IntentFilter> mLegacyFilters;
+    private final List<IntentFilter> mLegacyFilters;
 
     @Deprecated
     public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
@@ -59,10 +61,17 @@
         mFilters.add(new EphemeralIntentFilter(packageName, filters));
         mLegacyFilters = new ArrayList<IntentFilter>(filters.size());
         mLegacyFilters.addAll(filters);
+        mVersionCode = -1;
+    }
+
+    @Deprecated
+    public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
+            @Nullable List<EphemeralIntentFilter> filters) {
+        this(digest, packageName, filters, -1 /*versionCode*/);
     }
 
     public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
-            @Nullable List<EphemeralIntentFilter> filters) {
+            @Nullable List<EphemeralIntentFilter> filters, int versionConde) {
         // validate arguments
         if ((packageName == null && (filters != null && filters.size() != 0))
                 || (packageName != null && (filters == null || filters.size() == 0))) {
@@ -75,7 +84,9 @@
         } else {
             mFilters = null;
         }
+        mLegacyFilters = null;
         mPackageName = packageName;
+        mVersionCode = versionConde;
     }
 
     public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName,
@@ -88,6 +99,7 @@
         mPackageName = in.readString();
         mFilters = new ArrayList<EphemeralIntentFilter>();
         in.readList(mFilters, null /*loader*/);
+        mVersionCode = in.readInt();
         mLegacyFilters = new ArrayList<IntentFilter>();
         in.readList(mLegacyFilters, null /*loader*/);
     }
@@ -104,15 +116,19 @@
         return mPackageName;
     }
 
+    public List<EphemeralIntentFilter> getIntentFilters() {
+        return mFilters;
+    }
+
+    public int getVersionCode() {
+        return mVersionCode;
+    }
+
     @Deprecated
     public List<IntentFilter> getFilters() {
         return mLegacyFilters;
     }
 
-    public List<EphemeralIntentFilter> getIntentFilters() {
-        return mFilters;
-    }
-
     @Override
     public int describeContents() {
         return 0;
@@ -123,6 +139,7 @@
         out.writeParcelable(mDigest, flags);
         out.writeString(mPackageName);
         out.writeList(mFilters);
+        out.writeInt(mVersionCode);
         out.writeList(mLegacyFilters);
     }
 
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 430c7e7..c08bd1d 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IOnAppsChangedListener;
@@ -37,15 +38,21 @@
 interface ILauncherApps {
     void addOnAppsChangedListener(String callingPackage, in IOnAppsChangedListener listener);
     void removeOnAppsChangedListener(in IOnAppsChangedListener listener);
-    ParceledListSlice getLauncherActivities(String packageName, in UserHandle user);
-    ActivityInfo resolveActivity(in ComponentName component, in UserHandle user);
-    void startActivityAsUser(in ComponentName component, in Rect sourceBounds,
+    ParceledListSlice getLauncherActivities(
+            String callingPackage, String packageName, in UserHandle user);
+    ActivityInfo resolveActivity(
+            String callingPackage, in ComponentName component, in UserHandle user);
+    void startActivityAsUser(String callingPackage,
+            in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
-    void showAppDetailsAsUser(in ComponentName component, in Rect sourceBounds,
+    void showAppDetailsAsUser(
+            String callingPackage, in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
-    boolean isPackageEnabled(String packageName, in UserHandle user);
-    boolean isActivityEnabled(in ComponentName component, in UserHandle user);
-    ApplicationInfo getApplicationInfo(String packageName, int flags, in UserHandle user);
+    boolean isPackageEnabled(String callingPackage, String packageName, in UserHandle user);
+    boolean isActivityEnabled(
+            String callingPackage, in ComponentName component, in UserHandle user);
+    ApplicationInfo getApplicationInfo(
+            String callingPackage, String packageName, int flags, in UserHandle user);
 
     ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
             in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
@@ -60,4 +67,9 @@
             int userId);
 
     boolean hasShortcutHostPermission(String callingPackage);
+
+    ParceledListSlice getShortcutConfigActivities(
+            String callingPackage, String packageName, in UserHandle user);
+    IntentSender getShortcutConfigActivityIntent(String callingPackage, in ComponentName component,
+            in UserHandle user);
 }
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 154ff85..ecc8cd6 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -21,6 +21,7 @@
 import android.content.pm.IPackageInstallerSession;
 import android.content.pm.PackageInstaller;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.VersionedPackage;
 import android.content.IntentSender;
 
 import android.graphics.Bitmap;
@@ -44,7 +45,7 @@
     void registerCallback(IPackageInstallerCallback callback, int userId);
     void unregisterCallback(IPackageInstallerCallback callback);
 
-    void uninstall(String packageName, String callerPackageName, int flags,
+    void uninstall(in VersionedPackage versionedPackage, String callerPackageName, int flags,
             in IntentSender statusReceiver, int userId);
 
     void setPermissionsResult(int sessionId, boolean accepted);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 19cca8e..61531ae 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -47,6 +47,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VersionedPackage;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
@@ -63,6 +64,8 @@
     void checkPackageStartable(String packageName, int userId);
     boolean isPackageAvailable(String packageName, int userId);
     PackageInfo getPackageInfo(String packageName, int flags, int userId);
+    PackageInfo getPackageInfoVersioned(in VersionedPackage versionedPackage,
+            int flags, int userId);
     int getPackageUid(String packageName, int flags, int userId);
     int[] getPackageGids(String packageName, int flags, int userId);
 
@@ -138,6 +141,8 @@
 
     ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
 
+    ResolveInfo findPersistentPreferredActivity(in Intent intent, int userId);
+
     boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
 
     ParceledListSlice queryIntentActivities(in Intent intent,
@@ -229,18 +234,19 @@
     void setApplicationCategoryHint(String packageName, int categoryHint, String callerPackageName);
 
     /** @deprecated rawr, don't call AIDL methods directly! */
-    void deletePackageAsUser(in String packageName, IPackageDeleteObserver observer,
-            int userId, int flags);
+    void deletePackageAsUser(in String packageName, int versionCode,
+            IPackageDeleteObserver observer, int userId, int flags);
 
     /**
      * Delete a package for a specific user.
      *
-     * @param packageName The fully qualified name of the package to delete.
+     * @param versionedPackage The package to delete.
      * @param observer a callback to use to notify when the package deletion in finished.
      * @param userId the id of the user for whom to delete the package
      * @param flags - possible values: {@link #DONT_DELETE_DATA}
      */
-    void deletePackage(in String packageName, IPackageDeleteObserver2 observer, int userId, int flags);
+    void deletePackageVersioned(in VersionedPackage versionedPackage,
+            IPackageDeleteObserver2 observer, int userId, int flags);
 
     String getInstallerPackageName(in String packageName);
 
@@ -586,4 +592,8 @@
     List<String> getPreviousCodePaths(in String packageName);
 
     int getInstallReason(String packageName, int userId);
+
+    ParceledListSlice getSharedLibraries(int flags, int userId);
+
+    boolean canRequestPackageInstalls(String packageName, int userId);
 }
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 91df8e8..c90134a 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -15,6 +15,7 @@
  */
 package android.content.pm;
 
+import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
@@ -45,6 +46,8 @@
     boolean requestPinShortcut(String packageName, in ShortcutInfo shortcut,
             in IntentSender resultIntent, int userId);
 
+    Intent createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, int userId);
+
     void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage,
             int disabledMessageResId, int userId);
 
diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java
index 9d88cdd..a135d8f 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -18,6 +18,8 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 /**
  * Information you can retrieve about a particular piece of test
@@ -44,8 +46,12 @@
     public String publicSourceDir;
 
     /**
-     * Full paths to zero or more split APKs that, when combined with the base
-     * APK defined in {@link #sourceDir}, form a complete application.
+     * The names of all installed split APKs, ordered lexicographically.
+     */
+    public String[] splitNames;
+
+    /**
+     * Full paths to zero or more split APKs, indexed by the same order as {@link #splitNames}.
      */
     public String[] splitSourceDirs;
 
@@ -53,10 +59,31 @@
      * Full path to the publicly available parts of {@link #splitSourceDirs},
      * including resources and manifest. This may be different from
      * {@link #splitSourceDirs} if an application is forward locked.
+     *
+     * @see #splitSourceDirs
      */
     public String[] splitPublicSourceDirs;
 
     /**
+     * Maps the dependencies between split APKs. All splits implicitly depend on the base APK.
+     *
+     * Available since platform version O.
+     *
+     * Only populated if the application opts in to isolated split loading via the
+     * {@link android.R.attr.isolatedSplits} attribute in the &lt;manifest&gt; tag of the app's
+     * AndroidManifest.xml.
+     *
+     * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs},
+     * and {@link #splitPublicSourceDirs} arrays.
+     * Each key represents a split and its value is its parent split.
+     * Cycles do not exist because they are illegal and screened for during installation.
+     *
+     * May be null if no splits are installed, or if no dependencies exist between them.
+     * @hide
+     */
+    public SparseIntArray splitDependencies;
+
+    /**
      * Full path to a directory assigned to the package for its persistent data.
      */
     public String dataDir;
@@ -88,8 +115,10 @@
         targetPackage = orig.targetPackage;
         sourceDir = orig.sourceDir;
         publicSourceDir = orig.publicSourceDir;
+        splitNames = orig.splitNames;
         splitSourceDirs = orig.splitSourceDirs;
         splitPublicSourceDirs = orig.splitPublicSourceDirs;
+        splitDependencies = orig.splitDependencies;
         dataDir = orig.dataDir;
         deviceProtectedDataDir = orig.deviceProtectedDataDir;
         credentialProtectedDataDir = orig.credentialProtectedDataDir;
@@ -114,8 +143,10 @@
         dest.writeString(targetPackage);
         dest.writeString(sourceDir);
         dest.writeString(publicSourceDir);
+        dest.writeStringArray(splitNames);
         dest.writeStringArray(splitSourceDirs);
         dest.writeStringArray(splitPublicSourceDirs);
+        dest.writeSparseIntArray(splitDependencies);
         dest.writeString(dataDir);
         dest.writeString(deviceProtectedDataDir);
         dest.writeString(credentialProtectedDataDir);
@@ -135,13 +166,16 @@
         }
     };
 
+    @SuppressWarnings("unchecked")
     private InstrumentationInfo(Parcel source) {
         super(source);
         targetPackage = source.readString();
         sourceDir = source.readString();
         publicSourceDir = source.readString();
+        splitNames = source.readStringArray();
         splitSourceDirs = source.readStringArray();
         splitPublicSourceDirs = source.readStringArray();
+        splitDependencies = source.readSparseIntArray();
         dataDir = source.readString();
         deviceProtectedDataDir = source.readString();
         credentialProtectedDataDir = source.readString();
@@ -156,8 +190,10 @@
         ai.packageName = packageName;
         ai.sourceDir = sourceDir;
         ai.publicSourceDir = publicSourceDir;
+        ai.splitNames = splitNames;
         ai.splitSourceDirs = splitSourceDirs;
         ai.splitPublicSourceDirs = splitPublicSourceDirs;
+        ai.splitDependencies = splitDependencies;
         ai.dataDir = dataDir;
         ai.deviceProtectedDataDir = deviceProtectedDataDir;
         ai.credentialProtectedDataDir = credentialProtectedDataDir;
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
index f12abf3..068973b 100644
--- a/core/java/android/content/pm/IntentFilterVerificationInfo.java
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -22,6 +22,7 @@
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -36,6 +37,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Set;
 
 /**
  * The {@link com.android.server.pm.PackageManagerService} maintains some
@@ -43,6 +45,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class IntentFilterVerificationInfo implements Parcelable {
     private static final String TAG = IntentFilterVerificationInfo.class.getName();
 
@@ -55,22 +58,26 @@
     private String mPackageName;
     private int mMainStatus;
 
+    /** @hide */
     public IntentFilterVerificationInfo() {
         mPackageName = null;
         mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
     }
 
+    /** @hide */
     public IntentFilterVerificationInfo(String packageName, ArraySet<String> domains) {
         mPackageName = packageName;
         mDomains = domains;
         mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
     }
 
+    /** @hide */
     public IntentFilterVerificationInfo(XmlPullParser parser)
             throws IOException, XmlPullParserException {
         readFromXml(parser);
     }
 
+    /** @hide */
     public IntentFilterVerificationInfo(Parcel source) {
         readFromParcel(source);
     }
@@ -83,6 +90,7 @@
         return mMainStatus;
     }
 
+    /** @hide */
     public void setStatus(int s) {
         if (s >= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED &&
                 s <= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
@@ -92,14 +100,16 @@
         }
     }
 
-    public ArraySet<String> getDomains() {
+    public Set<String> getDomains() {
         return mDomains;
     }
 
+    /** @hide */
     public void setDomains(ArraySet<String> list) {
         mDomains = list;
     }
 
+    /** @hide */
     public String getDomainsString() {
         StringBuilder sb = new StringBuilder();
         for (String str : mDomains) {
@@ -135,6 +145,7 @@
         }
     }
 
+    /** @hide */
     public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
             IOException {
         mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null);
@@ -170,6 +181,7 @@
         }
     }
 
+    /** @hide */
     public void writeToXml(XmlSerializer serializer) throws IOException {
         serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
         serializer.attribute(null, ATTR_STATUS, String.valueOf(mMainStatus));
@@ -180,10 +192,12 @@
         }
     }
 
+    /** @hide */
     public String getStatusString() {
         return getStatusStringFromValue(((long)mMainStatus) << 32);
     }
 
+    /** @hide */
     public static String getStatusStringFromValue(long val) {
         StringBuilder sb = new StringBuilder();
         switch ((int)(val >> 32)) {
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 4cdd653..57d2ba7 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -27,6 +27,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.PackageManager.ApplicationInfoFlags;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
@@ -60,15 +61,18 @@
 
 /**
  * Class for retrieving a list of launchable activities for the current user and any associated
- * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile.
+ * managed profiles that are visible to the current user, which can be retrieved with
+ * {@link #getProfiles}. This is mainly for use by launchers.
+ *
+ * Apps can be queried for each user profile.
  * Since the PackageManager will not deliver package broadcasts for other profiles, you can register
  * for package changes here.
  * <p>
  * To watch for managed profiles being added or removed, register for the following broadcasts:
  * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}.
  * <p>
- * You can retrieve the list of profiles associated with this user with
- * {@link UserManager#getUserProfiles()}.
+ * Note as of Android O, apps on a managed profile are no longer allowed to access apps on the
+ * main profile.  Apps can only access profiles returned by {@link #getProfiles()}.
  */
 public class LauncherApps {
 
@@ -375,6 +379,24 @@
     }
 
     /**
+     * Return a list of profiles that the caller can access via the {@link LauncherApps} APIs.
+     *
+     * <p>If the caller is running on a managed profile, it'll return only the current profile.
+     * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
+     */
+    public List<UserHandle> getProfiles() {
+        final UserManager um = mContext.getSystemService(UserManager.class);
+        if (um.isManagedProfile()) {
+            // If it's a managed profile, only return the current profile.
+            final List result =  new ArrayList(1);
+            result.add(android.os.Process.myUserHandle());
+            return result;
+        } else {
+            return um.getUserProfiles();
+        }
+    }
+
+    /**
      * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
      * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
      *
@@ -384,25 +406,12 @@
      * @return List of launchable activities. Can be an empty list but will not be null.
      */
     public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
-        ParceledListSlice<ResolveInfo> activities = null;
         try {
-            activities = mService.getLauncherActivities(packageName, user);
+            return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
+                    packageName, user), user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
-        if (activities == null) {
-            return Collections.EMPTY_LIST;
-        }
-        ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
-        for (ResolveInfo ri : activities.getList()) {
-            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
-            if (DEBUG) {
-                Log.v(TAG, "Returning activity for profile " + user + " : "
-                        + lai.getComponentName());
-            }
-            lais.add(lai);
-        }
-        return lais;
     }
 
     /**
@@ -415,7 +424,8 @@
      */
     public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
         try {
-            ActivityInfo ai = mService.resolveActivity(intent.getComponent(), user);
+            ActivityInfo ai = mService.resolveActivity(mContext.getPackageName(),
+                    intent.getComponent(), user);
             if (ai != null) {
                 LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
                 return info;
@@ -440,7 +450,8 @@
             Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
         }
         try {
-            mService.startActivityAsUser(component, sourceBounds, opts, user);
+            mService.startActivityAsUser(mContext.getPackageName(),
+                    component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -458,7 +469,76 @@
     public void startAppDetailsActivity(ComponentName component, UserHandle user,
             Rect sourceBounds, Bundle opts) {
         try {
-            mService.showAppDetailsAsUser(component, sourceBounds, opts, user);
+            mService.showAppDetailsAsUser(mContext.getPackageName(),
+                    component, sourceBounds, opts, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves a list of config activities for creating {@link ShortcutInfo}.
+     *
+     * @param packageName The specific package to query. If null, it checks all installed packages
+     *            in the profile.
+     * @param user The UserHandle of the profile.
+     * @return List of config activities. Can be an empty list but will not be null.
+     *
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     * @see #getShortcutConfigActivityIntent(LauncherActivityInfo)
+     */
+    public List<LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String packageName,
+            @NonNull UserHandle user) {
+        try {
+            return convertToActivityList(mService.getShortcutConfigActivities(
+                    mContext.getPackageName(), packageName, user),
+                    user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    private List<LauncherActivityInfo> convertToActivityList(
+            @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) {
+        if (activities == null) {
+            return Collections.EMPTY_LIST;
+        }
+        ArrayList<LauncherActivityInfo> lais = new ArrayList<>();
+        for (ResolveInfo ri : activities.getList()) {
+            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
+            if (DEBUG) {
+                Log.v(TAG, "Returning activity for profile " + user + " : "
+                        + lai.getComponentName());
+            }
+            lais.add(lai);
+        }
+        return lais;
+    }
+
+    /**
+     * Returns an intent sender which can be used to start the configure activity for creating
+     * custom shortcuts. Use this method if the provider is in another profile as you are not
+     * allowed to start an activity in another profile.
+     *
+     * <p>The caller should receive {@link PinItemRequest} in onActivityResult on
+     * {@link android.app.Activity#RESULT_OK}.
+     *
+     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+     * #hasShortcutHostPermission()}.
+     *
+     * @param info a configuration activity returned by {@link #getShortcutConfigActivityList}
+     *
+     * @throws IllegalStateException when the user is locked or not running.
+     * @throws SecurityException if {@link #hasShortcutHostPermission()} is false.
+     *
+     * @see #getPinItemRequest(Intent)
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     * @see android.app.Activity#startIntentSenderForResult
+     */
+    public IntentSender getShortcutConfigActivityIntent(@NonNull LauncherActivityInfo info) {
+        try {
+            return mService.getShortcutConfigActivityIntent(
+                    mContext.getPackageName(), info.getComponentName(), info.getUser());
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -474,7 +554,7 @@
      */
     public boolean isPackageEnabled(String packageName, UserHandle user) {
         try {
-            return mService.isPackageEnabled(packageName, user);
+            return mService.isPackageEnabled(mContext.getPackageName(), packageName, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -494,7 +574,7 @@
     public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags,
             UserHandle user) {
         try {
-            return mService.getApplicationInfo(packageName, flags, user);
+            return mService.getApplicationInfo(mContext.getPackageName(), packageName, flags, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -510,7 +590,7 @@
      */
     public boolean isActivityEnabled(ComponentName component, UserHandle user) {
         try {
-            return mService.isActivityEnabled(component, user);
+            return mService.isActivityEnabled(mContext.getPackageName(), component, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -1131,6 +1211,13 @@
      * Represents a "pin shortcut" request made by an app, which is sent with
      * an {@link #ACTION_CONFIRM_PIN_ITEM} intent to the default launcher app.
      *
+     * <p>Note the launcher may receive a request to pin a shortcut that is already pinned, because
+     * the user may actually want to have multiple icons of the same shortcut on the launcher.
+     * The launcher can tell this case by calling {@link ShortcutInfo#isPinned()} on the shortcut
+     * returned by {@link #getShortcutInfo()}.  In this case, calling {@link #accept()} is optional;
+     * even if the launcher does not call it, the shortcut is already pinned.  Also in this case,
+     * the {@code options} argument to {@link #accept(Bundle)} will be ignored.
+     *
      * @see #EXTRA_PIN_ITEM_REQUEST
      * @see #getPinItemRequest(Intent)
      */
@@ -1195,8 +1282,13 @@
          * {@link #REQUEST_TYPE_APPWIDGET} request.
          */
         @Nullable
-        public AppWidgetProviderInfo getAppWidgetProviderInfo() {
-            return mAppWidgetInfo;
+        public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) {
+            if (mAppWidgetInfo != null) {
+                AppWidgetProviderInfo info = mAppWidgetInfo.clone();
+                info.updateDimensions(context.getResources().getDisplayMetrics());
+                return info;
+            }
+            return null;
         }
 
         /**
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index d40bab5..5d5696b 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -142,7 +142,7 @@
      * {@link PackageManager#GET_INSTRUMENTATION} was set.
      */
     public InstrumentationInfo[] instrumentation;
-    
+
     /**
      * Array of all {@link android.R.styleable#AndroidManifestPermission
      * &lt;permission&gt;} tags included under &lt;manifest&gt;,
@@ -150,7 +150,7 @@
      * {@link PackageManager#GET_PERMISSIONS} was set.
      */
     public PermissionInfo[] permissions;
-    
+
     /**
      * Array of all {@link android.R.styleable#AndroidManifestUsesPermission
      * &lt;uses-permission&gt;} tags included under &lt;manifest&gt;,
@@ -160,7 +160,7 @@
      * by the system at install time.
      */
     public String[] requestedPermissions;
-    
+
     /**
      * Array of flags of all {@link android.R.styleable#AndroidManifestUsesPermission
      * &lt;uses-permission&gt;} tags included under &lt;manifest&gt;,
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index db3f637..4de967c 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -23,7 +24,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.app.ActivityManager;
-import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.graphics.Bitmap;
@@ -36,9 +36,11 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.annotation.IntRange;
 import android.util.ExceptionUtils;
 
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -256,8 +258,6 @@
      */
     public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
 
-    private final Context mContext;
-    private final PackageManager mPm;
     private final IPackageInstaller mInstaller;
     private final int mUserId;
     private final String mInstallerPackageName;
@@ -265,10 +265,8 @@
     private final ArrayList<SessionCallbackDelegate> mDelegates = new ArrayList<>();
 
     /** {@hide} */
-    public PackageInstaller(Context context, PackageManager pm, IPackageInstaller installer,
+    public PackageInstaller(IPackageInstaller installer,
             String installerPackageName, int userId) {
-        mContext = context;
-        mPm = pm;
         mInstaller = installer;
         mInstallerPackageName = installerPackageName;
         mUserId = userId;
@@ -413,10 +411,35 @@
      * Uninstall the given package, removing it completely from the device. This
      * method is only available to the current "installer of record" for the
      * package.
+     *
+     * @param packageName The package to uninstall.
+     * @param statusReceiver Where to deliver the result.
      */
     public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
+       uninstall(new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
+               statusReceiver);
+    }
+
+    /**
+     * Uninstall the given package with a specific version code, removing it
+     * completely from the device. This method is only available to the current
+     * "installer of record" for the package. If the version code of the package
+     * does not match the one passed in the versioned package argument this
+     * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
+     * uninstall the latest version of the package.
+     *
+     * @param versionedPackage The versioned package to uninstall.
+     * @param statusReceiver Where to deliver the result.
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.DELETE_PACKAGES,
+            Manifest.permission.REQUEST_DELETE_PACKAGES})
+    public void uninstall(@NonNull VersionedPackage versionedPackage,
+            @NonNull IntentSender statusReceiver) {
+        Preconditions.checkNotNull(versionedPackage, "versionedPackage cannot be null");
         try {
-            mInstaller.uninstall(packageName, mInstallerPackageName, 0, statusReceiver, mUserId);
+            mInstaller.uninstall(versionedPackage, mInstallerPackageName,
+                    0, statusReceiver, mUserId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index bc79f41..11830c2 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -24,14 +24,10 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.UserHandle;
-import android.text.BidiFormatter;
 import android.text.Html;
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.util.Printer;
-import android.text.BidiFormatter;
-import android.text.TextPaint;
-import android.text.Html;
 import java.text.Collator;
 import java.util.Comparator;
 
@@ -50,31 +46,31 @@
      * Public name of this item. From the "android:name" attribute.
      */
     public String name;
-    
+
     /**
      * Name of the package that this item is in.
      */
     public String packageName;
-    
+
     /**
      * A string resource identifier (in the package's resources) of this
      * component's label.  From the "label" attribute or, if not set, 0.
      */
     public int labelRes;
-    
+
     /**
      * The string provided in the AndroidManifest file, if any.  You
      * probably don't want to use this.  You probably want
      * {@link PackageManager#getApplicationLabel}
      */
     public CharSequence nonLocalizedLabel;
-    
+
     /**
      * A drawable resource identifier (in the package's resources) of this
      * component's icon.  From the "icon" attribute or, if not set, 0.
      */
     public int icon;
-    
+
     /**
      * A drawable resource identifier (in the package's resources) of this
      * component's banner.  From the "banner" attribute or, if not set, 0.
@@ -85,10 +81,10 @@
      * A drawable resource identifier (in the package's resources) of this
      * component's logo. Logos may be larger/wider than icons and are
      * displayed by certain UI elements in place of a name or name/icon
-     * combination. From the "logo" attribute or, if not set, 0. 
+     * combination. From the "logo" attribute or, if not set, 0.
      */
     public int logo;
-    
+
     /**
      * Additional meta-data associated with this component.  This field
      * will only be filled in if you set the
@@ -124,10 +120,10 @@
      * Retrieve the current textual label associated with this item.  This
      * will call back on the given PackageManager to load the label from
      * the application.
-     * 
+     *
      * @param pm A PackageManager from which the label can be loaded; usually
      * the PackageManager from which you originally retrieved this item.
-     * 
+     *
      * @return Returns a CharSequence containing the item's label.  If the
      * item does not have a label, its name is returned.
      */
@@ -146,7 +142,7 @@
         }
         return packageName;
     }
- 
+
     /**
      * Same as {@link #loadLabel(PackageManager)} with the addition that
      * the returned label is safe for being presented in the UI since it
@@ -207,10 +203,10 @@
      * Retrieve the current graphical icon associated with this item.  This
      * will call back on the given PackageManager to load the icon from
      * the application.
-     * 
+     *
      * @param pm A PackageManager from which the icon can be loaded; usually
      * the PackageManager from which you originally retrieved this item.
-     * 
+     *
      * @return Returns a Drawable containing the item's icon.  If the
      * item does not have an icon, the item's default icon is returned
      * such as the default activity icon.
@@ -259,13 +255,13 @@
 
     /**
      * Retrieve the default graphical icon associated with this item.
-     * 
+     *
      * @param pm A PackageManager from which the icon can be loaded; usually
      * the PackageManager from which you originally retrieved this item.
-     * 
+     *
      * @return Returns a Drawable containing the item's default icon
      * such as the default activity icon.
-     * 
+     *
      * @hide
      */
     public Drawable loadDefaultIcon(PackageManager pm) {
@@ -291,10 +287,10 @@
      * Retrieve the current graphical logo associated with this item. This
      * will call back on the given PackageManager to load the logo from
      * the application.
-     * 
+     *
      * @param pm A PackageManager from which the logo can be loaded; usually
      * the PackageManager from which you originally retrieved this item.
-     * 
+     *
      * @return Returns a Drawable containing the item's logo. If the item
      * does not have a logo, this method will return null.
      */
@@ -307,31 +303,31 @@
         }
         return loadDefaultLogo(pm);
     }
-    
+
     /**
      * Retrieve the default graphical logo associated with this item.
-     * 
+     *
      * @param pm A PackageManager from which the logo can be loaded; usually
      * the PackageManager from which you originally retrieved this item.
-     * 
+     *
      * @return Returns a Drawable containing the item's default logo
      * or null if no default logo is available.
-     * 
+     *
      * @hide
      */
     protected Drawable loadDefaultLogo(PackageManager pm) {
         return null;
     }
-    
+
     /**
      * Load an XML resource attached to the meta-data of this item.  This will
      * retrieved the name meta-data entry, and if defined call back on the
      * given PackageManager to load its XML file from the application.
-     * 
+     *
      * @param pm A PackageManager from which the XML can be loaded; usually
      * the PackageManager from which you originally retrieved this item.
      * @param name Name of the meta-date you would like to load.
-     * 
+     *
      * @return Returns an XmlPullParser you can use to parse the XML file
      * assigned as the given meta-data.  If the meta-data name is not defined
      * or the XML resource could not be found, null is returned.
@@ -373,11 +369,11 @@
                     + " banner=0x" + Integer.toHexString(banner));
         }
     }
-    
+
     protected void dumpBack(Printer pw, String prefix) {
         // no back here
     }
-    
+
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeString(name);
         dest.writeString(packageName);
@@ -389,7 +385,7 @@
         dest.writeInt(banner);
         dest.writeInt(showUserIcon);
     }
-    
+
     protected PackageItemInfo(Parcel source) {
         name = source.readString();
         packageName = source.readString();
@@ -406,9 +402,9 @@
     /**
      * Get the ApplicationInfo for the application to which this item belongs,
      * if available, otherwise returns null.
-     * 
+     *
      * @return Returns the ApplicationInfo of this item, or null if not known.
-     * 
+     *
      * @hide
      */
     protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 04e649c..184f1856 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -20,6 +20,7 @@
 import android.annotation.CheckResult;
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -1287,6 +1288,13 @@
     public static final int DELETE_FAILED_ABORTED = -5;
 
     /**
+     * Deletion failed return code: this is passed to the
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * because the packge is a shared library used by other installed packages.
+     * {@hide} */
+    public static final int DELETE_FAILED_USED_SHARED_LIBRARY = -6;
+
+    /**
      * Return code that is passed to the {@link IPackageMoveObserver} when the
      * package has been successfully moved by the system.
      *
@@ -1360,6 +1368,14 @@
     public static final int MOVE_FAILED_DEVICE_ADMIN = -8;
 
     /**
+     * Error code that is passed to the {@link IPackageMoveObserver} if system does not allow
+     * non-system apps to be moved to internal storage.
+     *
+     * @hide
+     */
+    public static final int MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL = -9;
+
+    /**
      * Flag parameter for {@link #movePackage} to indicate that
      * the package should be moved to internal storage if its
      * been installed on external media.
@@ -1428,6 +1444,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0;
 
     /**
@@ -1438,6 +1455,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1;
 
     /**
@@ -1449,6 +1467,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2;
 
     /**
@@ -1460,6 +1479,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3;
 
     /**
@@ -1473,6 +1493,7 @@
      *
      * @hide
      */
+    @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4;
 
     /**
@@ -1854,6 +1875,20 @@
     public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports telephony carrier restriction mechanism.
+     *
+     * <p>Devices declaring this feature must have an implementation of the
+     * {@link android.telephony.TelephonyManager#getAllowedCarriers} and
+     * {@link android.telephony.TelephonyManager#setAllowedCarriers}.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_TELEPHONY_CARRIERLOCK =
+            "android.hardware.telephony.carrierlock";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports connecting to USB devices
      * as the USB host.
@@ -2127,6 +2162,15 @@
     public static final String FEATURE_WATCH = "android.hardware.type.watch";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a device for IoT and may not have an UI. An embedded
+     * device is defined as a full stack Android device with or without a display and no
+     * user-installable apps.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device supports printing.
      */
@@ -2602,6 +2646,11 @@
     public static final int NOTIFY_PACKAGE_USE_REASONS_COUNT = 8;
 
     /**
+     * Constant for specifying the highest installed package version code.
+     */
+    public static final int VERSION_CODE_HIGHEST = -1;
+
+    /**
      * Retrieve overall information about an application package that is
      * installed on the system.
      *
@@ -2649,7 +2698,58 @@
             throws NameNotFoundException;
 
     /**
-     * @hide
+     * Retrieve overall information about an application package that is
+     * installed on the system. This method can be used for retrieving
+     * information about packages for which multiple versions can be
+     * installed at the time. Currently only packages hosting static shared
+     * libraries can have multiple installed versions. The method can also
+     * be used to get info for a package that has a single version installed
+     * by passing {@link #VERSION_CODE_HIGHEST} in the {@link VersionedPackage}
+     * constructor.
+     *
+     * @param versionedPackage The versioned packages for which to query.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS},
+     *         {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION},
+     *         {@link #GET_INTENT_FILTERS}, {@link #GET_META_DATA},
+     *         {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+     *         {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #GET_SIGNATURES},
+     *         {@link #GET_URI_PERMISSION_PATTERNS}, {@link #GET_UNINSTALLED_PACKAGES},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
+     *
+     * @return A PackageInfo object containing information about the
+     *         package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the
+     *         package is not found in the list of installed applications, the
+     *         package information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DONT_DELETE_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     * @see #GET_ACTIVITIES
+     * @see #GET_CONFIGURATIONS
+     * @see #GET_GIDS
+     * @see #GET_INSTRUMENTATION
+     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
+     * @see #GET_PERMISSIONS
+     * @see #GET_PROVIDERS
+     * @see #GET_RECEIVERS
+     * @see #GET_SERVICES
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #GET_SIGNATURES
+     * @see #GET_URI_PERMISSION_PATTERNS
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     */
+    public abstract PackageInfo getPackageInfo(VersionedPackage versionedPackage,
+            @PackageInfoFlags int flags) throws NameNotFoundException;
+
+    /**
      * Retrieve overall information about an application package that is
      * installed on the system.
      *
@@ -2693,6 +2793,8 @@
      * @see #MATCH_DISABLED_COMPONENTS
      * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
      * @see #MATCH_UNINSTALLED_PACKAGES
+     *
+     * @hide
      */
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     public abstract PackageInfo getPackageInfoAsUser(String packageName,
@@ -3683,6 +3785,37 @@
     public abstract String[] getSystemSharedLibraryNames();
 
     /**
+     * Get a list of shared libraries on the device.
+     *
+     * @param flags To filter the libraries to return.
+     * @return The shared library list.
+     *
+     * @see #MATCH_FACTORY_ONLY
+     * @see #MATCH_KNOWN_PACKAGES
+     * @see #MATCH_ANY_USER
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     */
+    public abstract @NonNull List<SharedLibraryInfo> getSharedLibraries(
+            @InstallFlags int flags);
+
+    /**
+     * Get a list of shared libraries on the device.
+     *
+     * @param flags To filter the libraries to return.
+     * @param userId The user to query for.
+     * @return The shared library list.
+     *
+     * @see #MATCH_FACTORY_ONLY
+     * @see #MATCH_KNOWN_PACKAGES
+     * @see #MATCH_ANY_USER
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     *
+     * @hide
+     */
+    public abstract @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(
+            @InstallFlags int flags, @UserIdInt int userId);
+
+    /**
      * Get the name of the package hosting the services shared library.
      *
      * @return The library host package.
@@ -4951,6 +5084,7 @@
      *
      * @hide
      */
+    @SystemApi
     public abstract int getIntentVerificationStatusAsUser(String packageName, @UserIdInt int userId);
 
     /**
@@ -4973,6 +5107,7 @@
      *
      * @hide
      */
+    @SystemApi
     public abstract boolean updateIntentVerificationStatusAsUser(String packageName, int status,
             @UserIdInt int userId);
 
@@ -4988,6 +5123,7 @@
      *
      * @hide
      */
+    @SystemApi
     public abstract List<IntentFilterVerificationInfo> getIntentFilterVerifications(
             String packageName);
 
@@ -5002,6 +5138,7 @@
      *
      * @hide
      */
+    @SystemApi
     public abstract List<IntentFilter> getAllIntentFilters(String packageName);
 
     /**
@@ -5015,6 +5152,7 @@
      * @hide
      */
     @TestApi
+    @SystemApi
     public abstract String getDefaultBrowserPackageNameAsUser(@UserIdInt int userId);
 
     /**
@@ -5029,6 +5167,7 @@
      *
      * @hide
      */
+    @SystemApi
     public abstract boolean setDefaultBrowserPackageNameAsUser(String packageName,
             @UserIdInt int userId);
 
@@ -5066,6 +5205,7 @@
      *            indicate that no callback is desired.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.DELETE_PACKAGES)
     public abstract void deletePackage(String packageName, IPackageDeleteObserver observer,
             @DeleteFlags int flags);
 
@@ -5084,11 +5224,11 @@
      * @param userId The user Id
      * @hide
      */
-     @RequiresPermission(anyOf = {
+    @RequiresPermission(anyOf = {
             Manifest.permission.DELETE_PACKAGES,
             Manifest.permission.INTERACT_ACROSS_USERS_FULL})
-    public abstract void deletePackageAsUser(String packageName, IPackageDeleteObserver observer,
-            @DeleteFlags int flags, @UserIdInt int userId);
+    public abstract void deletePackageAsUser(@NonNull String packageName,
+            IPackageDeleteObserver observer, @DeleteFlags int flags, @UserIdInt int userId);
 
     /**
      * Retrieve the package name of the application that installed a package. This identifies
@@ -5829,6 +5969,7 @@
             case DELETE_FAILED_USER_RESTRICTED: return "DELETE_FAILED_USER_RESTRICTED";
             case DELETE_FAILED_OWNER_BLOCKED: return "DELETE_FAILED_OWNER_BLOCKED";
             case DELETE_FAILED_ABORTED: return "DELETE_FAILED_ABORTED";
+            case DELETE_FAILED_USED_SHARED_LIBRARY: return "DELETE_FAILED_USED_SHARED_LIBRARY";
             default: return Integer.toString(status);
         }
     }
@@ -5842,6 +5983,7 @@
             case DELETE_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
             case DELETE_FAILED_OWNER_BLOCKED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
             case DELETE_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            case DELETE_FAILED_USED_SHARED_LIBRARY: return PackageInstaller.STATUS_FAILURE_CONFLICT;
             default: return PackageInstaller.STATUS_FAILURE;
         }
     }
@@ -5917,4 +6059,21 @@
     @TestApi
     public abstract @InstallReason int getInstallReason(String packageName,
             @NonNull UserHandle user);
+
+    /**
+     * Checks whether the calling package is allowed to request package installs through package
+     * installer. Apps are encouraged to call this api before launching the package installer via
+     * intent {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. Starting from Android O, the
+     * user can explicitly choose what external sources they trust to install apps on the device.
+     * If this api returns false, the install request will be blocked by the package installer and
+     * a dialog will be shown to the user with an option to launch settings to change their
+     * preference. An application must target Android O or higher and declare permission
+     * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this api.
+     *
+     * @return true if the calling package is trusted by the user to request install packages on
+     * the device, false otherwise.
+     * @see {@link android.content.Intent#ACTION_INSTALL_PACKAGE}
+     * @see {@link android.provider.Settings#ACTION_MANAGE_EXTERNAL_SOURCES}
+     */
+    public abstract boolean canRequestPackageInstalls();
 }
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 2590a6b..a90b18a 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -138,9 +138,8 @@
      * @param userId The user under which to check.
      *
      * @return An {@link ApplicationInfo} containing information about the
-     *         package.
-     * @throws NameNotFoundException if a package with the given name cannot be
-     *             found on the system.
+     *         package, or {@code null} if no application exists with that
+     *         package name.
      */
     public abstract ApplicationInfo getApplicationInfo(String packageName, int userId);
 
@@ -223,7 +222,49 @@
             int userId);
 
     /**
+     * Grants access to the package metadata for an ephemeral application.
+     * <p>
+     * When an ephemeral application explicitly tries to interact with a full
+     * install application [via an activity, service or provider that has been
+     * exposed using the {@code visibleToInstantApp} attribute], the normal
+     * application must be able to see metadata about the connecting ephemeral
+     * app. If the ephemeral application uses an implicit intent [ie action VIEW,
+     * category BROWSABLE], it remains hidden from the launched activity.
+     * <p>
+     * If the {@code sourceUid} is not for an ephemeral app or {@code targetUid}
+     * is not for a fully installed app, this method will be a no-op.
+     *
+     * @param userId the user
+     * @param intent the intent that triggered the grant
+     * @param targetAppId The app ID of the fully installed application
+     * @param ephemeralAppId The app ID of the ephemeral application
+     */
+    public abstract void grantEphemeralAccess(int userId, Intent intent,
+            int targetAppId, int ephemeralAppId);
+
+    /**
      * @return The SetupWizard package name.
      */
     public abstract String getSetupWizardPackageName();
+
+    public interface ExternalSourcesPolicy {
+
+        int USER_TRUSTED = 0;   // User has trusted the package to install apps
+        int USER_BLOCKED = 1;   // User has blocked the package to install apps
+        int USER_DEFAULT = 2;   // Default code to use when user response is unavailable
+
+        /**
+         * Checks the user preference for whether a package is trusted to request installs through
+         * package installer
+         *
+         * @param packageName The package to check for
+         * @param uid the uid in which the package is running
+         * @return {@link USER_TRUSTED} if the user has trusted the package, {@link USER_BLOCKED}
+         * if user has blocked requests from the package, {@link USER_DEFAULT} if the user response
+         * is not yet available
+         */
+        int getPackageTrustedToInstallApps(String packageName, int uid);
+    }
+
+    public abstract void setExternalSourcesPolicy(ExternalSourcesPolicy policy);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 083e4cc6..8223726 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -18,12 +18,12 @@
 
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER;
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -49,6 +49,9 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.split.SplitAssetDependencyLoader;
+import android.content.pm.split.SplitAssetLoader;
+import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -74,6 +77,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.util.apk.ApkSignatureSchemeV2Verifier;
 import android.util.jar.StrictJarFile;
@@ -106,6 +110,7 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
@@ -155,6 +160,7 @@
 
     private static final String TAG_MANIFEST = "manifest";
     private static final String TAG_APPLICATION = "application";
+    private static final String TAG_PACKAGE_VERIFIER = "package-verifier";
     private static final String TAG_OVERLAY = "overlay";
     private static final String TAG_KEY_SETS = "key-sets";
     private static final String TAG_PERMISSION_GROUP = "permission-group";
@@ -178,6 +184,14 @@
     private static final String TAG_EAT_COMMENT = "eat-comment";
     private static final String TAG_PACKAGE = "package";
     private static final String TAG_RESTRICT_UPDATE = "restrict-update";
+    private static final String TAG_USES_SPLIT = "uses-split";
+
+    /**
+     * Bit mask of all the valid bits that can be set in restartOnConfigChanges.
+     * @hide
+     */
+    private static final int RESTART_ON_CONFIG_CHANGES_MASK =
+            ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
 
     // These are the tags supported by child packages
     private static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>();
@@ -345,6 +359,9 @@
         /** Names of any split APKs, ordered by parsed splitName */
         public final String[] splitNames;
 
+        /** Dependencies of any split APKs, ordered by parsed splitName */
+        public final String[] usesSplitNames;
+
         /**
          * Path where this package was found on disk. For monolithic packages
          * this is path to single base APK file; for cluster packages this is
@@ -367,14 +384,17 @@
         public final boolean multiArch;
         public final boolean use32bitAbi;
         public final boolean extractNativeLibs;
+        public final boolean isolatedSplits;
 
         public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
-                String[] splitCodePaths, int[] splitRevisionCodes) {
+                String[] usesSplitNames, String[] splitCodePaths,
+                int[] splitRevisionCodes) {
             this.packageName = baseApk.packageName;
             this.versionCode = baseApk.versionCode;
             this.installLocation = baseApk.installLocation;
             this.verifiers = baseApk.verifiers;
             this.splitNames = splitNames;
+            this.usesSplitNames = usesSplitNames;
             this.codePath = codePath;
             this.baseCodePath = baseApk.codePath;
             this.splitCodePaths = splitCodePaths;
@@ -385,6 +405,7 @@
             this.multiArch = baseApk.multiArch;
             this.use32bitAbi = baseApk.use32bitAbi;
             this.extractNativeLibs = baseApk.extractNativeLibs;
+            this.isolatedSplits = baseApk.isolatedSplits;
         }
 
         public List<String> getAllCodePaths() {
@@ -404,6 +425,7 @@
         public final String codePath;
         public final String packageName;
         public final String splitName;
+        public final String usesSplitName;
         public final int versionCode;
         public final int revisionCode;
         public final int installLocation;
@@ -415,15 +437,17 @@
         public final boolean multiArch;
         public final boolean use32bitAbi;
         public final boolean extractNativeLibs;
+        public final boolean isolatedSplits;
 
-        public ApkLite(String codePath, String packageName, String splitName, int versionCode,
-                int revisionCode, int installLocation, List<VerifierInfo> verifiers,
+        public ApkLite(String codePath, String packageName, String splitName, String usesSplitName,
+                int versionCode, int revisionCode, int installLocation, List<VerifierInfo> verifiers,
                 Signature[] signatures, Certificate[][] certificates, boolean coreApp,
                 boolean debuggable, boolean multiArch, boolean use32bitAbi,
-                boolean extractNativeLibs) {
+                boolean extractNativeLibs, boolean isolatedSplits) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
+            this.usesSplitName = usesSplitName;
             this.versionCode = versionCode;
             this.revisionCode = revisionCode;
             this.installLocation = installLocation;
@@ -435,6 +459,7 @@
             this.multiArch = multiArch;
             this.use32bitAbi = use32bitAbi;
             this.extractNativeLibs = extractNativeLibs;
+            this.isolatedSplits = isolatedSplits;
         }
     }
 
@@ -485,7 +510,7 @@
         return isApkPath(file.getName());
     }
 
-    private static boolean isApkPath(String path) {
+    public static boolean isApkPath(String path) {
         return path.endsWith(".apk");
     }
 
@@ -731,23 +756,23 @@
     public static PackageLite parsePackageLite(File packageFile, int flags)
             throws PackageParserException {
         if (packageFile.isDirectory()) {
-            return parseClusterPackageLite(packageFile, flags, null);
+            return parseClusterPackageLite(packageFile, flags);
         } else {
-            return parseMonolithicPackageLite(packageFile, flags, null);
+            return parseMonolithicPackageLite(packageFile, flags);
         }
     }
 
-    private static PackageLite parseMonolithicPackageLite(File packageFile, int flags,
-            AssetManager cachedAssetManager) throws PackageParserException {
+    private static PackageLite parseMonolithicPackageLite(File packageFile, int flags)
+            throws PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
-        final ApkLite baseApk = parseApkLite(packageFile, flags, cachedAssetManager);
+        final ApkLite baseApk = parseApkLite(packageFile, flags);
         final String packagePath = packageFile.getAbsolutePath();
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        return new PackageLite(packagePath, baseApk, null, null, null);
+        return new PackageLite(packagePath, baseApk, null, null, null, null);
     }
 
-    private static PackageLite parseClusterPackageLite(File packageDir, int flags,
-            AssetManager cachedAssetManager) throws PackageParserException {
+    private static PackageLite parseClusterPackageLite(File packageDir, int flags)
+            throws PackageParserException {
         final File[] files = packageDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
@@ -761,7 +786,7 @@
         final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
         for (File file : files) {
             if (isApkFile(file)) {
-                final ApkLite lite = parseApkLite(file, flags, cachedAssetManager);
+                final ApkLite lite = parseApkLite(file, flags);
 
                 // Assert that all package names and version codes are
                 // consistent with the first one we encounter.
@@ -801,10 +826,12 @@
         final int size = apks.size();
 
         String[] splitNames = null;
+        String[] usesSplitNames = null;
         String[] splitCodePaths = null;
         int[] splitRevisionCodes = null;
         if (size > 0) {
             splitNames = new String[size];
+            usesSplitNames = new String[size];
             splitCodePaths = new String[size];
             splitRevisionCodes = new int[size];
 
@@ -812,13 +839,15 @@
             Arrays.sort(splitNames, sSplitNameComparator);
 
             for (int i = 0; i < size; i++) {
-                splitCodePaths[i] = apks.get(splitNames[i]).codePath;
-                splitRevisionCodes[i] = apks.get(splitNames[i]).revisionCode;
+                final ApkLite apk = apks.get(splitNames[i]);
+                usesSplitNames[i] = apk.usesSplitName;
+                splitCodePaths[i] = apk.codePath;
+                splitRevisionCodes[i] = apk.revisionCode;
             }
         }
 
         final String codePath = packageDir.getAbsolutePath();
-        return new PackageLite(codePath, baseApk, splitNames, splitCodePaths,
+        return new PackageLite(codePath, baseApk, splitNames, usesSplitNames, splitCodePaths,
                 splitRevisionCodes);
     }
 
@@ -997,6 +1026,42 @@
         }
     }
 
+    private static SparseIntArray buildSplitDependencyTree(PackageLite pkg)
+            throws PackageParserException {
+        SparseIntArray splitDependencies = new SparseIntArray();
+        for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+            final String splitDependency = pkg.usesSplitNames[splitIdx];
+            if (splitDependency != null) {
+                final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
+                if (depIdx < 0) {
+                    throw new PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                            "Split '" + pkg.splitNames[splitIdx] + "' requires split '"
+                                    + splitDependency + "', which is missing.");
+                }
+                splitDependencies.put(splitIdx, depIdx);
+            }
+        }
+
+        // Verify that there are no cycles.
+        final BitSet bitset = new BitSet();
+        for (int i = 0; i < splitDependencies.size(); i++) {
+            int splitIdx = splitDependencies.keyAt(i);
+
+            bitset.clear();
+            while (splitIdx != -1) {
+                if (bitset.get(splitIdx)) {
+                    throw new PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                            "Cycle detected in split dependencies.");
+                }
+                bitset.set(splitIdx);
+                splitIdx = splitDependencies.get(splitIdx, -1);
+            }
+        }
+        return splitDependencies.size() != 0 ? splitDependencies : null;
+    }
+
     /**
      * Parse all APKs contained in the given directory, treating them as a
      * single package. This also performs sanity checking, such as requiring
@@ -1007,25 +1072,24 @@
      * must be done separately in {@link #collectCertificates(Package, int)}.
      */
     private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
-        final AssetManager assets = newConfiguredAssetManager();
-        final PackageLite lite = parseClusterPackageLite(packageDir, 0, assets);
-
+        final PackageLite lite = parseClusterPackageLite(packageDir, 0);
         if (mOnlyCoreApps && !lite.coreApp) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "Not a coreApp: " + packageDir);
         }
 
+        // Build the split dependency tree.
+        SparseIntArray splitDependencies = null;
+        final SplitAssetLoader assetLoader;
+        if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
+            splitDependencies = buildSplitDependencyTree(lite);
+            assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+        } else {
+            assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        }
+
         try {
-            // Load all splits into the AssetManager (base has already been loaded earlier)
-            // so that resources can be overriden when parsing the manifests.
-            loadApkIntoAssetManager(assets, lite.baseCodePath, flags);
-
-            if (!ArrayUtils.isEmpty(lite.splitCodePaths)) {
-                for (String path : lite.splitCodePaths) {
-                    loadApkIntoAssetManager(assets, path, flags);
-                }
-            }
-
+            final AssetManager assets = assetLoader.getBaseAssetManager();
             final File baseApk = new File(lite.baseCodePath);
             final Package pkg = parseBaseApk(baseApk, assets, flags);
             if (pkg == null) {
@@ -1040,9 +1104,12 @@
                 pkg.splitRevisionCodes = lite.splitRevisionCodes;
                 pkg.splitFlags = new int[num];
                 pkg.splitPrivateFlags = new int[num];
+                pkg.applicationInfo.splitNames = pkg.splitNames;
+                pkg.applicationInfo.splitDependencies = splitDependencies;
 
                 for (int i = 0; i < num; i++) {
-                    parseSplitApk(pkg, i, assets, flags);
+                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
+                    parseSplitApk(pkg, i, splitAssets, flags);
                 }
             }
 
@@ -1050,7 +1117,7 @@
             pkg.setUse32bitAbi(lite.use32bitAbi);
             return pkg;
         } finally {
-            IoUtils.closeQuietly(assets);
+            IoUtils.closeQuietly(assetLoader);
         }
     }
 
@@ -1067,7 +1134,7 @@
     @Deprecated
     public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
         final AssetManager assets = newConfiguredAssetManager();
-        final PackageLite lite = parseMonolithicPackageLite(apkFile, flags, assets);
+        final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
         if (mOnlyCoreApps) {
             if (!lite.coreApp) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
@@ -1161,7 +1228,7 @@
 
         final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
 
-        Resources res = null;
+        final Resources res;
         XmlResourceParser parser = null;
         try {
             res = new Resources(assets, mMetrics, null);
@@ -1218,7 +1285,7 @@
             }
 
             String tagName = parser.getName();
-            if (tagName.equals("application")) {
+            if (tagName.equals(TAG_APPLICATION)) {
                 if (foundApp) {
                     if (RIGID_PARSER) {
                         outError[0] = "<manifest> has more than one <application>";
@@ -1366,6 +1433,11 @@
                         "No APK Signature Scheme v2 signature in ephemeral package " + apkPath,
                         e);
                 }
+                // Static shared libraries must use only the V2 signing scheme
+                if (pkg.applicationInfo.isStaticSharedLibrary()) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                            "Static shared libs must use v2 signature scheme " + apkPath);
+                }
             } catch (Exception e) {
                 // APK Signature Scheme v2 signature was found but did not verify
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
@@ -1511,17 +1583,12 @@
      */
     public static ApkLite parseApkLite(File apkFile, int flags)
             throws PackageParserException {
-        return parseApkLite(apkFile, flags, null);
-    }
-
-    private static ApkLite parseApkLite(File apkFile, int flags,
-            @Nullable AssetManager cachedAssetManager) throws PackageParserException {
         final String apkPath = apkFile.getAbsolutePath();
 
         AssetManager assets = null;
         XmlResourceParser parser = null;
         try {
-            assets = cachedAssetManager == null ? newConfiguredAssetManager() : cachedAssetManager;
+            assets = newConfiguredAssetManager();
             int cookie = assets.addAssetPath(apkPath);
             if (cookie == 0) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
@@ -1531,7 +1598,6 @@
             final DisplayMetrics metrics = new DisplayMetrics();
             metrics.setToDefaults();
 
-            final Resources res = new Resources(assets, metrics, null);
             parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
 
             final Signature[] signatures;
@@ -1553,16 +1619,14 @@
             }
 
             final AttributeSet attrs = parser;
-            return parseApkLite(apkPath, res, parser, attrs, flags, signatures, certificates);
+            return parseApkLite(apkPath, parser, attrs, flags, signatures, certificates);
 
         } catch (XmlPullParserException | IOException | RuntimeException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                     "Failed to parse " + apkPath, e);
         } finally {
             IoUtils.closeQuietly(parser);
-            if (cachedAssetManager == null) {
-                IoUtils.closeQuietly(assets);
-            }
+            IoUtils.closeQuietly(assets);
         }
     }
 
@@ -1640,9 +1704,9 @@
                 (splitName != null) ? splitName.intern() : splitName);
     }
 
-    private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParser parser,
-            AttributeSet attrs, int flags, Signature[] signatures, Certificate[][] certificates)
-                    throws IOException, XmlPullParserException, PackageParserException {
+    private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
+            int flags, Signature[] signatures, Certificate[][] certificates)
+            throws IOException, XmlPullParserException, PackageParserException {
         final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
 
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
@@ -1653,6 +1717,8 @@
         boolean multiArch = false;
         boolean use32bitAbi = false;
         boolean extractNativeLibs = true;
+        boolean isolatedSplits = false;
+        String usesSplitName = null;
 
         for (int i = 0; i < attrs.getAttributeCount(); i++) {
             final String attr = attrs.getAttributeName(i);
@@ -1665,6 +1731,8 @@
                 revisionCode = attrs.getAttributeIntValue(i, 0);
             } else if (attr.equals("coreApp")) {
                 coreApp = attrs.getAttributeBooleanValue(i, false);
+            } else if (attr.equals("isolatedSplits")) {
+                isolatedSplits = attrs.getAttributeBooleanValue(i, false);
             }
         }
 
@@ -1679,14 +1747,16 @@
                 continue;
             }
 
-            if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) {
-                final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags);
+            if (parser.getDepth() != searchDepth) {
+                continue;
+            }
+
+            if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
+                final VerifierInfo verifier = parseVerifier(attrs);
                 if (verifier != null) {
                     verifiers.add(verifier);
                 }
-            }
-
-            if (parser.getDepth() == searchDepth && "application".equals(parser.getName())) {
+            } else if (TAG_APPLICATION.equals(parser.getName())) {
                 for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                     final String attr = attrs.getAttributeName(i);
                     if ("debuggable".equals(attr)) {
@@ -1702,12 +1772,25 @@
                         extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
                     }
                 }
+            } else if (TAG_USES_SPLIT.equals(parser.getName())) {
+                if (usesSplitName != null) {
+                    Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
+                    continue;
+                }
+
+                usesSplitName = attrs.getAttributeValue(ANDROID_RESOURCES, "name");
+                if (usesSplitName == null) {
+                    throw new PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "<uses-split> tag requires 'android:name' attribute");
+                }
             }
         }
 
-        return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
-                revisionCode, installLocation, verifiers, signatures, certificates, coreApp,
-                debuggable, multiArch, use32bitAbi, extractNativeLibs);
+        return new ApkLite(codePath, packageSplit.first, packageSplit.second, usesSplitName,
+                versionCode, revisionCode, installLocation, verifiers, signatures,
+                certificates, coreApp, debuggable, multiArch, use32bitAbi, extractNativeLibs,
+                isolatedSplits);
     }
 
     /**
@@ -1928,6 +2011,10 @@
             pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_EPHEMERAL;
         }
 
+        if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false)) {
+            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+        }
+
         // Resource boolean are -1, so 1 means we don't know the value.
         int supportsSmallScreens = 1;
         int supportsNormalScreens = 1;
@@ -2390,7 +2477,7 @@
         // cannot be windowed / resized. Note that an SDK version of 0 is common for
         // pre-Doughnut applications.
         if (pkg.applicationInfo.usesCompatibilityMode()) {
-            adjustPackageToBeUnresizeable(pkg);
+            adjustPackageToBeUnresizeableAndUnpipable(pkg);
         }
         return pkg;
     }
@@ -2401,9 +2488,10 @@
      *
      * @param pkg The package which needs to be marked as unresizable.
      */
-    private void adjustPackageToBeUnresizeable(Package pkg) {
+    private void adjustPackageToBeUnresizeableAndUnpipable(Package pkg) {
         for (Activity a : pkg.activities) {
             a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+            a.info.flags &= ~FLAG_SUPPORTS_PICTURE_IN_PICTURE;
         }
     }
 
@@ -2561,6 +2649,52 @@
         return fi;
     }
 
+    private boolean parseUsesStaticLibrary(Package pkg, Resources res, XmlResourceParser parser,
+            String[] outError) throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifestUsesStaticLibrary);
+
+        // Note: don't allow this value to be a reference to a resource that may change.
+        String lname = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
+        final int version = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
+        String certSha256 = sa.getNonResourceString(com.android.internal.R.styleable
+                .AndroidManifestUsesStaticLibrary_certDigest);
+        sa.recycle();
+
+        // Since an APK providing a static shared lib can only provide the lib - fail if malformed
+        if (lname == null || version < 0 || certSha256 == null) {
+            outError[0] = "Bad uses-static-library declaration name: " + lname + " version: "
+                    + version + " certDigest" + certSha256;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            XmlUtils.skipCurrentTag(parser);
+            return false;
+        }
+
+        // Can depend only on one version of the same library
+        if (pkg.usesStaticLibraries != null && pkg.usesStaticLibraries.contains(lname)) {
+            outError[0] = "Depending on multiple versions of static library " + lname;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            XmlUtils.skipCurrentTag(parser);
+            return false;
+        }
+
+        lname = lname.intern();
+        // We allow ":" delimiters in the SHA declaration as this is the format
+        // emitted by the certtool making it easy for developers to copy/paste.
+        certSha256 = certSha256.replace(":", "").toLowerCase();
+        pkg.usesStaticLibraries = ArrayUtils.add(pkg.usesStaticLibraries, lname);
+        pkg.usesStaticLibrariesVersions = ArrayUtils.appendInt(
+                pkg.usesStaticLibrariesVersions, version);
+        pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String.class,
+                pkg.usesStaticLibrariesCertDigests, certSha256);
+
+        XmlUtils.skipCurrentTag(parser);
+
+        return true;
+    }
+
     private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
             throws XmlPullParserException, IOException {
         TypedArray sa = res.obtainAttributes(parser,
@@ -3409,6 +3543,47 @@
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
                 }
+            } else if (tagName.equals("static-library")) {
+                sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary);
+
+                // Note: don't allow this value to be a reference to a resource
+                // that may change.
+                final String lname = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary_name);
+                final int version = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary_version, -1);
+
+                sa.recycle();
+
+                // Since the app canot run without a static lib - fail if malformed
+                if (lname == null || version < 0) {
+                    outError[0] = "Bad static-library declaration name: " + lname
+                            + " version: " + version;
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    XmlUtils.skipCurrentTag(parser);
+                    return false;
+                }
+
+                if (owner.mSharedUserId != null) {
+                    outError[0] = "sharedUserId not allowed in static shared library";
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
+                    XmlUtils.skipCurrentTag(parser);
+                    return false;
+                }
+
+                if (owner.staticSharedLibName != null) {
+                    outError[0] = "Multiple static-shared libs for package " + pkgName;
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                    XmlUtils.skipCurrentTag(parser);
+                    return false;
+                }
+
+                owner.staticSharedLibName = lname.intern();
+                owner.staticSharedLibVersion = version;
+                ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY;
+
+                XmlUtils.skipCurrentTag(parser);
 
             } else if (tagName.equals("library")) {
                 sa = res.obtainAttributes(parser,
@@ -3424,12 +3599,18 @@
                 if (lname != null) {
                     lname = lname.intern();
                     if (!ArrayUtils.contains(owner.libraryNames, lname)) {
-                        owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);
+                        owner.libraryNames = ArrayUtils.add(
+                                owner.libraryNames, lname);
                     }
                 }
 
                 XmlUtils.skipCurrentTag(parser);
 
+            } else if (tagName.equals("uses-static-library")) {
+                if (!parseUsesStaticLibrary(owner, res, parser, outError)) {
+                    return false;
+                }
+
             } else if (tagName.equals("uses-library")) {
                 sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestUsesLibrary);
@@ -3551,6 +3732,8 @@
                 continue;
             }
 
+            ComponentInfo parsedComponent = null;
+
             String tagName = parser.getName();
             if (tagName.equals("activity")) {
                 Activity a = parseActivity(owner, res, parser, flags, outError, false,
@@ -3561,6 +3744,7 @@
                 }
 
                 owner.activities.add(a);
+                parsedComponent = a.info;
 
             } else if (tagName.equals("receiver")) {
                 Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
@@ -3570,6 +3754,7 @@
                 }
 
                 owner.receivers.add(a);
+                parsedComponent = a.info;
 
             } else if (tagName.equals("service")) {
                 Service s = parseService(owner, res, parser, flags, outError);
@@ -3579,6 +3764,7 @@
                 }
 
                 owner.services.add(s);
+                parsedComponent = s.info;
 
             } else if (tagName.equals("provider")) {
                 Provider p = parseProvider(owner, res, parser, flags, outError);
@@ -3588,6 +3774,7 @@
                 }
 
                 owner.providers.add(p);
+                parsedComponent = p.info;
 
             } else if (tagName.equals("activity-alias")) {
                 Activity a = parseActivityAlias(owner, res, parser, flags, outError);
@@ -3597,6 +3784,7 @@
                 }
 
                 owner.activities.add(a);
+                parsedComponent = a.info;
 
             } else if (parser.getName().equals("meta-data")) {
                 // note: application meta-data is stored off to the side, so it can
@@ -3608,6 +3796,11 @@
                     return false;
                 }
 
+            } else if (tagName.equals("uses-static-library")) {
+                if (!parseUsesStaticLibrary(owner, res, parser, outError)) {
+                    return false;
+                }
+
             } else if (tagName.equals("uses-library")) {
                 sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestUsesLibrary);
@@ -3658,6 +3851,14 @@
                     return false;
                 }
             }
+
+            if (parsedComponent != null && parsedComponent.splitName == null) {
+                // If the loaded component did not specify a split, inherit the split name
+                // based on the split it is defined in.
+                // This is used to later load the correct split when starting this
+                // component.
+                parsedComponent.splitName = owner.splitNames[splitIndex];
+            }
         }
 
         return true;
@@ -3789,6 +3990,9 @@
         a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
                 owner.applicationInfo.taskAffinity, str, outError);
 
+        a.info.splitName =
+                sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_splitName, 0);
+
         a.info.flags = 0;
         if (sa.getBoolean(
                 R.styleable.AndroidManifestActivity_multiprocess, false)) {
@@ -3855,7 +4059,9 @@
             a.info.maxRecents = sa.getInt(
                     R.styleable.AndroidManifestActivity_maxRecents,
                     ActivityManager.getDefaultAppRecentsLimitStatic());
-            a.info.configChanges = sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0);
+            a.info.configChanges = getActivityConfigChanges(
+                    sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
+                    sa.getInt(R.styleable.AndroidManifestActivity_restartOnConfigChanges, 0));
             a.info.softInputMode = sa.getInt(
                     R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
 
@@ -3885,6 +4091,11 @@
 
             setActivityResizeMode(a.info, sa, owner);
 
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
+                    false)) {
+                a.info.flags |= FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+            }
+
             if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
                 a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
             }
@@ -3905,6 +4116,9 @@
 
             a.info.rotationAnimation =
                 sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, ROTATION_ANIMATION_ROTATE);
+
+            a.info.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode,
+                    ActivityInfo.COLOR_MODE_DEFAULT);
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
@@ -3931,10 +4145,10 @@
         }
 
         final boolean hasVisibleToEphemeral =
-                sa.hasValue(R.styleable.AndroidManifestActivity_visibleToEphemeral);
+                sa.hasValue(R.styleable.AndroidManifestActivity_visibleToInstantApps);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
-                || sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToEphemeral, false);
+                || sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
         if (visibleToEphemeral) {
             a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
         }
@@ -4046,16 +4260,13 @@
     private void setActivityResizeMode(ActivityInfo aInfo, TypedArray sa, Package owner) {
         final boolean appExplicitDefault = (owner.applicationInfo.privateFlags
                 & PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_EXPLICITLY_SET) != 0;
-        final boolean supportsPip =
-                sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture, false);
 
         if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
                 || appExplicitDefault) {
             // Activity or app explicitly set if it is resizeable or not;
             if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
                     appExplicitDefault)) {
-                aInfo.resizeMode =
-                        supportsPip ? RESIZE_MODE_RESIZEABLE_AND_PIPABLE : RESIZE_MODE_RESIZEABLE;
+                aInfo.resizeMode = RESIZE_MODE_RESIZEABLE;
             } else {
                 aInfo.resizeMode = RESIZE_MODE_UNRESIZEABLE;
             }
@@ -4083,6 +4294,17 @@
         }
     }
 
+    /**
+     * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
+     * @param restartOnConfigChanges The bit mask restartOnConfigChanges fetched from
+     *                               AndroidManifest.xml.
+     * @hide Exposed for unit testing only.
+     */
+    @TestApi
+    public static int getActivityConfigChanges(int configChanges, int restartOnConfigChanges) {
+        return configChanges | ((~restartOnConfigChanges) & RESTART_ON_CONFIG_CHANGES_MASK);
+    }
+
     private void parseLayout(Resources res, AttributeSet attrs, Activity a) {
         TypedArray sw = res.obtainAttributes(attrs,
                 com.android.internal.R.styleable.AndroidManifestLayout);
@@ -4244,7 +4466,7 @@
             }
         }
 
-        // TODO add visibleToInstantApp attribute to activity alias
+        // TODO add visibleToInstantApps attribute to activity alias
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0);
@@ -4397,6 +4619,9 @@
                 com.android.internal.R.styleable.AndroidManifestProvider_initOrder,
                 0);
 
+        p.info.splitName =
+                sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_splitName, 0);
+
         p.info.flags = 0;
 
         if (sa.getBoolean(
@@ -4420,10 +4645,10 @@
         }
 
         final boolean hasVisibleToEphemeral =
-                sa.hasValue(R.styleable.AndroidManifestProvider_visibleToEphemeral);
+                sa.hasValue(R.styleable.AndroidManifestProvider_visibleToInstantApps);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
-                || sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToEphemeral, false);
+                || sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
         if (visibleToEphemeral) {
             p.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
         }
@@ -4693,6 +4918,9 @@
             s.info.permission = str.length() > 0 ? str.toString().intern() : null;
         }
 
+        s.info.splitName =
+                sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0);
+
         s.info.flags = 0;
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestService_stopWithTask,
@@ -4731,10 +4959,10 @@
         }
 
         final boolean hasVisibleToEphemeral =
-                sa.hasValue(R.styleable.AndroidManifestService_visibleToEphemeral);
+                sa.hasValue(R.styleable.AndroidManifestService_visibleToInstantApps);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
-                || sa.getBoolean(R.styleable.AndroidManifestService_visibleToEphemeral, false);
+                || sa.getBoolean(R.styleable.AndroidManifestService_visibleToInstantApps, false);
         if (visibleToEphemeral) {
             s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
         }
@@ -4901,18 +5129,23 @@
         return data;
     }
 
-    private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser,
-            AttributeSet attrs, int flags) {
-        final TypedArray sa = res.obtainAttributes(attrs,
-                com.android.internal.R.styleable.AndroidManifestPackageVerifier);
+    private static VerifierInfo parseVerifier(AttributeSet attrs) {
+        String packageName = null;
+        String encodedPublicKey = null;
 
-        final String packageName = sa.getNonResourceString(
-                com.android.internal.R.styleable.AndroidManifestPackageVerifier_name);
+        final int attrCount = attrs.getAttributeCount();
+        for (int i = 0; i < attrCount; i++) {
+            final int attrResId = attrs.getAttributeNameResource(i);
+            switch (attrResId) {
+                case com.android.internal.R.attr.name:
+                    packageName = attrs.getAttributeValue(i);
+                    break;
 
-        final String encodedPublicKey = sa.getNonResourceString(
-                com.android.internal.R.styleable.AndroidManifestPackageVerifier_publicKey);
-
-        sa.recycle();
+                case com.android.internal.R.attr.publicKey:
+                    encodedPublicKey = attrs.getAttributeValue(i);
+                    break;
+            }
+        }
 
         if (packageName == null || packageName.length() == 0) {
             Slog.i(TAG, "verifier package name was null; skipping");
@@ -5167,6 +5400,10 @@
 
         public String packageName;
 
+        // The package name declared in the manifest as the package can be
+        // renamed, for example static shared libs use synthetic package names.
+        public String manifestPackageName;
+
         /** Names of any split APKs, ordered by parsed splitName */
         public String[] splitNames;
 
@@ -5221,8 +5458,13 @@
         public Package parentPackage;
         public ArrayList<Package> childPackages;
 
+        public String staticSharedLibName = null;
+        public int staticSharedLibVersion = 0;
         public ArrayList<String> libraryNames = null;
         public ArrayList<String> usesLibraries = null;
+        public ArrayList<String> usesStaticLibraries = null;
+        public int[] usesStaticLibrariesVersions = null;
+        public String[] usesStaticLibrariesCertDigests = null;
         public ArrayList<String> usesOptionalLibraries = null;
         public String[] usesLibraryFiles = null;
 
@@ -5321,6 +5563,7 @@
 
         public Package(String packageName) {
             this.packageName = packageName;
+            this.manifestPackageName = packageName;
             applicationInfo.packageName = packageName;
             applicationInfo.uid = -1;
         }
@@ -5639,6 +5882,7 @@
             final ClassLoader boot = Object.class.getClassLoader();
 
             packageName = dest.readString();
+            manifestPackageName = dest.readString();
             splitNames = dest.readStringArray();
             volumeUuid = dest.readString();
             codePath = dest.readString();
@@ -5679,11 +5923,23 @@
                 childPackages = null;
             }
 
+            staticSharedLibName = dest.readString();
+            staticSharedLibVersion = dest.readInt();
             libraryNames = dest.createStringArrayList();
             usesLibraries = dest.createStringArrayList();
             usesOptionalLibraries = dest.createStringArrayList();
             usesLibraryFiles = dest.readStringArray();
 
+            final int libCount = dest.readInt();
+            if (libCount > 0) {
+                usesStaticLibraries = new ArrayList<>(libCount);
+                dest.readStringList(usesStaticLibraries);
+                usesStaticLibrariesVersions = new int[libCount];
+                dest.readIntArray(usesStaticLibrariesVersions);
+                usesStaticLibrariesCertDigests = new String[libCount];
+                dest.readStringArray(usesStaticLibrariesCertDigests);
+            }
+
             preferredActivityFilters = new ArrayList<>();
             dest.readParcelableList(preferredActivityFilters, boot);
             if (preferredActivityFilters.size() == 0) {
@@ -5769,6 +6025,7 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeString(packageName);
+            dest.writeString(manifestPackageName);
             dest.writeStringArray(splitNames);
             dest.writeString(volumeUuid);
             dest.writeString(codePath);
@@ -5793,11 +6050,22 @@
             dest.writeStringList(protectedBroadcasts);
             dest.writeParcelable(parentPackage, flags);
             dest.writeParcelableList(childPackages, flags);
+            dest.writeString(staticSharedLibName);
+            dest.writeInt(staticSharedLibVersion);
             dest.writeStringList(libraryNames);
             dest.writeStringList(usesLibraries);
             dest.writeStringList(usesOptionalLibraries);
             dest.writeStringArray(usesLibraryFiles);
 
+            if (ArrayUtils.isEmpty(usesStaticLibraries)) {
+                dest.writeInt(-1);
+            } else {
+                dest.writeInt(usesStaticLibraries.size());
+                dest.writeStringList(usesStaticLibraries);
+                dest.writeIntArray(usesStaticLibrariesVersions);
+                dest.writeStringArray(usesStaticLibrariesCertDigests);
+            }
+
             dest.writeParcelableList(preferredActivityFilters, flags);
 
             dest.writeStringList(mOriginalPackages);
@@ -6219,6 +6487,9 @@
                 && p.usesLibraryFiles != null) {
             return true;
         }
+        if (p.staticSharedLibName != null) {
+            return true;
+        }
         return false;
     }
 
diff --git a/core/java/android/content/pm/SharedLibraryInfo.aidl b/core/java/android/content/pm/SharedLibraryInfo.aidl
new file mode 100644
index 0000000..56d7c832
--- /dev/null
+++ b/core/java/android/content/pm/SharedLibraryInfo.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+parcelable SharedLibraryInfo;
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
new file mode 100644
index 0000000..d79deb2
--- /dev/null
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class provides information for a shared library. There are
+ * three types of shared libraries: builtin - non-updatable part of
+ * the OS; dynamic - updatable backwards-compatible dynamically linked;
+ * static - updatable non backwards-compatible emulating static linking.
+ */
+public final class SharedLibraryInfo implements Parcelable {
+    /**
+     * Shared library type: this library is a part of the OS
+     * and cannot be updated or uninstalled.
+     * @hide
+     */
+    public static final int TYPE_BUILTIN = 0x1<<0;
+
+    /**
+     * Shared library type: this library is backwards-compatible, can
+     * be updated, and updates can be uninstalled. Clients link against
+     * the latest version of the library.
+     * @hide
+     */
+    public static final int TYPE_DYNAMIC = 0x1<<1;
+
+    /**
+     * Shared library type: this library is <strong>not</strong> backwards
+     * -compatible, can be updated and updates can be uninstalled. Clients
+     * link against a specific version of the library.
+     * @hide
+     */
+    public static final int TYPE_STATIC = 0x1<<2;
+
+    /**
+     * Constant for referring to an undefined version.
+     */
+    public static final int VERSION_UNDEFINED = -1;
+
+    private final String mName;
+    private final int mVersion;
+    private final int mType;
+    private final VersionedPackage mDeclaringPackage;
+    private final List<VersionedPackage> mDependentPackages;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param name The lib name.
+     * @param version The lib version if not builtin.
+     * @param type The lib type.
+     * @param declaringPackage The package that declares the library.
+     * @param dependentPackages The packages that depend on the library.
+     *
+     * @hide
+     */
+    public SharedLibraryInfo(String name, int version, int type,
+            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages) {
+        mName = name;
+        mVersion = version;
+        mType = type;
+        mDeclaringPackage = declaringPackage;
+        mDependentPackages = dependentPackages;
+    }
+
+    private SharedLibraryInfo(Parcel parcel) {
+        this(parcel.readString(), parcel.readInt(), parcel.readInt(),
+                parcel.readParcelable(null), parcel.readArrayList(null));
+    }
+
+    /** @hide */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Gets the library name.
+     *
+     * @return The name.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Gets the version of the library. For {@link #isStatic()}  static} libraries
+     * this is the declared version and for {@link #isDynamic()} dynamic} and
+     * {@link #isBuiltin()} builtin} it is {@link #VERSION_UNDEFINED} as these
+     * are not versioned.
+     *
+     * @return The version.
+     */
+    public @IntRange(from = -1) int getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * @return whether this library is builtin which means that it
+     * is a part of the OS and cannot be updated or uninstalled.
+     */
+    public boolean isBuiltin() {
+        return mType == TYPE_BUILTIN;
+    }
+
+    /**
+     * @return whether this library is dynamic which means that it
+     * is backwards-compatible, can be updated, and updates can be
+     * uninstalled. Clients link against the latest version of the
+     * library.
+     */
+    public boolean isDynamic() {
+        return mType == TYPE_DYNAMIC;
+    }
+
+    /**
+     * @return whether this library is dynamic which means that it
+     * is <strong>not</strong> backwards-compatible, can be updated
+     * and updates can be uninstalled. Clients link against a specific
+     * version of the library.
+     */
+    public boolean isStatic() {
+        return mType == TYPE_STATIC;
+    }
+
+    /**
+     * Gets the package that declares the library.
+     *
+     * @return The package declaring the library.
+     */
+    public @NonNull VersionedPackage getDeclaringPackage() {
+        return mDeclaringPackage;
+    }
+
+    /**
+     * Gets the packages that depend on the library.
+     *
+     * @return The dependent packages.
+     */
+    public @NonNull List<VersionedPackage> getDependentPackages() {
+        if (mDependentPackages == null) {
+            return Collections.emptyList();
+        }
+        return mDependentPackages;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "SharedLibraryInfo[name:" + mName + ", type:" + typeToString(mType)
+                + ", version:" + mVersion + (!getDependentPackages().isEmpty()
+                ? " has dependents" : "");
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mName);
+        parcel.writeInt(mVersion);
+        parcel.writeInt(mType);
+        parcel.writeParcelable(mDeclaringPackage, flags);
+        parcel.writeList(mDependentPackages);
+    }
+
+    private static String typeToString(int type) {
+        switch (type) {
+            case TYPE_BUILTIN: {
+                return "builtin";
+            }
+            case TYPE_DYNAMIC: {
+                return "dynamic";
+            }
+            case TYPE_STATIC: {
+                return "static";
+            }
+            default: {
+                return "unknown";
+            }
+        }
+    }
+
+    public static final Parcelable.Creator<SharedLibraryInfo> CREATOR =
+            new Parcelable.Creator<SharedLibraryInfo>() {
+        public SharedLibraryInfo createFromParcel(Parcel source) {
+            return new SharedLibraryInfo(source);
+        }
+
+        public SharedLibraryInfo[] newArray(int size) {
+            return new SharedLibraryInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 3853400..805054f 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -881,6 +881,31 @@
     }
 
     /**
+     * Returns an Intent which can be used by the default launcher to pin {@param shortcut}.
+     * This should be used by an Activity to set result in response to
+     * {@link Intent#ACTION_CREATE_SHORTCUT}.
+     *
+     * @param shortcut New shortcut to pin.  If an app wants to pin an existing (either dynamic
+     *     or manifest) shortcut, then it only needs to have an ID, and other fields don't have to
+     *     be set, in which case, the target shortcut must be enabled.
+     *     If it's a new shortcut, all the mandatory fields, such as a short label, must be
+     *     set.
+     * @return The intent that should be set as the result for the calling activity or null.
+     *
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     *
+     * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
+     */
+    public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) {
+        try {
+            return mService.createShortcutResultIntent(mContext.getPackageName(), shortcut,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called internally when an app is considered to have come to the foreground
      * even when technically it's not.  This method resets the throttling for this package.
      * For example, when the user sends an "inline reply" on a notification, the system UI will
diff --git a/core/java/android/content/pm/VersionedPackage.aidl b/core/java/android/content/pm/VersionedPackage.aidl
new file mode 100644
index 0000000..43412a4
--- /dev/null
+++ b/core/java/android/content/pm/VersionedPackage.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+parcelable VersionedPackage;
diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java
new file mode 100644
index 0000000..83e7815
--- /dev/null
+++ b/core/java/android/content/pm/VersionedPackage.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Encapsulates a package and its version code.
+ */
+public final class VersionedPackage implements Parcelable {
+    private final String mPackageName;
+    private final long mVersionCode;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntRange(from = PackageManager.VERSION_CODE_HIGHEST)
+    public @interface VersionCode{}
+
+    /**
+     * Creates a new instance. Use {@link PackageManager#VERSION_CODE_HIGHEST}
+     * to refer to the highest version code of this package.
+     * @param packageName The package name.
+     * @param versionCode The version code.
+     */
+    public VersionedPackage(@NonNull String packageName,
+            @VersionCode int versionCode) {
+        mPackageName = packageName;
+        mVersionCode = versionCode;
+    }
+
+    private VersionedPackage(Parcel parcel) {
+        mPackageName = parcel.readString();
+        mVersionCode = parcel.readLong();
+    }
+
+    /**
+     * Gets the package name.
+     *
+     * @return The package name.
+     */
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Gets the version code.
+     *
+     * @return The version code.
+     */
+    public @VersionCode long getVersionCode() {
+        return mVersionCode;
+    }
+
+    @Override
+    public String toString() {
+        return "VersionedPackage[" + mPackageName + "/" + mVersionCode + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mPackageName);
+        parcel.writeLong(mVersionCode);
+    }
+
+    public static final Creator<VersionedPackage> CREATOR = new Creator<VersionedPackage>() {
+        @Override
+        public VersionedPackage createFromParcel(Parcel source) {
+            return new VersionedPackage(source);
+        }
+
+        @Override
+        public VersionedPackage[] newArray(int size) {
+            return new VersionedPackage[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
new file mode 100644
index 0000000..5a9966d
--- /dev/null
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.split;
+
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+
+import android.content.pm.PackageParser;
+import android.content.res.AssetManager;
+import android.os.Build;
+
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+/**
+ * Loads the base and split APKs into a single AssetManager.
+ * @hide
+ */
+public class DefaultSplitAssetLoader implements SplitAssetLoader {
+    private final String mBaseCodePath;
+    private final String[] mSplitCodePaths;
+    private final int mFlags;
+
+    private AssetManager mCachedAssetManager;
+
+    public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) {
+        mBaseCodePath = pkg.baseCodePath;
+        mSplitCodePaths = pkg.splitCodePaths;
+        mFlags = flags;
+    }
+
+    private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
+            throws PackageParser.PackageParserException {
+        if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) {
+            throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                    "Invalid package file: " + apkPath);
+        }
+
+        if (assets.addAssetPath(apkPath) == 0) {
+            throw new PackageParser.PackageParserException(
+                    INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Failed adding asset path: " + apkPath);
+        }
+    }
+
+    @Override
+    public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+        if (mCachedAssetManager != null) {
+            return mCachedAssetManager;
+        }
+
+        AssetManager assets = new AssetManager();
+        try {
+            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    Build.VERSION.RESOURCES_SDK_INT);
+            loadApkIntoAssetManager(assets, mBaseCodePath, mFlags);
+
+            if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+                for (String apkPath : mSplitCodePaths) {
+                    loadApkIntoAssetManager(assets, apkPath, mFlags);
+                }
+            }
+
+            mCachedAssetManager = assets;
+            assets = null;
+            return mCachedAssetManager;
+        } finally {
+            if (assets != null) {
+                IoUtils.closeQuietly(assets);
+            }
+        }
+    }
+
+    @Override
+    public AssetManager getSplitAssetManager(int splitIdx)
+            throws PackageParser.PackageParserException {
+        return getBaseAssetManager();
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (mCachedAssetManager != null) {
+            IoUtils.closeQuietly(mCachedAssetManager);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
new file mode 100644
index 0000000..3ad45b6
--- /dev/null
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.split;
+
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+
+import android.content.pm.PackageParser;
+import android.content.res.AssetManager;
+import android.os.Build;
+import android.util.SparseIntArray;
+
+import libcore.io.IoUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation
+ * is to be used when an application opts-in to isolated split loading.
+ * @hide
+ */
+public class SplitAssetDependencyLoader
+        extends SplitDependencyLoaderHelper<PackageParser.PackageParserException>
+        implements SplitAssetLoader {
+    private static final int BASE_ASSET_PATH_IDX = -1;
+    private final String mBasePath;
+    private final String[] mSplitNames;
+    private final String[] mSplitPaths;
+    private final int mFlags;
+
+    private String[] mCachedBasePaths;
+    private AssetManager mCachedBaseAssetManager;
+
+    private String[][] mCachedSplitPaths;
+    private AssetManager[] mCachedAssetManagers;
+
+    public SplitAssetDependencyLoader(PackageParser.PackageLite pkg, SparseIntArray dependencies,
+            int flags) {
+        super(dependencies);
+        mBasePath = pkg.baseCodePath;
+        mSplitNames = pkg.splitNames;
+        mSplitPaths = pkg.splitCodePaths;
+        mFlags = flags;
+        mCachedBasePaths = null;
+        mCachedBaseAssetManager = null;
+        mCachedSplitPaths = new String[mSplitNames.length][];
+        mCachedAssetManagers = new AssetManager[mSplitNames.length];
+    }
+
+    @Override
+    protected boolean isSplitCached(int splitIdx) {
+        if (splitIdx != -1) {
+            return mCachedAssetManagers[splitIdx] != null;
+        }
+        return mCachedBaseAssetManager != null;
+    }
+
+    // Adds all non-code configuration splits for this split name. The split name is expected
+    // to represent a feature split.
+    private void addAllConfigSplits(String splitName, ArrayList<String> outAssetPaths) {
+        for (int i = 0; i < mSplitNames.length; i++) {
+            if (isConfigurationSplitOf(mSplitNames[i], splitName)) {
+                outAssetPaths.add(mSplitPaths[i]);
+            }
+        }
+    }
+
+    private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
+            throws PackageParser.PackageParserException {
+        final AssetManager assets = new AssetManager();
+        try {
+            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    Build.VERSION.RESOURCES_SDK_INT);
+
+            for (String assetPath : assetPaths) {
+                if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 &&
+                        !PackageParser.isApkPath(assetPath)) {
+                    throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                            "Invalid package file: " + assetPath);
+                }
+
+                if (assets.addAssetPath(assetPath) == 0) {
+                    throw new PackageParser.PackageParserException(
+                            INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                            "Failed adding asset path: " + assetPath);
+                }
+            }
+            return assets;
+        } catch (Throwable e) {
+            IoUtils.closeQuietly(assets);
+            throw e;
+        }
+    }
+
+    @Override
+    protected void constructSplit(int splitIdx, int parentSplitIdx) throws
+            PackageParser.PackageParserException {
+        final ArrayList<String> assetPaths = new ArrayList<>();
+        if (splitIdx == BASE_ASSET_PATH_IDX) {
+            assetPaths.add(mBasePath);
+            addAllConfigSplits(null, assetPaths);
+            mCachedBasePaths = assetPaths.toArray(new String[assetPaths.size()]);
+            mCachedBaseAssetManager = createAssetManagerWithPaths(mCachedBasePaths, mFlags);
+            return;
+        }
+
+        if (parentSplitIdx == BASE_ASSET_PATH_IDX) {
+            Collections.addAll(assetPaths, mCachedBasePaths);
+        } else {
+            Collections.addAll(assetPaths, mCachedSplitPaths[parentSplitIdx]);
+        }
+
+        assetPaths.add(mSplitPaths[splitIdx]);
+        addAllConfigSplits(mSplitNames[splitIdx], assetPaths);
+        mCachedSplitPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
+        mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedSplitPaths[splitIdx],
+                mFlags);
+    }
+
+    @Override
+    public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+        loadDependenciesForSplit(BASE_ASSET_PATH_IDX);
+        return mCachedBaseAssetManager;
+    }
+
+    @Override
+    public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
+        loadDependenciesForSplit(idx);
+        return mCachedAssetManagers[idx];
+    }
+
+    @Override
+    public void close() throws Exception {
+        IoUtils.closeQuietly(mCachedBaseAssetManager);
+        for (AssetManager assets : mCachedAssetManagers) {
+            IoUtils.closeQuietly(assets);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/split/SplitAssetLoader.java b/core/java/android/content/pm/split/SplitAssetLoader.java
new file mode 100644
index 0000000..108fb95
--- /dev/null
+++ b/core/java/android/content/pm/split/SplitAssetLoader.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.split;
+
+import android.content.pm.PackageParser;
+import android.content.res.AssetManager;
+
+/**
+ * Simple interface for loading base Assets and Splits. Used by PackageParser when parsing
+ * split APKs.
+ *
+ * @hide
+ */
+public interface SplitAssetLoader extends AutoCloseable {
+    AssetManager getBaseAssetManager() throws PackageParser.PackageParserException;
+    AssetManager getSplitAssetManager(int splitIdx) throws PackageParser.PackageParserException;
+}
diff --git a/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java b/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java
new file mode 100644
index 0000000..b493480
--- /dev/null
+++ b/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.split;
+
+import android.annotation.Nullable;
+import android.util.IntArray;
+import android.util.SparseIntArray;
+
+/**
+ * A helper class that implements the dependency tree traversal for splits. Callbacks
+ * are implemented by subclasses to notify whether a split has already been constructed
+ * and is cached, and to actually create the split requested.
+ *
+ * This helper is meant to be subclassed so as to reduce the number of allocations
+ * needed to make use of it.
+ *
+ * All inputs and outputs are assumed to be indices into an array of splits.
+ *
+ * @hide
+ */
+public abstract class SplitDependencyLoaderHelper<E extends Exception> {
+    @Nullable private final SparseIntArray mDependencies;
+
+    /**
+     * Construct a new SplitDependencyLoaderHelper. Meant to be called from the
+     * subclass constructor.
+     * @param dependencies The dependency tree of splits. Can be null, which leads to
+     *                     just the implicit dependency of all splits on the base.
+     */
+    protected SplitDependencyLoaderHelper(@Nullable SparseIntArray dependencies) {
+        mDependencies = dependencies;
+    }
+
+    /**
+     * Traverses the dependency tree and constructs any splits that are not already
+     * cached. This routine short-circuits and skips the creation of splits closer to the
+     * root if they are cached, as reported by the subclass implementation of
+     * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass
+     * implementation of {@link #constructSplit(int, int)}.
+     * @param splitIdx The index of the split to load. Can be -1, which represents the
+     *                 base Application.
+     */
+    protected void loadDependenciesForSplit(int splitIdx) throws E {
+        // Quick check before any allocations are done.
+        if (isSplitCached(splitIdx)) {
+            return;
+        }
+
+        final IntArray linearDependencies = new IntArray();
+        linearDependencies.add(splitIdx);
+
+        // Collect all the dependencies that need to be constructed.
+        // They will be listed from leaf to root.
+        while (splitIdx >= 0) {
+            splitIdx = mDependencies != null ? mDependencies.get(splitIdx, -1) : -1;
+            if (isSplitCached(splitIdx)) {
+                break;
+            }
+            linearDependencies.add(splitIdx);
+        }
+
+        // Visit each index, from right to left (root to leaf).
+        int parentIdx = splitIdx;
+        for (int i = linearDependencies.size() - 1; i >= 0; i--) {
+            final int idx = linearDependencies.get(i);
+            constructSplit(idx, parentIdx);
+            parentIdx = idx;
+        }
+    }
+
+    /**
+     * Subclass to report whether the split at `splitIdx` is cached and need not be constructed.
+     * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached.
+     * @param splitIdx The index of the split to check for in the cache.
+     * @return true if the split is cached and does not need to be constructed.
+     */
+    protected abstract boolean isSplitCached(int splitIdx);
+
+    /**
+     * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`.
+     * The result is expected to be cached by the subclass in its own structures.
+     * @param splitIdx The index of the split to construct. Can be -1, which represents the
+     *                 base Application.
+     * @param parentSplitIdx The index of the parent split. Can be -1, which represents the
+     *                       base Application.
+     * @throws E
+     */
+    protected abstract void constructSplit(int splitIdx, int parentSplitIdx) throws E;
+
+    /**
+     * Returns true if `splitName` represents a Configuration split of `featureSplitName`.
+     *
+     * A Configuration split's name is prefixed with the associated Feature split's name
+     * or the empty string if the split is for the base Application APK. It is then followed by the
+     * dollar sign character "$" and some unique string that should represent the configurations
+     * the split contains.
+     *
+     * Example:
+     * <table>
+     *     <tr>
+     *         <th>Feature split name</th>
+     *         <th>Configuration split name: xhdpi</th>
+     *         <th>Configuration split name: fr-rFR</th>
+     *     </tr>
+     *     <tr>
+     *         <td>(base APK)</td>
+     *         <td><code>$xhdpi</code></td>
+     *         <td><code>$fr-rFR</code></td>
+     *     </tr>
+     *     <tr>
+     *         <td><code>Extras</code></td>
+     *         <td><code>Extras$xhdpi</code></td>
+     *         <td><code>Extras$fr-rFR</code></td>
+     *     </tr>
+     * </table>
+     *
+     * @param splitName The name of the split to check.
+     * @param featureSplitName The name of the Feature split. May be null or "" if checking
+     *                         the base Application APK.
+     * @return true if the splitName represents a Configuration split of featureSplitName.
+     */
+    protected static boolean isConfigurationSplitOf(String splitName, String featureSplitName) {
+        if (featureSplitName == null || featureSplitName.length() == 0) {
+            // We are looking for configuration splits of the base, which have some legacy support.
+            if (splitName.startsWith("config_")) {
+                return true;
+            } else if (splitName.startsWith("$")) {
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            return splitName.startsWith(featureSplitName + "$");
+        }
+    }
+}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 48860f7..99fbee1 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -101,6 +101,68 @@
      */
     public boolean userSetLocale;
 
+
+    /** Constant for {@link #colorMode}: bits that encode whether the screen is wide gamut. */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 0x3;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
+     * indicating that it is unknown whether or not the screen is wide gamut.
+     */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED = 0x0;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
+     * indicating that the screen is not wide gamut.
+     * <p>Corresponds to the <code>-nowidecg</code> resource qualifier.</p>
+     */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_NO = 0x1;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} value
+     * indicating that the screen is wide gamut.
+     * <p>Corresponds to the <code>-widecg</code> resource qualifier.</p>
+     */
+    public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 0x2;
+
+    /** Constant for {@link #colorMode}: bits that encode whether the dynamic range of the screen. */
+    public static final int COLOR_MODE_HDR_MASK = 0xc;
+    /** Constant for {@link #colorMode}: bits shift to get the screen dynamic range. */
+    public static final int COLOR_MODE_HDR_SHIFT = 2;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
+     * indicating that it is unknown whether or not the screen is HDR.
+     */
+    public static final int COLOR_MODE_HDR_UNDEFINED = 0x0;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
+     * indicating that the screen is not HDR (low/standard dynamic range).
+     * <p>Corresponds to the <code>-lowdr</code> resource qualifier.</p>
+     */
+    public static final int COLOR_MODE_HDR_NO = 0x1 << COLOR_MODE_HDR_SHIFT;
+    /**
+     * Constant for {@link #colorMode}: a {@link #COLOR_MODE_HDR_MASK} value
+     * indicating that the screen is HDR (dynamic range).
+     * <p>Corresponds to the <code>-highdr</code> resource qualifier.</p>
+     */
+    public static final int COLOR_MODE_HDR_YES = 0x2 << COLOR_MODE_HDR_SHIFT;
+
+    /** Constant for {@link #colorMode}: a value indicating that the color mode is undefined */
+    @SuppressWarnings("PointlessBitwiseExpression")
+    public static final int COLOR_MODE_UNDEFINED = COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED |
+            COLOR_MODE_HDR_UNDEFINED;
+
+    /**
+     * Bit mask of for color capabilities of the screen. Currently there are two fields:
+     * <p>The {@link #COLOR_MODE_WIDE_COLOR_GAMUT_MASK} bits define the color gamut of
+     * the screen. They may be one of
+     * {@link #COLOR_MODE_WIDE_COLOR_GAMUT_NO} or {@link #COLOR_MODE_WIDE_COLOR_GAMUT_YES}.</p>
+     *
+     * <p>The {@link #COLOR_MODE_HDR_MASK} defines the dynamic range of the screen. They may be
+     * one of {@link #COLOR_MODE_HDR_NO} or {@link #COLOR_MODE_HDR_YES}.</p>
+     *
+     * <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information.</p>
+     */
+    public int colorMode;
+
     /** Constant for {@link #screenLayout}: bits that encode the size. */
     public static final int SCREENLAYOUT_SIZE_MASK = 0x0f;
     /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
@@ -331,6 +393,9 @@
         if ((diff & ActivityInfo.CONFIG_SCREEN_LAYOUT) != 0) {
             list.add("CONFIG_SCREEN_LAYOUT");
         }
+        if ((diff & ActivityInfo.CONFIG_COLOR_MODE) != 0) {
+            list.add("CONFIG_COLOR_MODE");
+        }
         if ((diff & ActivityInfo.CONFIG_UI_MODE) != 0) {
             list.add("CONFIG_UI_MODE");
         }
@@ -346,6 +411,9 @@
         if ((diff & ActivityInfo.CONFIG_FONT_SCALE) != 0) {
             list.add("CONFIG_FONT_SCALE");
         }
+        if ((diff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
+            list.add("CONFIG_ASSETS_PATHS");
+        }
         StringBuilder builder = new StringBuilder("{");
         for (int i = 0, n = list.size(); i < n; i++) {
             builder.append(list.get(i));
@@ -671,6 +739,21 @@
     public int compatSmallestScreenWidthDp;
 
     /**
+     * An undefined assetsSeq. This will not override an existing assetsSeq.
+     * @hide
+     */
+    public static final int ASSETS_SEQ_UNDEFINED = 0;
+
+    /**
+     * Internal counter that allows us to piggyback off the configuration change mechanism to
+     * signal to apps that the the assets for an Application have changed. A difference in these
+     * between two Configurations will yield a diff flag of
+     * {@link ActivityInfo#CONFIG_ASSETS_PATHS}.
+     * @hide
+     */
+    public int assetsSeq;
+
+    /**
      * @hide Internal book-keeping.
      */
     public int seq;
@@ -693,6 +776,7 @@
                     NATIVE_CONFIG_UI_MODE,
                     NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
                     NATIVE_CONFIG_LAYOUTDIR,
+                    NATIVE_CONFIG_COLOR_MODE,
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NativeConfig {}
@@ -729,6 +813,8 @@
     public static final int NATIVE_CONFIG_SMALLEST_SCREEN_SIZE = 0x2000;
     /** @hide Native-specific bit mask for LAYOUTDIR config ; DO NOT USE UNLESS YOU ARE SURE.*/
     public static final int NATIVE_CONFIG_LAYOUTDIR = 0x4000;
+    /** @hide Native-specific bit mask for COLOR_MODE config ; DO NOT USE UNLESS YOU ARE SURE.*/
+    public static final int NATIVE_CONFIG_COLOR_MODE = 0x10000;
 
     /**
      * <p>Construct an invalid Configuration. This state is only suitable for constructing a
@@ -787,6 +873,7 @@
         navigationHidden = o.navigationHidden;
         orientation = o.orientation;
         screenLayout = o.screenLayout;
+        colorMode = o.colorMode;
         uiMode = o.uiMode;
         screenWidthDp = o.screenWidthDp;
         screenHeightDp = o.screenHeightDp;
@@ -795,6 +882,7 @@
         compatScreenWidthDp = o.compatScreenWidthDp;
         compatScreenHeightDp = o.compatScreenHeightDp;
         compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
+        assetsSeq = o.assetsSeq;
         seq = o.seq;
     }
 
@@ -866,6 +954,20 @@
             default: sb.append(" layoutLong=");
                     sb.append(screenLayout&SCREENLAYOUT_LONG_MASK); break;
         }
+        switch ((colorMode &COLOR_MODE_HDR_MASK)) {
+            case COLOR_MODE_HDR_UNDEFINED: sb.append(" ?ldr"); break; // most likely not HDR
+            case COLOR_MODE_HDR_NO: /* ldr is not interesting to print */ break;
+            case COLOR_MODE_HDR_YES: sb.append(" hdr"); break;
+            default: sb.append(" dynamicRange=");
+                sb.append(colorMode &COLOR_MODE_HDR_MASK); break;
+        }
+        switch ((colorMode &COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
+            case COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED: sb.append(" ?wideColorGamut"); break;
+            case COLOR_MODE_WIDE_COLOR_GAMUT_NO: /* not wide is not interesting to print */ break;
+            case COLOR_MODE_WIDE_COLOR_GAMUT_YES: sb.append(" widecg"); break;
+            default: sb.append(" wideColorGamut=");
+                sb.append(colorMode &COLOR_MODE_WIDE_COLOR_GAMUT_MASK); break;
+        }
         switch (orientation) {
             case ORIENTATION_UNDEFINED: sb.append(" ?orien"); break;
             case ORIENTATION_LANDSCAPE: sb.append(" land"); break;
@@ -930,9 +1032,11 @@
             case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
             default: sb.append("/"); sb.append(navigationHidden); break;
         }
+        if (assetsSeq != 0) {
+            sb.append(" as.").append(assetsSeq);
+        }
         if (seq != 0) {
-            sb.append(" s.");
-            sb.append(seq);
+            sb.append(" s.").append(seq);
         }
         sb.append('}');
         return sb.toString();
@@ -955,11 +1059,13 @@
         navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
         orientation = ORIENTATION_UNDEFINED;
         screenLayout = SCREENLAYOUT_UNDEFINED;
+        colorMode = COLOR_MODE_UNDEFINED;
         uiMode = UI_MODE_TYPE_UNDEFINED;
         screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
         screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
         smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
         densityDpi = DENSITY_DPI_UNDEFINED;
+        assetsSeq = ASSETS_SEQ_UNDEFINED;
         seq = 0;
     }
 
@@ -1089,6 +1195,23 @@
                 | (delta.screenLayout & SCREENLAYOUT_COMPAT_NEEDED);
         }
 
+        if (((delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
+                     COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED)
+                && (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)
+                != (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
+            changed |= ActivityInfo.CONFIG_COLOR_MODE;
+            colorMode = (colorMode & ~COLOR_MODE_WIDE_COLOR_GAMUT_MASK)
+                    | (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
+        }
+
+        if (((delta.colorMode & COLOR_MODE_HDR_MASK) != COLOR_MODE_HDR_UNDEFINED)
+                && (delta.colorMode & COLOR_MODE_HDR_MASK)
+                != (colorMode & COLOR_MODE_HDR_MASK)) {
+            changed |= ActivityInfo.CONFIG_COLOR_MODE;
+            colorMode = (colorMode & ~COLOR_MODE_HDR_MASK)
+                    | (delta.colorMode & COLOR_MODE_HDR_MASK);
+        }
+
         if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
                 && uiMode != delta.uiMode) {
             changed |= ActivityInfo.CONFIG_UI_MODE;
@@ -1130,6 +1253,10 @@
         if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
             compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
         }
+        if (delta.assetsSeq != ASSETS_SEQ_UNDEFINED) {
+            changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
+            assetsSeq = delta.assetsSeq;
+        }
         if (delta.seq != 0) {
             seq = delta.seq;
         }
@@ -1234,6 +1361,19 @@
                 getScreenLayoutNoDirection(delta.screenLayout)) {
             changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
         }
+        if ((compareUndefined ||
+                     (delta.colorMode & COLOR_MODE_HDR_MASK) != COLOR_MODE_HDR_UNDEFINED)
+                && (colorMode & COLOR_MODE_HDR_MASK) !=
+                        (delta.colorMode & COLOR_MODE_HDR_MASK)) {
+            changed |= ActivityInfo.CONFIG_COLOR_MODE;
+        }
+        if ((compareUndefined ||
+                     (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
+                             COLOR_MODE_WIDE_COLOR_GAMUT_UNDEFINED)
+                && (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
+                        (delta.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
+            changed |= ActivityInfo.CONFIG_COLOR_MODE;
+        }
         if ((compareUndefined || delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED))
                 && uiMode != delta.uiMode) {
             changed |= ActivityInfo.CONFIG_UI_MODE;
@@ -1254,6 +1394,10 @@
                 && densityDpi != delta.densityDpi) {
             changed |= ActivityInfo.CONFIG_DENSITY;
         }
+        if ((compareUndefined || delta.assetsSeq != ASSETS_SEQ_UNDEFINED)
+                && assetsSeq != delta.assetsSeq) {
+            changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
+        }
 
         return changed;
     }
@@ -1272,7 +1416,11 @@
      */
     public static boolean needNewResources(@Config int configChanges,
             @Config int interestingChanges) {
-        return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
+        // CONFIG_ASSETS_PATHS and CONFIG_FONT_SCALE are higher level configuration changes that
+        // all resources are subject to change with.
+        interestingChanges = interestingChanges | ActivityInfo.CONFIG_ASSETS_PATHS
+                | ActivityInfo.CONFIG_FONT_SCALE;
+        return (configChanges & interestingChanges) != 0;
     }
 
     /**
@@ -1337,6 +1485,7 @@
         dest.writeInt(navigationHidden);
         dest.writeInt(orientation);
         dest.writeInt(screenLayout);
+        dest.writeInt(colorMode);
         dest.writeInt(uiMode);
         dest.writeInt(screenWidthDp);
         dest.writeInt(screenHeightDp);
@@ -1345,6 +1494,7 @@
         dest.writeInt(compatScreenWidthDp);
         dest.writeInt(compatScreenHeightDp);
         dest.writeInt(compatSmallestScreenWidthDp);
+        dest.writeInt(assetsSeq);
         dest.writeInt(seq);
     }
 
@@ -1370,6 +1520,7 @@
         navigationHidden = source.readInt();
         orientation = source.readInt();
         screenLayout = source.readInt();
+        colorMode = source.readInt();
         uiMode = source.readInt();
         screenWidthDp = source.readInt();
         screenHeightDp = source.readInt();
@@ -1378,6 +1529,7 @@
         compatScreenWidthDp = source.readInt();
         compatScreenHeightDp = source.readInt();
         compatSmallestScreenWidthDp = source.readInt();
+        assetsSeq = source.readInt();
         seq = source.readInt();
     }
 
@@ -1450,6 +1602,8 @@
         if (n != 0) return n;
         n = this.orientation - that.orientation;
         if (n != 0) return n;
+        n = this.colorMode - that.colorMode;
+        if (n != 0) return n;
         n = this.screenLayout - that.screenLayout;
         if (n != 0) return n;
         n = this.uiMode - that.uiMode;
@@ -1461,6 +1615,8 @@
         n = this.smallestScreenWidthDp - that.smallestScreenWidthDp;
         if (n != 0) return n;
         n = this.densityDpi - that.densityDpi;
+        if (n != 0) return n;
+        n = this.assetsSeq - that.assetsSeq;
         //if (n != 0) return n;
         return n;
     }
@@ -1493,11 +1649,13 @@
         result = 31 * result + navigationHidden;
         result = 31 * result + orientation;
         result = 31 * result + screenLayout;
+        result = 31 * result + colorMode;
         result = 31 * result + uiMode;
         result = 31 * result + screenWidthDp;
         result = 31 * result + screenHeightDp;
         result = 31 * result + smallestScreenWidthDp;
         result = 31 * result + densityDpi;
+        result = 31 * result + assetsSeq;
         return result;
     }
 
@@ -1600,6 +1758,24 @@
     }
 
     /**
+     * Return whether the screen has a wide color gamut.
+     *
+     * @return true if the screen has a wide color gamut, false otherwise
+     */
+    public boolean isScreenWideColorGamut() {
+        return (colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) == COLOR_MODE_WIDE_COLOR_GAMUT_YES;
+    }
+
+    /**
+     * Return whether the screen has a high dynamic range.
+     *
+     * @return true if the screen has a high dynamic range, false otherwise
+     */
+    public boolean isScreenHdr() {
+        return (colorMode & COLOR_MODE_HDR_MASK) == COLOR_MODE_HDR_YES;
+    }
+
+    /**
      *
      * @hide
      */
@@ -1731,6 +1907,28 @@
                 break;
         }
 
+        switch (config.colorMode & Configuration.COLOR_MODE_HDR_MASK) {
+            case Configuration.COLOR_MODE_HDR_YES:
+                parts.add("highdr");
+                break;
+            case Configuration.COLOR_MODE_HDR_NO:
+                parts.add("lowdr");
+                break;
+            default:
+                break;
+        }
+
+        switch (config.colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK) {
+            case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES:
+                parts.add("widecg");
+                break;
+            case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO:
+                parts.add("nowidecg");
+                break;
+            default:
+                break;
+        }
+
         switch (config.orientation) {
             case Configuration.ORIENTATION_LANDSCAPE:
                 parts.add("land");
@@ -1956,6 +2154,16 @@
             delta.screenLayout |= change.screenLayout & SCREENLAYOUT_ROUND_MASK;
         }
 
+        if ((base.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK) !=
+                (change.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK)) {
+            delta.colorMode |= change.colorMode & COLOR_MODE_WIDE_COLOR_GAMUT_MASK;
+        }
+
+        if ((base.colorMode & COLOR_MODE_HDR_MASK) !=
+                (change.colorMode & COLOR_MODE_HDR_MASK)) {
+            delta.colorMode |= change.colorMode & COLOR_MODE_HDR_MASK;
+        }
+
         if ((base.uiMode & UI_MODE_TYPE_MASK) != (change.uiMode & UI_MODE_TYPE_MASK)) {
             delta.uiMode |= change.uiMode & UI_MODE_TYPE_MASK;
         }
@@ -1979,6 +2187,10 @@
         if (base.densityDpi != change.densityDpi) {
             delta.densityDpi = change.densityDpi;
         }
+
+        if (base.assetsSeq != change.assetsSeq) {
+            delta.assetsSeq = change.assetsSeq;
+        }
         return delta;
     }
 
@@ -1994,6 +2206,7 @@
     private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid";
     private static final String XML_ATTR_ORIENTATION = "ori";
     private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay";
+    private static final String XML_ATTR_COLOR_MODE = "clrMod";
     private static final String XML_ATTR_UI_MODE = "ui";
     private static final String XML_ATTR_SCREEN_WIDTH = "width";
     private static final String XML_ATTR_SCREEN_HEIGHT = "height";
@@ -2036,6 +2249,8 @@
                 ORIENTATION_UNDEFINED);
         configOut.screenLayout = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_LAYOUT,
                 SCREENLAYOUT_UNDEFINED);
+        configOut.colorMode = XmlUtils.readIntAttribute(parser, XML_ATTR_COLOR_MODE,
+                COLOR_MODE_UNDEFINED);
         configOut.uiMode = XmlUtils.readIntAttribute(parser, XML_ATTR_UI_MODE, 0);
         configOut.screenWidthDp = XmlUtils.readIntAttribute(parser, XML_ATTR_SCREEN_WIDTH,
                 SCREEN_WIDTH_DP_UNDEFINED);
@@ -2046,6 +2261,8 @@
                         SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
         configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
                 DENSITY_DPI_UNDEFINED);
+
+        // For persistence, we don't care about assetsSeq, so do not read it out.
     }
 
 
@@ -2096,6 +2313,9 @@
         if (config.screenLayout != SCREENLAYOUT_UNDEFINED) {
             XmlUtils.writeIntAttribute(xml, XML_ATTR_SCREEN_LAYOUT, config.screenLayout);
         }
+        if (config.colorMode != COLOR_MODE_UNDEFINED) {
+            XmlUtils.writeIntAttribute(xml, XML_ATTR_COLOR_MODE, config.colorMode);
+        }
         if (config.uiMode != 0) {
             XmlUtils.writeIntAttribute(xml, XML_ATTR_UI_MODE, config.uiMode);
         }
@@ -2111,5 +2331,7 @@
         if (config.densityDpi != DENSITY_DPI_UNDEFINED) {
             XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
         }
+
+        // For persistence, we do not care about assetsSeq, so do not write it out.
     }
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ad11307..c3185a7 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -40,6 +40,7 @@
 import android.annotation.XmlRes;
 import android.content.pm.ActivityInfo;
 import android.graphics.Movie;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.drawable.DrawableInflater;
@@ -333,7 +334,35 @@
             return res;
         }
         throw new NotFoundException("String resource ID #0x"
-                                    + Integer.toHexString(id));
+                + Integer.toHexString(id));
+    }
+
+    /**
+     * Return the Typeface value associated with a particular resource ID.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Typeface The Typeface data associated with the resource.
+     */
+    @NonNull public Typeface getFont(@StringRes int id) throws NotFoundException {
+        final TypedValue value = obtainTempTypedValue();
+        try {
+            final ResourcesImpl impl = mResourcesImpl;
+            impl.getValue(id, value, true);
+            Typeface typeface = impl.loadFont(value, id);
+            if (typeface != null) {
+                return typeface;
+            }
+        } finally {
+            releaseTempTypedValue(value);
+        }
+        throw new NotFoundException("Font resource ID #0x"
+                + Integer.toHexString(id));
     }
 
     /**
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index eb010e4..05892e0 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -31,6 +31,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.Resources.NotFoundException;
+import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.icu.text.PluralRules;
@@ -47,6 +48,7 @@
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Locale;
@@ -740,6 +742,36 @@
     }
 
     /**
+     * Loads a font from XML or resources stream.
+     */
+    @Nullable
+    public Typeface loadFont(TypedValue value, int id) {
+        if (value.string == null) {
+            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+                    + Integer.toHexString(id) + ") is not a Font: " + value);
+        }
+
+        final String file = value.string.toString();
+
+        if (DEBUG_LOAD) {
+            Log.v(TAG, "Loading font for cookie " + value.assetCookie + ": " + file);
+        }
+
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+        try {
+            if (file.endsWith(".xml")) {
+                // TODO handle xml type font definitions
+            } else {
+                return Typeface.createFromResources(
+                        mAssets, value.string.toString(), value.assetCookie);
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        }
+        return null;
+    }
+
+    /**
      * Given the value and id, we can get the XML filename as in value.data, based on that, we
      * first try to load CSL from the cache. If not found, try to get from the constant state.
      * Last, parse the XML and generate the CSL.
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 227066d..8cd3d7b 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -728,13 +728,10 @@
      * @param values the {@link ContentValues} to put the row into.
      */
     public static void cursorRowToContentValues(Cursor cursor, ContentValues values) {
-        AbstractWindowedCursor awc =
-                (cursor instanceof AbstractWindowedCursor) ? (AbstractWindowedCursor) cursor : null;
-
         String[] columns = cursor.getColumnNames();
         int length = columns.length;
         for (int i = 0; i < length; i++) {
-            if (awc != null && awc.isBlob(i)) {
+            if (cursor.getType(i) == Cursor.FIELD_TYPE_BLOB) {
                 values.put(columns[i], cursor.getBlob(i));
             } else {
                 values.put(columns[i], cursor.getString(i));
diff --git a/core/java/android/hardware/HardwareBuffer.aidl b/core/java/android/hardware/HardwareBuffer.aidl
new file mode 100644
index 0000000..5bdd966
--- /dev/null
+++ b/core/java/android/hardware/HardwareBuffer.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+parcelable HardwareBuffer;
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
new file mode 100644
index 0000000..e97bb2f
--- /dev/null
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import dalvik.annotation.optimization.FastNative;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * HardwareBuffer wraps a native <code>AHardwareBuffer</code> object, which is a low-level object
+ * representing a memory buffer accessible by various hardware units. HardwareBuffer allows sharing
+ * buffers across different application processes. In particular, HardwareBuffers may be mappable
+ * to memory accessibly to various hardware systems, such as the GPU, a sensor or context hub, or
+ * other auxiliary processing units.
+ *
+ * For more information, see the NDK documentation for <code>AHardwareBuffer</code>.
+ */
+public final class HardwareBuffer implements Parcelable {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RGBA_8888, RGBA_FP16, RGBX_8888, RGB_888, RGB_565, BLOB})
+    public @interface Format {};
+
+    /** Format: 8 bits each red, green, blue, alpha */
+    public static final int RGBA_8888   = 1;
+    /** Format: 8 bits each red, green, blue, alpha, alpha is always 0xFF */
+    public static final int RGBX_8888   = 2;
+    /** Format: 8 bits each red, green, blue, no alpha */
+    public static final int RGB_888     = 3;
+    /** Format: 5 bits each red and blue, 6 bits green, no alpha */
+    public static final int RGB_565     = 4;
+    /** Format: 16 bits each red, green, blue, alpha */
+    public static final int RGBA_FP16   = 0x16;
+    /** Format: opaque format used for raw data transfer; must have a height of 1 */
+    public static final int BLOB        = 0x21;
+
+    // Note: do not rename, this field is used by native code
+    private long mNativeObject;
+
+    // Invoked on destruction
+    private Runnable mCleaner;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {USAGE0_CPU_READ, USAGE0_CPU_READ_OFTEN, USAGE0_CPU_WRITE,
+            USAGE0_CPU_WRITE_OFTEN, USAGE0_GPU_SAMPLED_IMAGE, USAGE0_GPU_COLOR_OUTPUT,
+            USAGE0_GPU_STORAGE_IMAGE, USAGE0_GPU_CUBEMAP, USAGE0_GPU_DATA_BUFFER,
+            USAGE0_PROTECTED_CONTENT, USAGE0_SENSOR_DIRECT_DATA, USAGE0_VIDEO_ENCODE})
+    public @interface Usage0 {};
+
+    /** Usage0: the buffer will sometimes be read by the CPU */
+    public static final long USAGE0_CPU_READ               = (1 << 1);
+    /** Usage0: the buffer will often be read by the CPU*/
+    public static final long USAGE0_CPU_READ_OFTEN         = (1 << 2 | USAGE0_CPU_READ);
+    /** Usage0: the buffer will sometimes be written to by the CPU */
+    public static final long USAGE0_CPU_WRITE              = (1 << 5);
+    /** Usage0: the buffer will often be written to by the CPU */
+    public static final long USAGE0_CPU_WRITE_OFTEN        = (1 << 6 | USAGE0_CPU_WRITE);
+    /** Usage0: the buffer will be read from by the GPU */
+    public static final long USAGE0_GPU_SAMPLED_IMAGE      = (1 << 10);
+    /** Usage0: the buffer will be written to by the GPU */
+    public static final long USAGE0_GPU_COLOR_OUTPUT       = (1 << 11);
+    /** Usage0: the buffer will be read from and written to by the GPU */
+    public static final long USAGE0_GPU_STORAGE_IMAGE      = (USAGE0_GPU_SAMPLED_IMAGE |
+            USAGE0_GPU_COLOR_OUTPUT);
+    /** Usage0: the buffer will be used as a cubemap texture */
+    public static final long USAGE0_GPU_CUBEMAP            = (1 << 13);
+    /** Usage0: the buffer will be used as a shader storage or uniform buffer object*/
+    public static final long USAGE0_GPU_DATA_BUFFER        = (1 << 14);
+    /** Usage0: the buffer must not be used outside of a protected hardware path */
+    public static final long USAGE0_PROTECTED_CONTENT      = (1 << 18);
+    /** Usage0: the buffer will be used for sensor direct data */
+    public static final long USAGE0_SENSOR_DIRECT_DATA     = (1 << 29);
+    /** Usage0: the buffer will be read by a hardware video encoder */
+    public static final long USAGE0_VIDEO_ENCODE           = (1 << 21);
+
+    // The approximate size of a native AHardwareBuffer object.
+    private static final long NATIVE_HARDWARE_BUFFER_SIZE = 232;
+    /**
+     * Creates a new <code>HardwareBuffer</code> instance.
+     *
+     * <p>Calling this method will throw an <code>IllegalStateException</code> if
+     * format is not a supported Format type.</p>
+     *
+     * @param width The width in pixels of the buffer
+     * @param height The height in pixels of the buffer
+     * @param format The format of each pixel, one of {@link #RGBA_8888}, {@link #RGBA_FP16},
+     * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}
+     * @param layers The number of layers in the buffer
+     * @param usage Flags describing how the buffer will be used, one of
+     *     {@link #USAGE0_CPU_READ}, {@link #USAGE0_CPU_READ_OFTEN}, {@link #USAGE0_CPU_WRITE},
+     *     {@link #USAGE0_CPU_WRITE_OFTEN}, {@link #USAGE0_GPU_SAMPLED_IMAGE},
+     *     {@link #USAGE0_GPU_COLOR_OUTPUT},{@link #USAGE0_GPU_STORAGE_IMAGE},
+     *     {@link #USAGE0_GPU_CUBEMAP}, {@link #USAGE0_GPU_DATA_BUFFER},
+     *     {@link #USAGE0_PROTECTED_CONTENT}, {@link #USAGE0_SENSOR_DIRECT_DATA},
+     *     {@link #USAGE0_VIDEO_ENCODE}
+     *
+     * @return A <code>HardwareBuffer</code> instance if successful, or throws an
+     *     IllegalArgumentException if the dimensions passed are invalid (either zero, negative, or
+     *     too large to allocate), if the format is not supported, if the requested number of layers
+     *     is less than one or not supported, or if the passed usage flags are not a supported set.
+     */
+    @NonNull
+    public static HardwareBuffer create(int width, int height, @Format int format, int layers,
+            @Usage0 long usage) {
+        if (!HardwareBuffer.isSupportedFormat(format)) {
+            throw new IllegalArgumentException("Invalid pixel format " + format);
+        }
+        if (width <= 0) {
+            throw new IllegalArgumentException("Invalid width " + width);
+        }
+        if (height <= 0) {
+            throw new IllegalArgumentException("Invalid height " + height);
+        }
+        if (layers <= 0) {
+            throw new IllegalArgumentException("Invalid layer count " + layers);
+        }
+        if (format == BLOB && height != 1) {
+            throw new IllegalArgumentException("Height must be 1 when using the BLOB format");
+        }
+        long nativeObject = nCreateHardwareBuffer(width, height, format, layers, usage);
+        if (nativeObject == 0) {
+            throw new IllegalArgumentException("Unable to create a HardwareBuffer, either the " +
+                    "dimensions passed were too large, too many image layers were requested, " +
+                    "or an invalid set of usage flags was passed");
+        }
+        return new HardwareBuffer(nativeObject);
+    }
+
+    /**
+     * Private use only. See {@link #create(int, int, int, int, int, long, long)}. May also be
+     * called from JNI using an already allocated native <code>HardwareBuffer</code>.
+     */
+    private HardwareBuffer(long nativeObject) {
+        mNativeObject = nativeObject;
+
+        long nativeSize = NATIVE_HARDWARE_BUFFER_SIZE;
+        NativeAllocationRegistry registry = new NativeAllocationRegistry(
+            HardwareBuffer.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
+        mCleaner = registry.registerNativeAllocation(this, mNativeObject);
+    }
+
+    /**
+     * Returns the width of this buffer in pixels.
+     */
+    public int getWidth() {
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("This HardwareBuffer has been destroyed and its width "
+                    + "cannot be obtained.");
+        }
+        return nGetWidth(mNativeObject);
+    }
+
+    /**
+     * Returns the height of this buffer in pixels.
+     */
+    public int getHeight() {
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("This HardwareBuffer has been destroyed and its height "
+                    + "cannot be obtained.");
+        }
+        return nGetHeight(mNativeObject);
+    }
+
+    /**
+     * Returns the format of this buffer, one of {@link #RGBA_8888}, {@link #RGBA_FP16},
+     * {@link #RGBX_8888}, {@link #RGB_565}, or {@link #RGB_888}.
+     */
+    public int getFormat() {
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("This HardwareBuffer has been destroyed and its format "
+                    + "cannot be obtained.");
+        }
+        return nGetFormat(mNativeObject);
+    }
+
+    /**
+     * Returns the number of layers in this buffer.
+     */
+    public int getLayers() {
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("This HardwareBuffer has been destroyed and its layer "
+                    + "count cannot be obtained.");
+        }
+        return nGetLayers(mNativeObject);
+    }
+
+    /**
+     * Returns the usage flags of the usage hints set on this buffer.
+     */
+    public long getUsage() {
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("This HardwareBuffer has been destroyed and its usage "
+                    + "cannot be obtained.");
+        }
+        return nGetUsage(mNativeObject);
+    }
+
+    /**
+     * Destroys this buffer immediately. Calling this method frees up any
+     * underlying native resources. After calling this method, this buffer
+     * must not be used in any way.
+     *
+     * @see #isDestroyed()
+     */
+    public void destroy() {
+        if (mNativeObject != 0) {
+            mNativeObject = 0;
+            mCleaner.run();
+            mCleaner = null;
+        }
+    }
+
+    /**
+     * Indicates whether this buffer has been destroyed. A destroyed buffer
+     * cannot be used in any way: the buffer cannot be written to a parcel, etc.
+     *
+     * @return True if this <code>HardwareBuffer</code> is in a destroyed state,
+     *         false otherwise.
+     *
+     * @see #destroy()
+     */
+    public boolean isDestroyed() {
+        return mNativeObject == 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return Parcelable.CONTENTS_FILE_DESCRIPTOR;
+    }
+
+    /**
+     * Flatten this object in to a Parcel.
+     *
+     * <p>Calling this method will throw an <code>IllegalStateException</code> if
+     * {@link #destroy()} has been previously called.</p>
+     *
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("This HardwareBuffer has been destroyed and cannot be "
+                    + "written to a parcel.");
+        }
+        nWriteHardwareBufferToParcel(mNativeObject, dest);
+    }
+
+    public static final Parcelable.Creator<HardwareBuffer> CREATOR =
+            new Parcelable.Creator<HardwareBuffer>() {
+        public HardwareBuffer createFromParcel(Parcel in) {
+            long nativeObject = nReadHardwareBufferFromParcel(in);
+            if (nativeObject != 0) {
+                return new HardwareBuffer(nativeObject);
+            }
+            return null;
+        }
+
+        public HardwareBuffer[] newArray(int size) {
+            return new HardwareBuffer[size];
+        }
+    };
+
+    /**
+     * Validates whether a particular format is supported by HardwareBuffer.
+     *
+     * @param format The format to validate.
+     *
+     * @return True if <code>format</code> is a supported format. false otherwise.
+     * See {@link #create(int, int, int, int, int, long, long)}.a
+     */
+    private static boolean isSupportedFormat(@Format int format) {
+        switch(format) {
+            case RGBA_8888:
+            case RGBA_FP16:
+            case RGBX_8888:
+            case RGB_565:
+            case RGB_888:
+            case BLOB:
+                return true;
+        }
+        return false;
+    }
+
+    private static native long nCreateHardwareBuffer(int width, int height, int format, int layers,
+            long usage);
+    private static native long nGetNativeFinalizer();
+    private static native void nWriteHardwareBufferToParcel(long nativeObject, Parcel dest);
+    private static native long nReadHardwareBufferFromParcel(Parcel in);
+    @FastNative
+    private static native int nGetWidth(long nativeObject);
+    @FastNative
+    private static native int nGetHeight(long nativeObject);
+    @FastNative
+    private static native int nGetFormat(long nativeObject);
+    @FastNative
+    private static native int nGetLayers(long nativeObject);
+    @FastNative
+    private static native long nGetUsage(long nativeObject);
+}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 06ab13e..d87c55e 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -729,14 +729,22 @@
     private static final int DATA_INJECTION_MASK = 0x10;
     private static final int DATA_INJECTION_SHIFT = 4;
 
-    // MASK for dynamic sensor (sensor that added during runtime), bit 6.
+    // MASK for dynamic sensor (sensor that added during runtime), bit 5.
     private static final int DYNAMIC_SENSOR_MASK = 0x20;
     private static final int DYNAMIC_SENSOR_SHIFT = 5;
 
-    // MASK for indication bit of sensor additional information support (bit 7).
+    // MASK for indication bit of sensor additional information support, bit 6.
     private static final int ADDITIONAL_INFO_MASK = 0x40;
     private static final int ADDITIONAL_INFO_SHIFT = 6;
 
+    // Mask for direct mode highest rate level, bit 7, 8, 9.
+    private static final int DIRECT_REPORT_MASK = 0x380;
+    private static final int DIRECT_REPORT_SHIFT = 7;
+
+    // Mask for supported direct channel, bit 10, 11
+    private static final int DIRECT_CHANNEL_MASK = 0xC00;
+    private static final int DIRECT_CHANNEL_SHIFT = 10;
+
     // TODO(): The following arrays are fragile and error-prone. This needs to be refactored.
 
     // Note: This needs to be updated, whenever a new sensor is added.
@@ -796,6 +804,42 @@
         return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
     }
 
+    /**
+     * Get the highest supported direct report mode rate level of the sensor.
+     *
+     * @return Highest direct report rate level of this sensor. If the sensor does not support
+     * direct report mode, this returns {@link SensorDirectChannel#RATE_STOP}.
+     * @see SensorDirectChannel#RATE_STOP
+     * @see SensorDirectChannel#RATE_NORMAL
+     * @see SensorDirectChannel#RATE_FAST
+     * @see SensorDirectChannel#RATE_VERY_FAST
+     */
+    @SensorDirectChannel.RateLevel
+    public int getHighestDirectReportRateLevel() {
+        int rateLevel = ((mFlags & DIRECT_REPORT_MASK) >> DIRECT_REPORT_SHIFT);
+        return rateLevel <= SensorDirectChannel.RATE_VERY_FAST
+                ? rateLevel : SensorDirectChannel.RATE_VERY_FAST;
+    }
+
+    /**
+     * Test if sensor support direct channel backed by a specific type of shared memory.
+     *
+     * @param sharedMemType type of shared memory used by direct channel.
+     * @return <code>true</code> if the shared memory type is supported.
+     * @see SensorDirectChannel#TYPE_ASHMEM
+     * @see SensorDirectChannel#TYPE_HARDWARE_BUFFER
+     */
+    public boolean isDirectChannelTypeSupported(@SensorDirectChannel.MemoryType int sharedMemType) {
+        switch (sharedMemType) {
+            case SensorDirectChannel.TYPE_ASHMEM:
+                return (mFlags & (1 << DIRECT_CHANNEL_SHIFT)) > 0;
+            case SensorDirectChannel.TYPE_HARDWARE_BUFFER:
+                return (mFlags & (1 << DIRECT_CHANNEL_SHIFT + 1)) > 0;
+            default:
+                return false;
+        }
+    }
+
     static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
         // RotationVector length has changed to 3 to 5 for API level 18
         // Set it to 3 for backward compatibility.
diff --git a/core/java/android/hardware/SensorDirectChannel.java b/core/java/android/hardware/SensorDirectChannel.java
new file mode 100644
index 0000000..0efd62b
--- /dev/null
+++ b/core/java/android/hardware/SensorDirectChannel.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware;
+
+import android.annotation.IntDef;
+import android.os.MemoryFile;
+
+import dalvik.system.CloseGuard;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Class representing a sensor direct channel. Use {@link
+ * SensorManager#createDirectChannel(android.os.MemoryFile)} to obtain object.
+ */
+public final class SensorDirectChannel implements AutoCloseable {
+
+    // shared memory types
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {TYPE_ASHMEM, TYPE_HARDWARE_BUFFER})
+    public @interface MemoryType {};
+    /**
+     * Shared memory type ashmem, wrapped in MemoryFile object.
+     *
+     * @see SensorManager#createDirectChannel(MemoryFile)
+     */
+    public static final int TYPE_ASHMEM = 1;
+
+    /**
+     * Shared memory type wrapped by HardwareBuffer object.
+     *
+     * @see SensorManager#createDirectChannel(HardwareBuffer)
+     */
+    public static final int TYPE_HARDWARE_BUFFER = 2;
+
+    // sensor rate levels
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {RATE_STOP, RATE_NORMAL, RATE_FAST, RATE_VERY_FAST})
+    public @interface RateLevel {};
+
+    /**
+     * Sensor stopped (no event output).
+     *
+     * @see SensorManager#configureDirectChannel(SensorDirectChannel, Sensor, int)
+     */
+    public static final int RATE_STOP = 0;
+    /**
+     * Sensor operates at nominal rate of 50Hz.
+     *
+     * The actual rate is expected to be between 55% to 220% of nominal rate, thus between 27.5Hz to
+     * 110Hz.
+     *
+     * @see SensorManager#configureDirectChannel(SensorDirectChannel, Sensor, int)
+     */
+    public static final int RATE_NORMAL = 1; //50Hz
+    /**
+     * Sensor operates at nominal rate of 200Hz.
+     *
+     * The actual rate is expected to be between 55% to 220% of nominal rate, thus between 110Hz to
+     * 440Hz.
+     *
+     * @see SensorManager#configureDirectChannel(SensorDirectChannel, Sensor, int)
+     */
+    public static final int RATE_FAST = 2; // ~200Hz
+    /**
+     * Sensor operates at nominal rate of 800Hz.
+     *
+     * The actual rate is expected to be between 55% to 220% of nominal rate, thus between 440Hz to
+     * 1760Hz.
+     *
+     * @see SensorManager#configureDirectChannel(SensorDirectChannel, Sensor, int)
+     */
+    public static final int RATE_VERY_FAST = 3; // ~800Hz
+
+    /**
+     * Determine if a channel is still valid. A channel is invalidated after {@link #close()} is
+     * called.
+     *
+     * @return <code>true</code> if channel is valid.
+     */
+    public boolean isValid() {
+        return !mClosed.get();
+    }
+
+    /**
+     * Close sensor direct channel.
+     *
+     * Stop all active sensor in the channel and free sensor system resource related to channel.
+     * Shared memory used for creating the direct channel need to be closed or freed separately.
+     *
+     * @see SensorManager#createDirectChannel(MemoryFile)
+     * @see SensorManager#createDirectChannel(HardwareBuffer)
+     */
+    @Override
+    public void close() {
+        mCloseGuard.close();
+        if (mClosed.compareAndSet(false, true)) {
+            // actual close action
+            mManager.destroyDirectChannel(this);
+        }
+    }
+
+    /** @hide */
+    SensorDirectChannel(SensorManager manager, int id, int type, long size) {
+        mManager = manager;
+        mNativeHandle = id;
+        mType = type;
+        mSize = size;
+        mCloseGuard.open("SensorDirectChannel");
+    }
+
+    /** @hide */
+    int getNativeHandle() {
+        return mNativeHandle;
+    }
+
+    /**
+     * This function encode handle information in {@link android.os.Memory} into a long array to be
+     * passed down to native methods.
+     *
+     * @hide */
+    static long[] encodeData(MemoryFile ashmem) {
+        int fd;
+        try {
+            fd = ashmem.getFileDescriptor().getInt$();
+        } catch (IOException e) {
+            fd = -1;
+        }
+        return new long[] { 1 /*numFds*/, 0 /*numInts*/, fd };
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            mCloseGuard.warnIfOpen();
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private final AtomicBoolean mClosed = new AtomicBoolean();
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+    private final SensorManager mManager;
+    private final int mNativeHandle;
+    private final long mSize;
+    private final int mType;
+}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 04ee1e6..cfda2f4 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -19,6 +19,7 @@
 import android.annotation.SystemApi;
 import android.os.Build;
 import android.os.Handler;
+import android.os.MemoryFile;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -492,7 +493,7 @@
         if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION ||
                 type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE ||
                 type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE ||
-                type == Sensor.TYPE_WRIST_TILT_GESTURE) {
+                type == Sensor.TYPE_WRIST_TILT_GESTURE || type == Sensor.TYPE_DYNAMIC_SENSOR_META) {
             wakeUpSensor = true;
         }
 
@@ -881,6 +882,104 @@
 
 
     /**
+     * Create a sensor direct channel backed by shared memory wrapped by MemoryFile object.
+     *
+     * Use the returned {@link android.hardware.SensorDirectChannel} object to configure direct
+     * report of sensor events. After use, call {@link android.hardware.SensorDirectChannel#close()}
+     * to free up resource in sensor system associated with the direct channel.
+     *
+     * @param mem A {@link android.os.MemoryFile} shared memory object.
+     * @return A {@link android.hardware.SensorDirectChannel} object if successful, null otherwise.
+     * @throws IllegalArgumentException when mem is null.
+     * @see SensorDirectChannel#close()
+     * @see #configureDirectChannel(SensorDirectChannel, Sensor, int)
+     */
+    public SensorDirectChannel createDirectChannel(MemoryFile mem) {
+        return createDirectChannelImpl(mem.length(), mem, null);
+    }
+
+    /**
+     * Create a sensor direct channel backed by shared memory wrapped by HardwareBuffer object.
+     *
+     * Use the returned {@link android.hardware.SensorDirectChannel} object to configure direct
+     * report of sensor events. After use, call {@link android.hardware.SensorDirectChannel#close()}
+     * to free up resource in sensor system associated with the direct channel.
+     *
+     * @param mem A {@link android.hardware.HardwareBuffer} shared memory object.
+     * @return A {@link android.hardware.SensorDirectChannel} object if successful,
+     *         null otherwise.
+     * @throws IllegalArgumentException when mem is null.
+     * @see SensorDirectChannel#close()
+     * @see #configureDirectChannel(SensorDirectChannel, Sensor, int)
+     */
+    public SensorDirectChannel createDirectChannel(HardwareBuffer mem) {
+        return null;
+    }
+
+    /** @hide */
+    protected abstract SensorDirectChannel createDirectChannelImpl(long size,
+            MemoryFile ashmemFile, HardwareBuffer hardwareBuffer);
+
+    /** @hide */
+    void destroyDirectChannel(SensorDirectChannel channel) {
+        destroyDirectChannelImpl(channel);
+    }
+
+    /** @hide */
+    protected abstract void destroyDirectChannelImpl(SensorDirectChannel channel);
+
+    /**
+     * Configure sensor rate or stop sensor report on a direct report channel specified.
+     *
+     * To start event report of a sensor, or change rate of existing report, call this function with
+     * rateLevel other than {@link android.hardware.SensorDirectChannel#RATE_STOP}. Sensor events
+     * will be added into a queue formed by the shared memory used in creation of direction channel.
+     * Each element of the queue has size of 104 bytes and represents a sensor event. Data
+     * structure of an element (all fields in little-endian):
+     *
+     *   offset   type                    name
+     *-  ---------------------------------------------
+     *   0x0000   int32_t                 size (always 104)
+     *   0x0004   int32_t                 sensor report token
+     *   0x0008   int32_t                 type (see SensorType)
+     *   0x000C   uint32_t                atomic counter
+     *   0x0010   int64_t                 timestamp (see Event)
+     *   0x0018   float[16]/int64_t[8]    data (data type depends on sensor type)
+     *   0x0058   int32_t[4]              reserved (set to zero)
+     *
+     * There is no head or tail pointers. The sequence and frontier of new sensor events is
+     * determined by the atomic counter, which counts from 1 after creation of direct channel and
+     * increments 1 for each new event. The writer in sensor system will wrap around from to
+     * start of shared memory region when it reaches the end. If size of memory region is not
+     * a multiple of size of element (104 bytes), the residual is not used at the end.
+     * Function returns a positive sensor report token on success. This token can be used for
+     * differentiate sensor events from multiple sensor of the same type. For example, if there are
+     * two accelerometer in the system A and B, it is guaranteed different report tokens will be
+     * returned when starting sensor A and B.
+     *
+     * To stop a sensor, call this function with rateLevel equal {@link
+     * android.hardware.SensorDirectChannel#RATE_STOP}. If the sensor parameter is left to be null,
+     * this will stop all active sensor report associated with the direct channel specified.
+     * Function return 1 on success or 0 on failure.
+     *
+     * @param channel A {@link android.hardware.SensorDirectChannel} object representing direct
+     *                channel to be configured.
+     * @param sensor A {@link android.hardware.Sensor} object to denote sensor to be operated.
+     * @param rateLevel rate level defined in {@link android.hardware.SensorDirectChannel}.
+     * @return starting report or changing rate: positive sensor report token on success, 0 on failure;
+     *         stopping report: 1 on success, 0 on failure.
+     * @throws IllegalArgumentException when SensorDirectChannel is null.
+     */
+    public int configureDirectChannel(SensorDirectChannel channel, Sensor sensor,
+            @SensorDirectChannel.RateLevel int rateLevel) {
+        return configureDirectChannelImpl(channel, sensor, rateLevel);
+    }
+
+    /** @hide */
+    protected abstract int configureDirectChannelImpl(
+            SensorDirectChannel channel, Sensor s, int rate);
+
+    /**
      * Used for receiving notifications from the SensorManager when dynamic sensors are connected or
      * disconnected.
      */
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 259ca03..4992def 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.MemoryFile;
 import android.os.MessageQueue;
 import android.util.Log;
 import android.util.SparseArray;
@@ -57,6 +58,13 @@
     private static native void nativeGetDynamicSensors(long nativeInstance, List<Sensor> list);
     private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
 
+    private static native int nativeCreateDirectChannel(
+            long nativeInstance, long size, int channelType, long [] channelData);
+    private static native void nativeDestroyDirectChannel(
+            long nativeInstance, int channelHandle);
+    private static native int nativeConfigDirectChannel(
+            long nativeInstance, int channelHandle, int sensorHandle, int rate);
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static boolean sNativeClassInited = false;
@@ -484,6 +492,71 @@
         return changed;
     }
 
+    /** @hide */
+    protected int configureDirectChannelImpl(
+            SensorDirectChannel channel, Sensor sensor, int rate) {
+        if (channel == null) throw new IllegalArgumentException("channel cannot be null");
+
+        if (!channel.isValid()) {
+            throw new IllegalStateException("channel is invalid");
+        }
+
+        if (rate < SensorDirectChannel.RATE_STOP
+                || rate > SensorDirectChannel.RATE_VERY_FAST) {
+            throw new IllegalArgumentException("rate parameter invalid");
+        }
+
+        if (sensor == null && rate != SensorDirectChannel.RATE_STOP) {
+            // the stop all sensors case
+            throw new IllegalArgumentException(
+                    "when sensor is null, rate can only be DIRECT_RATE_STOP");
+        }
+
+        int sensorHandle = (sensor == null) ? -1 : sensor.getHandle();
+
+        int ret = nativeConfigDirectChannel(
+                mNativeInstance, channel.getNativeHandle(), sensorHandle, rate);
+
+        if (rate == SensorDirectChannel.RATE_STOP) {
+            return (ret == 0) ? 1 : 0;
+        } else {
+            return (ret > 0) ? ret : 0;
+        }
+    }
+
+    /** @hide */
+    protected SensorDirectChannel createDirectChannelImpl(long size,
+            MemoryFile ashmemFile, HardwareBuffer grallocMemObject) {
+        SensorDirectChannel ch = null;
+
+        if (size <= 0) throw new IllegalArgumentException("size has to be greater than 0");
+
+        if (ashmemFile != null) {
+            if (size != ashmemFile.length()) {
+                throw new IllegalArgumentException("size has to match MemoryFile.length()");
+            }
+            int id = nativeCreateDirectChannel(
+                    mNativeInstance, size, SensorDirectChannel.TYPE_ASHMEM,
+                    SensorDirectChannel.encodeData(ashmemFile));
+            if (id > 0) {
+                ch = new SensorDirectChannel(this, id, SensorDirectChannel.TYPE_ASHMEM, size);
+            }
+        } else if (grallocMemObject != null) {
+            Log.wtf(TAG, "Implement GRALLOC or remove GRALLOC support entirely");
+        } else {
+            throw new IllegalArgumentException("Invalid parameter");
+        }
+
+        return ch;
+    }
+
+    /** @hide */
+    protected void destroyDirectChannelImpl(SensorDirectChannel channel) {
+        if (channel != null) {
+            nativeDestroyDirectChannel(mNativeInstance, channel.getNativeHandle());
+        }
+    }
+
     /*
      * BaseEventQueue is the communication channel with the sensor service,
      * SensorEventQueue, TriggerEventQueue are subclases and there is one-to-one mapping between
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 62b7f32..bcebb7d 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -221,8 +221,8 @@
     public abstract void tearDown(@NonNull Surface surface) throws CameraAccessException;
 
     /**
-     * <p>Finish the deferred output configurations where the output Surface was not configured
-     * before.</p>
+     * <p>Finalize the output configurations that now have their deferred and/or extra Surfaces
+     * included.</p>
      *
      * <p>For camera use cases where a preview and other output configurations need to be
      * configured, it can take some time for the preview Surface to be ready. For example, if the
@@ -235,22 +235,31 @@
      * and defer the preview output configuration until the Surface is ready. After the
      * {@link CameraCaptureSession} is created successfully with this deferred output and other
      * normal outputs, the application can start submitting requests as long as they do not include
-     * deferred output Surfaces. Once a deferred Surface is ready, the application can set the
-     * Surface on the deferred output configuration with the
-     * {@link OutputConfiguration#setDeferredSurface} method, and then finish the deferred output
+     * deferred output Surfaces. Once a deferred Surface is ready, the application can add the
+     * Surface to the deferred output configuration with the
+     * {@link OutputConfiguration#addSurface} method, and then update the deferred output
      * configuration via this method, before it can submit capture requests with this output
      * target.</p>
      *
-     * <p>The output Surfaces included by this list of deferred
+     * <p>This function can also be called in case where multiple surfaces share the same
+     * OutputConfiguration, and one of the surfaces becomes available after the {@link
+     * CameraCaptureSession} is created. In that case, the application must first create the
+     * OutputConfiguration with the available Surface, then enable furture surface sharing via
+     * {@link OutputConfiguration#enableSurfaceSharing}, before creating the CameraCaptureSession.
+     * After the CameraCaptureSession is created, and once the extra Surface becomes available, the
+     * application must then call {@link OutputConfiguration#addSurface} before finalizing the
+     * configuration with this method.</p>
+     *
+     * <p>The output Surfaces included by this list of
      * {@link OutputConfiguration OutputConfigurations} can be used as {@link CaptureRequest}
      * targets as soon as this call returns.</p>
      *
      * <p>This method is not supported by
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}-level devices.</p>
      *
-     * @param deferredOutputConfigs a list of {@link OutputConfiguration OutputConfigurations} that
-     *            have had {@link OutputConfiguration#setDeferredSurface setDeferredSurface} invoked
-     *            with a valid output Surface.
+     * @param outputConfigs a list of {@link OutputConfiguration OutputConfigurations} that
+     *            have had {@link OutputConfiguration#addSurface addSurface} invoked with a valid
+     *            output Surface after {@link CameraDevice#createCaptureSessionByOutputConfigurations}.
      * @throws CameraAccessException if the camera device is no longer connected or has encountered
      *             a fatal error.
      * @throws IllegalStateException if this session is no longer active, either because the session
@@ -261,8 +270,8 @@
      *             source. Or if one of the output configuration was already finished with an
      *             included surface in a prior call.
      */
-    public abstract void finishDeferredConfiguration(
-            List<OutputConfiguration> deferredOutputConfigs) throws CameraAccessException;
+    public abstract void finalizeOutputConfigurations(
+            List<OutputConfiguration> outputConfigs) throws CameraAccessException;
 
     /**
      * <p>Submit a request for an image to be captured by the camera device.</p>
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 98a8904..12b46c1 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1625,6 +1625,34 @@
             new Key<Integer>("android.control.postRawSensitivityBoost", int.class);
 
     /**
+     * <p>Allow camera device to enable zero-shutter-lag mode for requests with
+     * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE.</p>
+     * <p>If enableZsl is <code>true</code>, the camera device may enable zero-shutter-lag mode for requests with
+     * STILL_CAPTURE capture intent. The camera device may use images captured in the past to
+     * produce output images for a zero-shutter-lag request. The result metadata including the
+     * {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} reflects the source frames used to produce output images.
+     * Therefore, the contents of the output images and the result metadata may be out of order
+     * compared to previous regular requests. enableZsl does not affect requests with other
+     * capture intents.</p>
+     * <p>For example, when requests are submitted in the following order:
+     *   Request A: enableZsl is ON, {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} is PREVIEW
+     *   Request B: enableZsl is ON, {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} is STILL_CAPTURE</p>
+     * <p>The output images for request B may have contents captured before the output images for
+     * request A, and the result metadata for request B may be older than the result metadata for
+     * request A.</p>
+     * <p>Note that when enableZsl is <code>true</code>, it is not guaranteed to get output images captured in the
+     * past for requests with STILL_CAPTURE capture intent.</p>
+     * <p>The value of enableZsl in capture templates is always <code>false</code> if present.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_CAPTURE_INTENT
+     * @see CaptureResult#SENSOR_TIMESTAMP
+     */
+    @PublicKey
+    public static final Key<Boolean> CONTROL_ENABLE_ZSL =
+            new Key<Boolean>("android.control.enableZsl", boolean.class);
+
+    /**
      * <p>Operation mode for edge
      * enhancement.</p>
      * <p>Edge enhancement improves sharpness and details in the captured image. OFF means
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 23f4b9e..3f8b57a 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2132,6 +2132,34 @@
             new Key<Integer>("android.control.postRawSensitivityBoost", int.class);
 
     /**
+     * <p>Allow camera device to enable zero-shutter-lag mode for requests with
+     * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE.</p>
+     * <p>If enableZsl is <code>true</code>, the camera device may enable zero-shutter-lag mode for requests with
+     * STILL_CAPTURE capture intent. The camera device may use images captured in the past to
+     * produce output images for a zero-shutter-lag request. The result metadata including the
+     * {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} reflects the source frames used to produce output images.
+     * Therefore, the contents of the output images and the result metadata may be out of order
+     * compared to previous regular requests. enableZsl does not affect requests with other
+     * capture intents.</p>
+     * <p>For example, when requests are submitted in the following order:
+     *   Request A: enableZsl is ON, {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} is PREVIEW
+     *   Request B: enableZsl is ON, {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} is STILL_CAPTURE</p>
+     * <p>The output images for request B may have contents captured before the output images for
+     * request A, and the result metadata for request B may be older than the result metadata for
+     * request A.</p>
+     * <p>Note that when enableZsl is <code>true</code>, it is not guaranteed to get output images captured in the
+     * past for requests with STILL_CAPTURE capture intent.</p>
+     * <p>The value of enableZsl in capture templates is always <code>false</code> if present.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_CAPTURE_INTENT
+     * @see CaptureResult#SENSOR_TIMESTAMP
+     */
+    @PublicKey
+    public static final Key<Boolean> CONTROL_ENABLE_ZSL =
+            new Key<Boolean>("android.control.enableZsl", boolean.class);
+
+    /**
      * <p>Operation mode for edge
      * enhancement.</p>
      * <p>Edge enhancement improves sharpness and details in the captured image. OFF means
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 5e9fd66..891df63 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -48,8 +48,6 @@
 
     /** Input surface configured by native camera framework based on user-specified configuration */
     private final Surface mInput;
-    /** User-specified set of surfaces used as the configuration outputs */
-    private final List<Surface> mOutputs;
     /**
      * User-specified state callback, used for outgoing events; calls to this object will be
      * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
@@ -87,21 +85,17 @@
      * There must be no pending actions
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
-    CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs,
+    CameraCaptureSessionImpl(int id, Surface input,
             CameraCaptureSession.StateCallback callback, Handler stateHandler,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
             Handler deviceStateHandler, boolean configureSuccess) {
-        if (outputs == null || outputs.isEmpty()) {
-            throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
-        } else if (callback == null) {
+        if (callback == null) {
             throw new IllegalArgumentException("callback must not be null");
         }
 
         mId = id;
         mIdString = String.format("Session %d: ", mId);
 
-        // TODO: extra verification of outputs
-        mOutputs = outputs;
         mInput = input;
         mStateHandler = checkHandler(stateHandler);
         mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
@@ -157,9 +151,9 @@
     }
 
     @Override
-    public void finishDeferredConfiguration(
-            List<OutputConfiguration> deferredOutputConfigs) throws CameraAccessException {
-        mDeviceImpl.finishDeferredConfig(deferredOutputConfigs);
+    public void finalizeOutputConfigurations(
+            List<OutputConfiguration> outputConfigs) throws CameraAccessException {
+        mDeviceImpl.finalizeOutputConfigs(outputConfigs);
     }
 
     @Override
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 4481a74..15dbf26 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -58,14 +58,14 @@
      * There must be no pending actions
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
-    CameraConstrainedHighSpeedCaptureSessionImpl(int id, List<Surface> outputs,
+    CameraConstrainedHighSpeedCaptureSessionImpl(int id,
             CameraCaptureSession.StateCallback callback, Handler stateHandler,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
             Handler deviceStateHandler, boolean configureSuccess,
             CameraCharacteristics characteristics) {
         mCharacteristics = characteristics;
         CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
-        mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, outputs, wrapperCallback,
+        mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
                 stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
     }
 
@@ -258,9 +258,9 @@
     }
 
     @Override
-    public void finishDeferredConfiguration(List<OutputConfiguration> deferredOutputConfigs)
+    public void finalizeOutputConfigurations(List<OutputConfiguration> deferredOutputConfigs)
             throws CameraAccessException {
-        mSessionImpl.finishDeferredConfiguration(deferredOutputConfigs);
+        mSessionImpl.finalizeOutputConfigurations(deferredOutputConfigs);
     }
 
     private class WrapperCallback extends StateCallback {
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 97ca56ef..2364ebe 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -501,7 +501,7 @@
             CameraCaptureSession.StateCallback callback, Handler handler)
             throws CameraAccessException {
         if (DEBUG) {
-            Log.d(TAG, "createCaptureSessionByOutputConfiguration");
+            Log.d(TAG, "createCaptureSessionByOutputConfigurations");
         }
 
         // OutputConfiguration objects are immutable, but need to have our own array
@@ -621,19 +621,15 @@
                 }
             }
 
-            List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
-            for (OutputConfiguration config : outputConfigurations) {
-                outSurfaces.add(config.getSurface());
-            }
             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
             CameraCaptureSessionCore newSession = null;
             if (isConstrainedHighSpeed) {
                 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
-                        outSurfaces, callback, handler, this, mDeviceHandler, configureSuccess,
+                        callback, handler, this, mDeviceHandler, configureSuccess,
                         mCharacteristics);
             } else {
                 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
-                        outSurfaces, callback, handler, this, mDeviceHandler,
+                        callback, handler, this, mDeviceHandler,
                         configureSuccess);
             }
 
@@ -747,14 +743,14 @@
         }
     }
 
-    public void finishDeferredConfig(List<OutputConfiguration> deferredConfigs)
+    public void finalizeOutputConfigs(List<OutputConfiguration> outputConfigs)
             throws CameraAccessException {
-        if (deferredConfigs == null || deferredConfigs.size() == 0) {
+        if (outputConfigs == null || outputConfigs.size() == 0) {
             throw new IllegalArgumentException("deferred config is null or empty");
         }
 
         synchronized(mInterfaceLock) {
-            for (OutputConfiguration config : deferredConfigs) {
+            for (OutputConfiguration config : outputConfigs) {
                 int streamId = -1;
                 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
                     // Have to use equal here, as createCaptureSessionByOutputConfigurations() and
@@ -769,11 +765,11 @@
                             + "session");
                 }
 
-                if (config.getSurface() == null) {
-                    throw new IllegalArgumentException("The deferred config for stream " + streamId
-                            + " must have a non-null surface");
+                if (config.getSurfaces().size() == 0) {
+                    throw new IllegalArgumentException("The final config for stream " + streamId
+                            + " must have at least 1 surface");
                 }
-                mRemoteDevice.setDeferredConfiguration(streamId, config);
+                mRemoteDevice.finalizeOutputConfigurations(streamId, config);
             }
         }
     }
@@ -1946,24 +1942,33 @@
 
             Runnable failureDispatch = null;
             if (errorCode == ERROR_CAMERA_BUFFER) {
-                final Surface outputSurface =
-                        mConfiguredOutputs.get(resultExtras.getErrorStreamId()).getSurface();
-                if (DEBUG) {
-                    Log.v(TAG, String.format("Lost output buffer reported for frame %d, target %s",
-                            frameNumber, outputSurface));
-                }
-                failureDispatch = new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!CameraDeviceImpl.this.isClosed()){
-                            holder.getCallback().onCaptureBufferLost(
-                                CameraDeviceImpl.this,
-                                request,
-                                outputSurface,
-                                frameNumber);
-                        }
+                // Because 1 stream id could map to multiple surfaces, we need to specify both
+                // streamId and surfaceId.
+                List<Surface> surfaces =
+                        mConfiguredOutputs.get(resultExtras.getErrorStreamId()).getSurfaces();
+                for (Surface surface : surfaces) {
+                    if (!request.containsTarget(surface)) {
+                        continue;
                     }
-                };
+                    if (DEBUG) {
+                        Log.v(TAG, String.format("Lost output buffer reported for frame %d, target %s",
+                                frameNumber, surface));
+                    }
+                    failureDispatch = new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!CameraDeviceImpl.this.isClosed()){
+                                holder.getCallback().onCaptureBufferLost(
+                                    CameraDeviceImpl.this,
+                                    request,
+                                    surface,
+                                    frameNumber);
+                            }
+                        }
+                    };
+                    // Dispatch the failure callback
+                    holder.getHandler().post(failureDispatch);
+                }
             } else {
                 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
 
@@ -2000,10 +2005,11 @@
                 }
                 mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
                 checkAndFireSequenceComplete();
+
+                // Dispatch the failure callback
+                holder.getHandler().post(failureDispatch);
             }
 
-            // Dispatch the failure callback
-            holder.getHandler().post(failureDispatch);
         }
 
     } // public class CameraDeviceCallbacks
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index d77f60b..d9f666e 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -215,10 +215,10 @@
         }
     }
 
-    public void setDeferredConfiguration(int streamId, OutputConfiguration deferredConfig)
+    public void finalizeOutputConfigurations(int streamId, OutputConfiguration deferredConfig)
             throws CameraAccessException {
         try {
-            mRemoteDevice.setDeferredConfiguration(streamId, deferredConfig);
+            mRemoteDevice.finalizeOutputConfigurations(streamId, deferredConfig);
         } catch (Throwable t) {
             CameraManager.throwAsPublicException(t);
             throw new UnsupportedOperationException("Unexpected exception", t);
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 2a9bf6b..d8ec4df 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -578,8 +578,8 @@
     }
 
     @Override
-    public void setDeferredConfiguration(int steamId, OutputConfiguration config) {
-        String err = "Set deferred configuration is not supported on legacy devices";
+    public void finalizeOutputConfigurations(int steamId, OutputConfiguration config) {
+        String err = "Finalizing output configuration is not supported on legacy devices";
         Log.e(TAG, err);
         throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
     }
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index f897d85..6b7546f 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -31,13 +31,18 @@
 import android.util.Size;
 import android.view.Surface;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Collections;
+import java.util.ArrayList;
+
 import static com.android.internal.util.Preconditions.*;
 
 /**
  * A class for describing camera output, which contains a {@link Surface} and its specific
  * configuration for creating capture session.
  *
- * @see CameraDevice#createCaptureSessionByOutputConfiguration
+ * @see CameraDevice#createCaptureSessionByOutputConfigurations
  *
  */
 public final class OutputConfiguration implements Parcelable {
@@ -112,6 +117,15 @@
     private final int SURFACE_TYPE_SURFACE_TEXTURE = 1;
 
     /**
+     * Maximum number of surfaces supported by one {@link OutputConfiguration}.
+     *
+     * <p>The combined number of surfaces added by the constructor and
+     * {@link OutputConfiguration#addSurface} should not exceed this value.</p>
+     *
+     */
+    private static final int MAX_SURFACES_COUNT = 2;
+
+    /**
      * Create a new {@link OutputConfiguration} instance with a {@link Surface},
      * with a surface group ID.
      *
@@ -169,7 +183,6 @@
         this(SURFACE_GROUP_ID_NONE, surface, rotation);
     }
 
-
     /**
      * Create a new {@link OutputConfiguration} instance, with rotation and a group ID.
      *
@@ -197,13 +210,15 @@
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
         mSurfaceGroupId = surfaceGroupId;
         mSurfaceType = SURFACE_TYPE_UNKNOWN;
-        mSurface = surface;
+        mSurfaces = new ArrayList<Surface>();
+        mSurfaces.add(surface);
         mRotation = rotation;
         mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
         mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
         mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
         mConfiguredGenerationId = surface.getGenerationId();
         mIsDeferredConfig = false;
+        mIsShared = false;
     }
 
     /**
@@ -211,16 +226,16 @@
      * source class.
      * <p>
      * This constructor takes an argument for desired Surface size and the Surface source class
-     * without providing the actual output Surface. This is used to setup a output configuration
+     * without providing the actual output Surface. This is used to setup an output configuration
      * with a deferred Surface. The application can use this output configuration to create a
      * session.
      * </p>
      * <p>
-     * However, the actual output Surface must be set via {@link #setDeferredSurface} and finish the
-     * deferred Surface configuration via {@link CameraCaptureSession#finishDeferredConfiguration}
-     * before submitting a request with this Surface target. The deferred Surface can only be
-     * obtained from either from {@link android.view.SurfaceView} by calling
-     * {@link android.view.SurfaceHolder#getSurface}, or from
+     * However, the actual output Surface must be set via {@link #addSurface} and the deferred
+     * Surface configuration must be finalized via {@link
+     * CameraCaptureSession#finalizeOutputConfigurations} before submitting a request with this
+     * Surface target. The deferred Surface can only be obtained either from {@link
+     * android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface}, or from
      * {@link android.graphics.SurfaceTexture} via
      * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).
      * </p>
@@ -243,20 +258,56 @@
         }
 
         mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
-        mSurface = null;
+        mSurfaces = new ArrayList<Surface>();
         mRotation = ROTATION_0;
         mConfiguredSize = surfaceSize;
         mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
         mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
         mConfiguredGenerationId = 0;
         mIsDeferredConfig = true;
+        mIsShared = false;
+    }
+
+    /**
+     * Enable multiple surfaces sharing the same OutputConfiguration
+     *
+     * <p>For advanced use cases, a camera application may require more streams than the combination
+     * guaranteed by {@link CameraDevice#createCaptureSession}. In this case, more than one
+     * compatible surface can be attached to an OutputConfiguration so that they map to one
+     * camera stream, and the outputs share memory buffers when possible. </p>
+     *
+     * <p>Two surfaces are compatible in the below cases:</p>
+     *
+     * <li> Surfaces with the same size, format, dataSpace, and Surface source class. In this case,
+     * {@link CameraDevice#createCaptureSessionByOutputConfigurations} is guaranteed to succeed.
+     *
+     * <li> Surfaces with the same size, format, and dataSpace, but different Surface source classes
+     * that are generally not compatible. However, on some devices, the underlying camera device is
+     * able to use the same buffer layout for both surfaces. The only way to discover if this is the
+     * case is to create a capture session with that output configuration. For example, if the
+     * camera device uses the same private buffer format between a SurfaceView/SurfaceTexture and a
+     * MediaRecorder/MediaCodec, {@link CameraDevice#createCaptureSessionByOutputConfigurations}
+     * will succeed. Otherwise, it throws {@code IllegalArgumentException}.
+     * </ol>
+     *
+     * <p>To enable surface sharing, this function must be called before {@link
+     * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link
+     * CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
+     *
+     * <p>Up to 2 surfaces can be shared for an OutputConfiguration. The supported surfaces for
+     * sharing must be of type SurfaceTexture, SurfaceView, MediaRecorder, MediaCodec, or
+     * implementation defined ImageReader.</p>
+     */
+    public void enableSurfaceSharing() {
+        mIsShared = true;
     }
 
     /**
      * Check if this configuration has deferred configuration.
      *
-     * <p>This will return true if the output configuration was constructed with surface deferred.
-     * It will return true even after the deferred surface is set later.</p>
+     * <p>This will return true if the output configuration was constructed with surface deferred by
+     * {@link OutputConfiguration#OutputConfiguration(Size, Class)}. It will return true even after
+     * the deferred surface is added later by {@link OutputConfiguration#addSurface}.</p>
      *
      * @return true if this configuration has deferred surface.
      * @hide
@@ -266,38 +317,63 @@
     }
 
     /**
-     * Set the deferred surface to this OutputConfiguration.
+     * Add a surface to this OutputConfiguration.
      *
-     * <p>
-     * The deferred surface must be obtained from either from {@link android.view.SurfaceView} by
-     * calling {@link android.view.SurfaceHolder#getSurface}, or from
-     * {@link android.graphics.SurfaceTexture} via
-     * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). After the deferred
-     * surface is set, the application must finish the deferred surface configuration via
-     * {@link CameraCaptureSession#finishDeferredConfiguration} before submitting a request with
-     * this surface target.
+     * <p> This function can be called before or after {@link
+     * CameraDevice#createCaptureSessionByOutputConfigurations}. If it's called after,
+     * the application must finalize the capture session with
+     * {@link CameraCaptureSession#finalizeOutputConfigurations}.
      * </p>
      *
-     * @param surface The deferred surface to be set.
-     * @throws IllegalArgumentException if the Surface is invalid.
-     * @throws IllegalStateException if a Surface was already set to this deferred
-     *         OutputConfiguration.
+     * <p> If the OutputConfiguration was constructed with a deferred surface by {@link
+     * OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained
+     * from {@link android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface},
+     * or from {@link android.graphics.SurfaceTexture} via
+     * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).</p>
+     *
+     * <p> If the OutputConfiguration was constructed by other constructors, the added
+     * surface must be compatible with the existing surface. See {@link #enableSurfaceSharing} for
+     * details of compatible surfaces.</p>
+     *
+     * <p> If the OutputConfiguration already contains a Surface, {@link #enableSurfaceSharing} must
+     * be called before calling this function to add a new Surface.</p>
+     *
+     * @param surface The surface to be added.
+     * @throws IllegalArgumentException if the Surface is invalid, the Surface's
+     *         size/dataspace/format doesn't match, or adding the Surface would exceed number of
+     *         shared surfaces supported.
+     * @throws IllegalStateException if the Surface was already added to this OutputConfiguration,
+     *         or if the OutputConfiguration is not shared and it already has a surface associated
+     *         with it.
      */
-    public void setDeferredSurface(@NonNull Surface surface) {
+    public void addSurface(@NonNull Surface surface) {
         checkNotNull(surface, "Surface must not be null");
-        if (mSurface != null) {
-            throw new IllegalStateException("Deferred surface is already set!");
+        if (mSurfaces.contains(surface)) {
+            throw new IllegalStateException("Surface is already added!");
+        }
+        if (mSurfaces.size() == 1 && !mIsShared) {
+            throw new IllegalStateException("Cannot have 2 surfaces for a non-sharing configuration");
+        }
+        if (mSurfaces.size() + 1 > MAX_SURFACES_COUNT) {
+            throw new IllegalArgumentException("Exceeds maximum number of surfaces");
         }
 
-        // This will throw IAE is the surface was abandoned.
-        Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
-        if (!surfaceSize.equals(mConfiguredSize)) {
-            Log.w(TAG, "Deferred surface size " + surfaceSize +
-                    " is different with pre-configured size " + mConfiguredSize +
-                    ", the pre-configured size will be used.");
+        // TODO: b/34697112. This needs to be reverted once app fix is merged.
+        // Do not throw exception for below case:
+        // - OutputConfiguration(Size(0, 0), klass)
+        // - addSurface(surface)
+        if ((mConfiguredSize.getWidth() != 0 || mConfiguredSize.getHeight() != 0) &&
+                !mConfiguredSize.equals(SurfaceUtils.getSurfaceSize(surface))) {
+            throw new IllegalArgumentException("The size of added surface doesn't match");
+        }
+        if (mConfiguredDataspace != SurfaceUtils.getSurfaceDataspace(surface)) {
+            throw new IllegalArgumentException("The dataspace of added surface doesn't match");
+        }
+        if (mConfiguredFormat != SurfaceUtils.getSurfaceFormat(surface)) {
+            throw new IllegalArgumentException("The format of added surface format doesn't match");
         }
 
-        mSurface = surface;
+        mSurfaces.add(surface);
     }
 
     /**
@@ -313,7 +389,7 @@
             throw new IllegalArgumentException("OutputConfiguration shouldn't be null");
         }
 
-        this.mSurface = other.mSurface;
+        this.mSurfaces = other.mSurfaces;
         this.mRotation = other.mRotation;
         this.mSurfaceGroupId = other.mSurfaceGroupId;
         this.mSurfaceType = other.mSurfaceType;
@@ -333,37 +409,55 @@
         int surfaceType = source.readInt();
         int width = source.readInt();
         int height = source.readInt();
-        Surface surface = Surface.CREATOR.createFromParcel(source);
+        boolean isDeferred = source.readInt() == 1;
+        ArrayList<Surface> surfaces = new ArrayList<Surface>();
+        source.readTypedList(surfaces, Surface.CREATOR);
+
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
+
         mSurfaceGroupId = surfaceSetId;
-        mSurface = surface;
         mRotation = rotation;
-        if (surface != null) {
+        mSurfaces = surfaces;
+        mConfiguredSize = new Size(width, height);
+        mIsDeferredConfig = isDeferred;
+        mSurfaces = surfaces;
+        if (mSurfaces.size() > 0) {
             mSurfaceType = SURFACE_TYPE_UNKNOWN;
-            mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
-            mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
-            mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
-            mConfiguredGenerationId = mSurface.getGenerationId();
-            mIsDeferredConfig = true;
+            mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurfaces.get(0));
+            mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurfaces.get(0));
+            mConfiguredGenerationId = mSurfaces.get(0).getGenerationId();
         } else {
             mSurfaceType = surfaceType;
-            mConfiguredSize = new Size(width, height);
             mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
-            mConfiguredGenerationId = 0;
             mConfiguredDataspace =
                     StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
-            mIsDeferredConfig = false;
+            mConfiguredGenerationId = 0;
         }
     }
 
     /**
      * Get the {@link Surface} associated with this {@link OutputConfiguration}.
      *
-     * @return the {@link Surface} associated with this {@link OutputConfiguration}.
+     * If more than one surface is associated with this {@link OutputConfiguration}, return the
+     * first one as specified in the constructor or {@link OutputConfiguration#addSurface}.
+     */
+    public @Nullable Surface getSurface() {
+        if (mSurfaces.size() == 0) {
+            return null;
+        }
+
+        return mSurfaces.get(0);
+    }
+
+    /**
+     * Get the immutable list of surfaces associated with this {@link OutputConfiguration}.
+     *
+     * @return the list of surfaces associated with this {@link OutputConfiguration} as specified in
+     * the constructor and {@link OutputConfiguration#addSurface}. The list should not be modified.
      */
     @NonNull
-    public Surface getSurface() {
-        return mSurface;
+    public List<Surface> getSurfaces() {
+        return Collections.unmodifiableList(mSurfaces);
     }
 
     /**
@@ -423,9 +517,9 @@
         dest.writeInt(mSurfaceType);
         dest.writeInt(mConfiguredSize.getWidth());
         dest.writeInt(mConfiguredSize.getHeight());
-        if (mSurface != null) {
-            mSurface.writeToParcel(dest, flags);
-        }
+        dest.writeInt(mIsDeferredConfig ? 1 : 0);
+        dest.writeInt(mIsShared ? 1 : 0);
+        dest.writeTypedList(mSurfaces);
     }
 
     /**
@@ -445,20 +539,25 @@
             return true;
         } else if (obj instanceof OutputConfiguration) {
             final OutputConfiguration other = (OutputConfiguration) obj;
-            boolean iSSurfaceEqual = mSurface == other.mSurface &&
-                    mConfiguredGenerationId == other.mConfiguredGenerationId ;
-            if (mIsDeferredConfig) {
-                Log.i(TAG, "deferred config has the same surface");
-                iSSurfaceEqual = true;
+            if (mRotation != other.mRotation ||
+                    !mConfiguredSize.equals(other.mConfiguredSize) ||
+                    mConfiguredFormat != other.mConfiguredFormat ||
+                    mSurfaceGroupId != other.mSurfaceGroupId ||
+                    mSurfaceType != other.mSurfaceType ||
+                    mIsDeferredConfig != other.mIsDeferredConfig ||
+                    mIsShared != other.mIsShared ||
+                    mConfiguredFormat != other.mConfiguredFormat ||
+                    mConfiguredDataspace != other.mConfiguredDataspace ||
+                    mConfiguredGenerationId != other.mConfiguredGenerationId)
+                return false;
+
+            int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
+            for (int i = 0;  i < minLen; i++) {
+                if (mSurfaces.get(i) != other.mSurfaces.get(i))
+                    return false;
             }
-            return mRotation == other.mRotation &&
-                   iSSurfaceEqual&&
-                   mConfiguredSize.equals(other.mConfiguredSize) &&
-                   mConfiguredFormat == other.mConfiguredFormat &&
-                   mConfiguredDataspace == other.mConfiguredDataspace &&
-                   mSurfaceGroupId == other.mSurfaceGroupId &&
-                   mSurfaceType == other.mSurfaceType &&
-                   mIsDeferredConfig == other.mIsDeferredConfig;
+
+            return true;
         }
         return false;
     }
@@ -468,22 +567,23 @@
      */
     @Override
     public int hashCode() {
-        // Need ensure that the hashcode remains unchanged after set a deferred surface. Otherwise
-        // The deferred output configuration will be lost in the camera streammap after the deferred
+        // Need ensure that the hashcode remains unchanged after adding a deferred surface. Otherwise
+        // the deferred output configuration will be lost in the camera streammap after the deferred
         // surface is set.
         if (mIsDeferredConfig) {
             return HashCodeHelpers.hashCode(
                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
-                    mSurfaceGroupId, mSurfaceType);
+                    mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0);
         }
 
         return HashCodeHelpers.hashCode(
-            mRotation, mSurface.hashCode(), mConfiguredGenerationId,
-            mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace, mSurfaceGroupId);
+                mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
+                mConfiguredSize.hashCode(), mConfiguredFormat,
+                mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0);
     }
 
     private static final String TAG = "OutputConfiguration";
-    private Surface mSurface;
+    private ArrayList<Surface> mSurfaces;
     private final int mRotation;
     private final int mSurfaceGroupId;
     // Surface source type, this is only used by the deferred surface configuration objects.
@@ -497,4 +597,6 @@
     private final int mConfiguredGenerationId;
     // Flag indicating if this config has deferred surface.
     private final boolean mIsDeferredConfig;
+    // Flag indicating if this config has shared surfaces
+    private boolean mIsShared;
 }
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index aea1258..3eb5844 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -19,6 +19,8 @@
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.util.IntArray;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 
@@ -147,6 +149,21 @@
     public abstract void setDisplayOffsets(int displayId, int x, int y);
 
     /**
+     * Provide a list of UIDs that are present on the display and are allowed to access it.
+     *
+     * @param displayAccessUIDs Mapping displayId -> int array of UIDs.
+     */
+    public abstract void setDisplayAccessUIDs(SparseArray<IntArray> displayAccessUIDs);
+
+    /**
+     * Check if specified UID's content is present on display and should be granted access to it.
+     *
+     * @param uid UID to be checked.
+     * @param displayId id of the display where presence of the content is checked.
+     * */
+    public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
+
+    /**
      * Describes the requested power state of the display.
      *
      * This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 4b57078..2c9e6c7 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1074,7 +1074,7 @@
 
         @Override // binder call
         public void onAuthenticationFailed(long deviceId) {
-            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();;
+            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
         }
 
         @Override // binder call
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index a9c09c4..bdb278b 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -81,4 +81,6 @@
 
     void setPointerIconType(int typeId);
     void setCustomPointerIcon(in PointerIcon icon);
+
+    void requestPointerCapture(IBinder windowToken, boolean enabled);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2b0593f..6e202b0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -40,6 +40,7 @@
 import android.util.SparseArray;
 import android.view.InputDevice;
 import android.view.InputEvent;
+import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
@@ -898,6 +899,25 @@
         }
     }
 
+    /**
+     * Request or release pointer capture.
+     * <p>
+     * When in capturing mode, the pointer icon disappears and all mouse events are dispatched to
+     * the window which has requested the capture. Relative position changes are available through
+     * {@link MotionEvent#getX} and {@link MotionEvent#getY}.
+     *
+     * @param enable true when requesting pointer capture, false when releasing.
+     *
+     * @hide
+     */
+    public void requestPointerCapture(IBinder windowToken, boolean enable) {
+        try {
+            mIm.requestPointerCapture(windowToken, enable);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     private void populateInputDevicesLocked() {
         if (mInputDevicesChangedListener == null) {
             final InputDevicesChangedListener listener = new InputDevicesChangedListener();
diff --git a/core/java/android/hardware/usb/UsbEndpoint.java b/core/java/android/hardware/usb/UsbEndpoint.java
index 708d651..c346700 100644
--- a/core/java/android/hardware/usb/UsbEndpoint.java
+++ b/core/java/android/hardware/usb/UsbEndpoint.java
@@ -75,8 +75,8 @@
      * if the direction is host to device, and
      * {@link UsbConstants#USB_DIR_IN} if the
      * direction is device to host.
-     * @see {@link UsbConstants#USB_DIR_IN}
-     * @see {@link UsbConstants#USB_DIR_OUT}
+     * @see UsbConstants#USB_DIR_IN
+     * @see UsbConstants#USB_DIR_OUT
      *
      * @return the endpoint's direction
      */
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index c9a4e9b..fea730e 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,11 +16,12 @@
 
 package android.hardware.usb;
 
-import com.android.internal.util.Preconditions;
-
+import android.hardware.usb.V1_0.Constants;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 /**
  * Represents a physical USB port and describes its characteristics.
  * <p>
@@ -33,6 +34,7 @@
     private final String mId;
     private final int mSupportedModes;
 
+    public static final int MODE_NONE = Constants.PortMode.NONE;
     /**
      * Mode bit: This USB port can act as a downstream facing port (host).
      * <p>
@@ -40,7 +42,7 @@
      * combination of roles (and possibly others as well).
      * </p>
      */
-    public static final int MODE_DFP = 1 << 0;
+    public static final int MODE_DFP = Constants.PortMode.DFP;
 
     /**
      * Mode bit: This USB port can act as an upstream facing port (device).
@@ -49,7 +51,7 @@
      * combination of roles (and possibly others as well).
      * </p>
      */
-    public static final int MODE_UFP = 1 << 1;
+    public static final int MODE_UFP = Constants.PortMode.UFP;
 
     /**
      * Mode bit: This USB port can act either as an downstream facing port (host) or as
@@ -60,29 +62,43 @@
      * combination of roles (and possibly others as well).
      * </p>
      */
-    public static final int MODE_DUAL = MODE_DFP | MODE_UFP;
+    public static final int MODE_DUAL = Constants.PortMode.DRP;
+
+    /**
+     * Power role: This USB port does not have a power role.
+     */
+    public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
 
     /**
      * Power role: This USB port can act as a source (provide power).
      */
-    public static final int POWER_ROLE_SOURCE = 1;
+    public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
 
     /**
      * Power role: This USB port can act as a sink (receive power).
      */
-    public static final int POWER_ROLE_SINK = 2;
+    public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+
+    /**
+     * Power role: This USB port does not have a data role.
+     */
+    public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
 
     /**
      * Data role: This USB port can act as a host (access data services).
      */
-    public static final int DATA_ROLE_HOST = 1;
+    public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
 
     /**
      * Data role: This USB port can act as a device (offer data services).
      */
-    public static final int DATA_ROLE_DEVICE = 2;
+    public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
 
-    private static final int NUM_DATA_ROLES = 3;
+    private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES;
+    /**
+     * Points to the first power role in the IUsb HAL.
+     */
+    private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
 
     /** @hide */
     public UsbPort(String id, int supportedModes) {
@@ -126,14 +142,14 @@
      */
     public static int combineRolesAsBit(int powerRole, int dataRole) {
         checkRoles(powerRole, dataRole);
-        final int index = powerRole * NUM_DATA_ROLES + dataRole;
+        final int index = ((powerRole - POWER_ROLE_OFFSET) * NUM_DATA_ROLES) + dataRole;
         return 1 << index;
     }
 
     /** @hide */
     public static String modeToString(int mode) {
         switch (mode) {
-            case 0:
+            case MODE_NONE:
                 return "none";
             case MODE_DFP:
                 return "dfp";
@@ -149,7 +165,7 @@
     /** @hide */
     public static String powerRoleToString(int role) {
         switch (role) {
-            case 0:
+            case POWER_ROLE_NONE:
                 return "no-power";
             case POWER_ROLE_SOURCE:
                 return "source";
@@ -163,7 +179,7 @@
     /** @hide */
     public static String dataRoleToString(int role) {
         switch (role) {
-            case 0:
+            case DATA_ROLE_NONE:
                 return "no-data";
             case DATA_ROLE_HOST:
                 return "host";
@@ -183,7 +199,7 @@
         while (combo != 0) {
             final int index = Integer.numberOfTrailingZeros(combo);
             combo &= ~(1 << index);
-            final int powerRole = index / NUM_DATA_ROLES;
+            final int powerRole = (index / NUM_DATA_ROLES + POWER_ROLE_OFFSET);
             final int dataRole = index % NUM_DATA_ROLES;
             if (first) {
                 first = false;
@@ -200,9 +216,28 @@
     }
 
     /** @hide */
+    public static void checkMode(int powerRole) {
+        Preconditions.checkArgumentInRange(powerRole, Constants.PortMode.NONE,
+                Constants.PortMode.NUM_MODES - 1, "portMode");
+    }
+
+    /** @hide */
+    public static void checkPowerRole(int dataRole) {
+        Preconditions.checkArgumentInRange(dataRole, Constants.PortPowerRole.NONE,
+                Constants.PortPowerRole.NUM_POWER_ROLES - 1, "powerRole");
+    }
+
+    /** @hide */
+    public static void checkDataRole(int mode) {
+        Preconditions.checkArgumentInRange(mode, Constants.PortDataRole.NONE,
+                Constants.PortDataRole.NUM_DATA_ROLES - 1, "powerRole");
+    }
+
+    /** @hide */
     public static void checkRoles(int powerRole, int dataRole) {
-        Preconditions.checkArgumentInRange(powerRole, 0, POWER_ROLE_SINK, "powerRole");
-        Preconditions.checkArgumentInRange(dataRole, 0, DATA_ROLE_DEVICE, "dataRole");
+        Preconditions.checkArgumentInRange(powerRole, POWER_ROLE_NONE, POWER_ROLE_SINK,
+                "powerRole");
+        Preconditions.checkArgumentInRange(dataRole, DATA_ROLE_NONE, DATA_ROLE_DEVICE, "dataRole");
     }
 
     @Override
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index c6338cb..beed6e9 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2367,16 +2367,16 @@
         }
         return true;
     }
-    
+
     /**
      * Return text that can be used as a button label for the given
      * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.  Returns null
      * if there is no action requested.  Note that there is no guarantee that
      * the returned text will be relatively short, so you probably do not
      * want to use it as text on a soft keyboard key label.
-     * 
-     * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
-     * 
+     *
+     * @param imeOptions The value from {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
+     *
      * @return Returns a label to use, or null if there is no action.
      */
     public CharSequence getTextForImeAction(int imeOptions) {
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
new file mode 100644
index 0000000..2bf841c
--- /dev/null
+++ b/core/java/android/metrics/LogMaker.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.metrics;
+
+import android.annotation.SystemApi;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+
+
+/**
+ * Helper class to assemble more complex logs.
+ *
+ * @hide
+ */
+@SystemApi
+public class LogMaker {
+    private static final String TAG = "LogBuilder";
+
+    /**
+     * Min required eventlog line length.
+     * See: android/util/cts/EventLogTest.java
+     * Size checks enforced here are intended only as sanity checks;
+     * your logs may be truncated earlier. Please log responsibly.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static final int MAX_SERIALIZED_SIZE = 4000;
+
+    private SparseArray<Object> entries = new SparseArray();
+
+    public LogMaker(int mainCategory) {
+        setCategory(mainCategory);
+    }
+
+    /* Deserialize from the eventlog */
+    public LogMaker(Object[] items) {
+      deserialize(items);
+    }
+
+    public LogMaker setCategory(int category) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_CATEGORY, category);
+        return this;
+    }
+
+    public LogMaker setType(int type) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_TYPE, type);
+        return this;
+    }
+
+    public LogMaker setSubtype(int subtype) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE, subtype);
+        return this;
+    }
+
+    public LogMaker setTimestamp(long timestamp) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_TIMESTAMP, timestamp);
+        return this;
+    }
+
+    public LogMaker setPackageName(String packageName) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_PACKAGENAME, packageName);
+        return this;
+    }
+
+    public LogMaker setCounterName(String name) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_NAME, name);
+        return this;
+    }
+
+    public LogMaker setCounterBucket(int bucket) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET, bucket);
+        return this;
+    }
+
+    public LogMaker setCounterBucket(long bucket) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET, bucket);
+        return this;
+    }
+
+    public LogMaker setCounterValue(int value) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_VALUE, value);
+        return this;
+    }
+
+    /**
+     * @param tag From your MetricsEvent enum.
+     * @param value One of Integer, Long, Float, String
+     * @return
+     */
+    public LogMaker addTaggedData(int tag, Object value) {
+        if (!isValidValue(value)) {
+            throw new IllegalArgumentException(
+                    "Value must be loggable type - int, long, float, String");
+        }
+        if (value.toString().getBytes().length > MAX_SERIALIZED_SIZE) {
+            Log.i(TAG, "Log value too long, omitted: " + value.toString());
+        } else {
+            entries.put(tag, value);
+        }
+        return this;
+    }
+
+    public boolean isValidValue(Object value) {
+        if (value == null) {
+            Log.i("LogBuilder", "Logging a null value.");
+            return true;
+        }
+        return value instanceof Integer ||
+            value instanceof String ||
+            value instanceof Long ||
+            value instanceof Float;
+    }
+
+    public Object getTaggedData(int tag) {
+        return entries.get(tag);
+    }
+
+    public int getCategory() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_CATEGORY);
+        if (obj instanceof Integer) {
+            return (Integer) obj;
+        } else {
+            return MetricsEvent.VIEW_UNKNOWN;
+        }
+    }
+
+    public int getType() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_TYPE);
+        if (obj instanceof Integer) {
+            return (Integer) obj;
+        } else {
+            return MetricsEvent.TYPE_UNKNOWN;
+        }
+    }
+
+    public int getSubtype() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE);
+        if (obj instanceof Integer) {
+            return (Integer) obj;
+        } else {
+            return 0;
+        }
+    }
+
+    public long getTimestamp() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_TIMESTAMP);
+        if (obj instanceof Long) {
+            return (Long) obj;
+        } else {
+            return 0;
+        }
+    }
+
+    public String getPackageName() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_PACKAGENAME);
+        if (obj instanceof String) {
+            return (String) obj;
+        } else {
+            return null;
+        }
+    }
+
+    public String getCounterName() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_NAME);
+        if (obj instanceof String) {
+            return (String) obj;
+        } else {
+            return null;
+        }
+    }
+
+    public long getCounterBucket() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET);
+        if (obj instanceof Number) {
+            return ((Number) obj).longValue();
+        } else {
+            return 0L;
+        }
+    }
+
+    public boolean isLongCounterBucket() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET);
+        return obj instanceof Long;
+    }
+
+    public int getCounterValue() {
+        Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_VALUE);
+        if (obj instanceof Integer) {
+            return (Integer) obj;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Assemble logs into structure suitable for EventLog.
+     */
+    public Object[] serialize() {
+        Object[] out = new Object[entries.size() * 2];
+        for (int i = 0; i < entries.size(); i++) {
+            out[i * 2] = entries.keyAt(i);
+            out[i * 2 + 1] = entries.valueAt(i);
+        }
+        int size = out.toString().getBytes().length;
+        if (size > MAX_SERIALIZED_SIZE) {
+            Log.i(TAG, "Log line too long, did not emit: " + size + " bytes.");
+            throw new RuntimeException();
+        }
+        return out;
+    }
+
+    public void deserialize(Object[] items) {
+        int i = 0;
+        while (i < items.length) {
+            Object key = items[i++];
+            Object value = i < items.length ? items[i++] : null;
+            if (key instanceof Integer) {
+                entries.put((Integer) key, value);
+            } else {
+                Log.i(TAG, "Invalid key " + key.toString());
+            }
+        }
+    }
+}
diff --git a/core/java/android/metrics/MetricsReader.java b/core/java/android/metrics/MetricsReader.java
new file mode 100644
index 0000000..079c2c9
--- /dev/null
+++ b/core/java/android/metrics/MetricsReader.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.metrics;
+
+import android.annotation.SystemApi;
+
+import com.android.internal.logging.legacy.LegacyConversionLogger;
+import com.android.internal.logging.legacy.EventLogCollector;
+
+import java.util.Queue;
+
+/**
+ * Read platform logs.
+ * @hide
+ */
+@SystemApi
+public class MetricsReader {
+    private EventLogCollector mReader;
+    private Queue<LogMaker> mEventQueue;
+    private long mLastEventMs;
+    private long mCheckpointMs;
+
+    /** Open a new session and start reading logs.
+     *
+     * Starts reading from the oldest log not already read by this reader object.
+     * On first invocation starts from the oldest available log ion the system.
+     */
+    public void read(long startMs) {
+        EventLogCollector reader = EventLogCollector.getInstance();
+        LegacyConversionLogger logger = new LegacyConversionLogger();
+        mLastEventMs = reader.collect(logger, startMs);
+        mEventQueue = logger.getEvents();
+    }
+
+    public void checkpoint() {
+        read(0L);
+        mCheckpointMs = mLastEventMs;
+        mEventQueue = null;
+    }
+
+    public void reset() {
+        read(mCheckpointMs);
+    }
+
+    /* Does the current log session have another entry? */
+    public boolean hasNext() {
+        return mEventQueue == null ? false : !mEventQueue.isEmpty();
+    }
+
+    /* Next entry in the current log session. */
+    public LogMaker next() {
+        return mEventQueue == null ? null : mEventQueue.remove();
+    }
+
+}
diff --git a/core/java/android/net/INetworkScoreCache.aidl b/core/java/android/net/INetworkScoreCache.aidl
index 35601ce..1da7d67 100644
--- a/core/java/android/net/INetworkScoreCache.aidl
+++ b/core/java/android/net/INetworkScoreCache.aidl
@@ -34,7 +34,7 @@
  * the current scores for each network for debugging purposes.
  * @hide
  */
-interface INetworkScoreCache
+oneway interface INetworkScoreCache
 {
     void updateScores(in List<ScoredNetwork> networks);
 
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index 9573953..82432c7 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -21,6 +21,7 @@
 import android.net.RecommendationRequest;
 import android.net.RecommendationResult;
 import android.net.ScoredNetwork;
+import android.os.RemoteCallback;
 
 /**
  * A service for updating network scores from a network scorer application.
@@ -117,4 +118,16 @@
      *         scorer.
      */
     String getActiveScorerPackage();
+    
+    /**
+     * Request a recommendation for the best network to connect to
+     * taking into account the inputs from the {@link RecommendationRequest}.
+     *
+     * @param request a {@link RecommendationRequest} instance containing the details of the request
+     * @param remoteCallback a {@link RemoteCallback} instance to invoke when the recommendation
+     *                       is available.
+     * @throws SecurityException if the caller is not the system
+     */
+    oneway void requestRecommendationAsync(in RecommendationRequest request,
+                                           in RemoteCallback remoteCallback);
 }
diff --git a/core/java/android/net/NetworkBadging.java b/core/java/android/net/NetworkBadging.java
new file mode 100644
index 0000000..5cf2f96
--- /dev/null
+++ b/core/java/android/net/NetworkBadging.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net;
+
+import android.annotation.DrawableRes;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.net.ScoredNetwork.Badging;
+import android.net.wifi.WifiManager;
+import android.view.View;
+
+/**
+ * Utility methods for working with network badging.
+ *
+ * TODO: move ScoredNetwork.Badging and related constants to this class.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkBadging {
+    private NetworkBadging() {}
+
+    /**
+     * Returns a Wi-Fi icon for a network with a given signal level and badging value.
+     *
+     * @param signalLevel The level returned by {@link WifiManager#calculateSignalLevel(int, int)}
+     *                    for a network. Must be between 0 and {@link WifiManager#RSSI_LEVELS}-1.
+     * @param badging  {@see ScoredNetwork#Badging}, retrieved from
+     *                 {@link ScoredNetwork#calculateBadge(int)}.
+     * @param theme The theme for the current application, may be null.
+     * @return Drawable for the given icon
+     * @throws IllegalArgumentException if {@code signalLevel} is out of range or {@code badging}
+     *                                  is an invalid value
+     */
+    @NonNull public static Drawable getWifiIcon(
+            @IntRange(from=0, to=4) int signalLevel, @Badging int badging, @Nullable Theme theme) {
+        Resources resources = Resources.getSystem();
+        if (badging == ScoredNetwork.BADGING_NONE) {
+            return resources.getDrawable(getWifiSignalResource(signalLevel), theme);
+        }
+        Drawable[] layers = new Drawable[] {
+                resources.getDrawable(getBadgedWifiSignalResource(signalLevel), theme),
+                resources.getDrawable(getWifiBadgeResource(badging), theme)
+        };
+        return new LayerDrawable(layers);
+    }
+
+    /**
+     * Returns the wifi signal resource id for the given signal level.
+     *
+     * <p>This wifi signal resource is a wifi icon to be displayed by itself when there is no badge.
+     *
+     * @param signalLevel The level returned by {@link WifiManager#calculateSignalLevel(int, int)}
+     *                    for a network. Must be between 0 and {@link WifiManager#RSSI_LEVELS}-1.
+     * @return the @DrawableRes for the icon
+     * @throws IllegalArgumentException for an invalid signal level
+     * @hide
+     */
+    @DrawableRes private static int getWifiSignalResource(int signalLevel) {
+        switch (signalLevel) {
+            case 0:
+                return com.android.internal.R.drawable.ic_wifi_signal_0;
+            case 1:
+                return com.android.internal.R.drawable.ic_wifi_signal_1;
+            case 2:
+                return com.android.internal.R.drawable.ic_wifi_signal_2;
+            case 3:
+                return com.android.internal.R.drawable.ic_wifi_signal_3;
+            case 4:
+                return com.android.internal.R.drawable.ic_wifi_signal_4;
+            default:
+                throw new IllegalArgumentException("Invalid signal level: " + signalLevel);
+        }
+    }
+
+    /**
+     * Returns the badged wifi signal resource id for the given signal level.
+     *
+     * <p>This badged wifi signal resource should be displayed with the quality badge retrieved
+     * from {@link #getWifiBadgeResource(int)}. If there is no badge,
+     * {@link #getWifiBadgeResource(int)} should be used instead of this method.
+     *
+     * @param signalLevel The level returned by {@link WifiManager#calculateSignalLevel(int, int)}
+     *                    for a network. Must be between 0 and {@link WifiManager#RSSI_LEVELS}-1.
+     * @return the @DrawableRes for the icon
+     * @throws IllegalArgumentException for an invalid signal level
+     * @hide
+     */
+    @DrawableRes private static int getBadgedWifiSignalResource(int signalLevel) {
+        switch (signalLevel) {
+            case 0:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_0_bars;
+            case 1:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_1_bar;
+            case 2:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_2_bars;
+            case 3:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_3_bars;
+            case 4:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_4_bars;
+            default:
+                throw new IllegalArgumentException("Invalid signal level: " + signalLevel);
+        }
+    }
+
+    /**
+     * Returns the wifi quality badge resource id for the the given badging balue.
+     *
+     * <p>This badge should be displayed with the badge signal resource retrieved from
+     * {@link #getBadgedWifiSignalResource(int)}.
+     *
+     * @param badging {@see ScoredNetwork#Badging} from {@link ScoredNetwork#calculateBadge(int)}.
+     * @return the @DrawableRes for the icon or {@link View#NO_ID} for
+     *         {@link ScoredNetwork#BADGING_NONE}
+     * @throws IllegalArgumentException for an invalid badging value.
+     * @hide
+     */
+    @DrawableRes private static int getWifiBadgeResource(@Badging int badging) {
+        switch (badging) {
+            case ScoredNetwork.BADGING_NONE:
+                return View.NO_ID;
+            case ScoredNetwork.BADGING_SD:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_sd;
+            case ScoredNetwork.BADGING_HD:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_hd;
+            case ScoredNetwork.BADGING_4K:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_4k;
+            default:
+                throw new IllegalArgumentException("No resource found for badge: " + badging);
+        }
+    }
+}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index c704ef0..0775bda 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -24,8 +24,10 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Build;
+import android.service.NetworkIdentityProto;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import java.util.Objects;
 
@@ -110,6 +112,23 @@
         return builder.append("}").toString();
     }
 
+    public void writeToProto(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+
+        proto.write(NetworkIdentityProto.TYPE, mType);
+
+        // Not dumping mSubType, subtypes are no longer supported.
+
+        if (mSubscriberId != null) {
+            proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
+        }
+        proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
+        proto.write(NetworkIdentityProto.ROAMING, mRoaming);
+        proto.write(NetworkIdentityProto.METERED, mMetered);
+
+        proto.end(start);
+    }
+
     public int getType() {
         return mType;
     }
diff --git a/core/java/android/net/NetworkKey.java b/core/java/android/net/NetworkKey.java
index ffbc401..e5f0bf0 100644
--- a/core/java/android/net/NetworkKey.java
+++ b/core/java/android/net/NetworkKey.java
@@ -16,9 +16,14 @@
 
 package android.net;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiSsid;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import java.util.Objects;
 
@@ -52,6 +57,39 @@
     public final WifiKey wifiKey;
 
     /**
+     * Constructs a new NetworkKey for the given wifi {@link ScanResult}.
+     *
+     * @throws IllegalArgumentException if the given ScanResult is malformed
+     * @hide
+     */
+    public static NetworkKey createFromScanResult(ScanResult result) {
+        return new NetworkKey(
+                new WifiKey(
+                        '"' + result.wifiSsid.toString() + '"', result.BSSID));
+    }
+
+    /**
+     * Constructs a new NetworkKey for the given {@link WifiInfo}.
+     *
+     * @param wifiInfo the {@link WifiInfo} to create a {@link NetworkKey} for.
+     * @return A new {@link NetworkKey} instance or <code>null</code> if the given {@link WifiInfo}
+     *         instance doesn't represent a connected WiFi network.
+     * @hide
+     */
+    @Nullable
+    public static NetworkKey createFromWifiInfo(@Nullable WifiInfo wifiInfo) {
+        if (wifiInfo != null) {
+            final String ssid = wifiInfo.getSSID();
+            final String bssid = wifiInfo.getBSSID();
+            if (!TextUtils.isEmpty(ssid) && !ssid.equals(WifiSsid.NONE)
+                    && !TextUtils.isEmpty(bssid)) {
+                return new NetworkKey(new WifiKey(ssid, bssid));
+            }
+        }
+        return null;
+    }
+
+    /**
      * Construct a new {@link NetworkKey} for a Wi-Fi network.
      * @param wifiKey the {@link WifiKey} identifying this Wi-Fi network.
      */
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 2870dd6..57cf1a5 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -16,18 +16,28 @@
 
 package android.net;
 
+import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
+
+import android.Manifest;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * Class that manages communication between network subsystems and a network scorer.
@@ -354,4 +364,43 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Request a recommendation for which network to connect to.
+     *
+     * <p>The callback will be run on the thread associated with provided {@link Handler}.
+     *
+     * @param request a {@link RecommendationRequest} instance containing additional
+     *                request details
+     * @param handler a {@link Handler} instance representing the thread to complete the future on.
+     *                If null the responding binder thread will be used.
+     * @return a {@link CompletableFuture} instance that will eventually receive the
+     *         {@link RecommendationResult}.
+     * @throws SecurityException
+     * @hide
+     */
+    public CompletableFuture<RecommendationResult> requestRecommendation(
+            final @NonNull RecommendationRequest request,
+            final @Nullable Handler handler) {
+        Preconditions.checkNotNull(request, "RecommendationRequest cannot be null.");
+
+        final CompletableFuture<RecommendationResult> futureResult =
+                new CompletableFuture<>();
+
+        RemoteCallback remoteCallback = new RemoteCallback(new RemoteCallback.OnResultListener() {
+            @Override
+            public void onResult(Bundle data) {
+                RecommendationResult result = data.getParcelable(EXTRA_RECOMMENDATION_RESULT);
+                futureResult.complete(result);
+            }
+        }, handler);
+
+        try {
+            mService.requestRecommendationAsync(request, remoteCallback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return futureResult;
+    }
 }
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index 23d5af5..9e4dd87 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -226,16 +226,6 @@
         return false;
     }
 
-    /** Determine whether the application with the given UID is the enabled scorer. */
-    @Deprecated // Use NetworkScoreManager.isCallerActiveScorer()
-    public boolean isCallerActiveScorer(int callingUid) {
-        NetworkScorerAppData defaultApp = getActiveScorer();
-        if (defaultApp == null) {
-            return false;
-        }
-        return callingUid == defaultApp.packageUid;
-    }
-
     private boolean isNetworkRecommendationsDisabled() {
         final ContentResolver cr = mContext.getContentResolver();
         // A value of 1 indicates enabled.
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 4a4accb..5f521de 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -31,7 +31,10 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.service.NetworkStatsHistoryBucketProto;
+import android.service.NetworkStatsHistoryProto;
 import android.util.MathUtils;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -628,6 +631,33 @@
         }
     }
 
+    public void writeToProto(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+
+        proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration);
+
+        for (int i = 0; i < bucketCount; i++) {
+            final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS);
+
+            proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i);
+
+            proto.end(startBucket);
+        }
+
+        proto.end(start);
+    }
+
+    private static void writeToProto(ProtoOutputStream proto, long tag, long[] array, int index) {
+        if (array != null) {
+            proto.write(tag, array[index]);
+        }
+    }
+
     @Override
     public String toString() {
         final CharArrayWriter writer = new CharArrayWriter();
diff --git a/core/java/android/net/RecommendationRequest.java b/core/java/android/net/RecommendationRequest.java
index d013f64..b89a245 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -34,8 +34,11 @@
 @SystemApi
 public final class RecommendationRequest implements Parcelable {
     private final ScanResult[] mScanResults;
-    private final WifiConfiguration mCurrentSelectedConfig;
-    private final NetworkCapabilities mRequiredCapabilities;
+    private final WifiConfiguration mDefaultConfig;
+    private WifiConfiguration mConnectedConfig;
+    private WifiConfiguration[] mConnectableConfigs;
+    private final int mLastSelectedNetworkId;
+    private final long mLastSelectedNetworkTimestamp;
 
     /**
      * Builder class for constructing {@link RecommendationRequest} instances.
@@ -44,26 +47,65 @@
     @SystemApi
     public static final class Builder {
         private ScanResult[] mScanResults;
-        private WifiConfiguration mCurrentConfig;
-        private NetworkCapabilities mNetworkCapabilities;
+        private WifiConfiguration mDefaultConfig;
+        private WifiConfiguration mConnectedConfig;
+        private WifiConfiguration[] mConnectableConfigs;
+        private int mLastSelectedNetworkId;
+        private long mLastSelectedTimestamp;
 
         public Builder setScanResults(ScanResult[] scanResults) {
             mScanResults = scanResults;
             return this;
         }
 
-        public Builder setCurrentRecommendedWifiConfig(WifiConfiguration config) {
-            this.mCurrentConfig = config;
+        /**
+         * @param config the {@link WifiConfiguration} to return if no recommendation is available.
+         * @return this
+         */
+        public Builder setDefaultWifiConfig(WifiConfiguration config) {
+            this.mDefaultConfig = config;
             return this;
         }
 
-        public Builder setNetworkCapabilities(NetworkCapabilities capabilities) {
-            mNetworkCapabilities = capabilities;
+        /**
+         * @param config the {@link WifiConfiguration} of the connected network at the time the
+         *               this request was made.
+         * @return this
+         */
+        public Builder setConnectedWifiConfig(WifiConfiguration config) {
+            this.mConnectedConfig = config;
             return this;
         }
 
+        /**
+         * @param connectableConfigs the set of saved {@link WifiConfiguration}s that can be
+         *                           connected to based on the current set of {@link ScanResult}s.
+         * @return this
+         */
+        public Builder setConnectableConfigs(WifiConfiguration[] connectableConfigs) {
+            this.mConnectableConfigs = connectableConfigs;
+            return this;
+        }
+
+        /**
+         * @param networkId The {@link WifiConfiguration#networkId} of the last user selected
+         *                  network.
+         * @param timestamp The {@link android.os.SystemClock#elapsedRealtime()} when the user
+         *                  selected {@code networkId}.
+         * @return this
+         */
+        public Builder setLastSelectedNetwork(int networkId, long timestamp) {
+            this.mLastSelectedNetworkId = networkId;
+            this.mLastSelectedTimestamp = timestamp;
+            return this;
+        }
+
+        /**
+         * @return a new {@link RecommendationRequest} instance
+         */
         public RecommendationRequest build() {
-            return new RecommendationRequest(mScanResults, mCurrentConfig, mNetworkCapabilities);
+            return new RecommendationRequest(mScanResults, mDefaultConfig, mConnectedConfig,
+                    mConnectableConfigs, mLastSelectedNetworkId, mLastSelectedTimestamp);
         }
     }
 
@@ -79,45 +121,103 @@
     }
 
     /**
-     * @return The best recommendation at the time this {@code RecommendationRequest} instance
-     *         was created. This may be null which indicates that no recommendation is available.
+     * @return the {@link WifiConfiguration} to return if no recommendation is available.
      */
-    public WifiConfiguration getCurrentSelectedConfig() {
-        return mCurrentSelectedConfig;
+    public WifiConfiguration getDefaultWifiConfig() {
+        return mDefaultConfig;
     }
 
     /**
-     *
-     * @return The set of {@link NetworkCapabilities} the recommendation must be constrained to.
-     *         This may be {@code null} which indicates that there are no constraints on the
-     *         capabilities of the recommended network.
+     * @return the {@link WifiConfiguration} of the connected network at the time the this request
+     *         was made.
      */
-    public NetworkCapabilities getRequiredCapabilities() {
-        return mRequiredCapabilities;
+    public WifiConfiguration getConnectedConfig() {
+        return mConnectedConfig;
+    }
+
+    /**
+     * @return the set of saved {@link WifiConfiguration}s that can be connected to based on the
+     *         current set of {@link ScanResult}s.
+     */
+    public WifiConfiguration[] getConnectableConfigs() {
+        return mConnectableConfigs;
+    }
+
+    /**
+     * @param connectedConfig the {@link WifiConfiguration} of the connected network at the time
+     *                        the this request was made.
+     */
+    public void setConnectedConfig(WifiConfiguration connectedConfig) {
+        mConnectedConfig = connectedConfig;
+    }
+
+    /**
+     * @param connectableConfigs the set of saved {@link WifiConfiguration}s that can be connected
+     *                           to based on the current set of {@link ScanResult}s.
+     */
+    public void setConnectableConfigs(WifiConfiguration[] connectableConfigs) {
+        mConnectableConfigs = connectableConfigs;
+    }
+
+    /**
+     * @return The {@link WifiConfiguration#networkId} of the last user selected network.
+     *         {@code 0} if not set.
+     */
+    public int getLastSelectedNetworkId() {
+        return mLastSelectedNetworkId;
+    }
+
+    /**
+     * @return The {@link android.os.SystemClock#elapsedRealtime()} when the user selected
+     *         {@link #getLastSelectedNetworkId()}. {@code 0} if not set.
+     */
+    public long getLastSelectedNetworkTimestamp() {
+        return mLastSelectedNetworkTimestamp;
     }
 
     @VisibleForTesting
     RecommendationRequest(ScanResult[] scanResults,
-            WifiConfiguration currentSelectedConfig,
-            NetworkCapabilities requiredCapabilities) {
+            WifiConfiguration defaultWifiConfig,
+            WifiConfiguration connectedWifiConfig,
+            WifiConfiguration[] connectableConfigs,
+            int lastSelectedNetworkId,
+            long lastSelectedNetworkTimestamp) {
         mScanResults = scanResults;
-        mCurrentSelectedConfig = currentSelectedConfig;
-        mRequiredCapabilities = requiredCapabilities;
+        mDefaultConfig = defaultWifiConfig;
+        mConnectedConfig = connectedWifiConfig;
+        mConnectableConfigs = connectableConfigs;
+        mLastSelectedNetworkId = lastSelectedNetworkId;
+        mLastSelectedNetworkTimestamp = lastSelectedNetworkTimestamp;
     }
 
     protected RecommendationRequest(Parcel in) {
         final int resultCount = in.readInt();
         if (resultCount > 0) {
             mScanResults = new ScanResult[resultCount];
+            final ClassLoader classLoader = ScanResult.class.getClassLoader();
             for (int i = 0; i < resultCount; i++) {
-                mScanResults[i] = in.readParcelable(ScanResult.class.getClassLoader());
+                mScanResults[i] = in.readParcelable(classLoader);
             }
         } else {
             mScanResults = null;
         }
 
-        mCurrentSelectedConfig = in.readParcelable(WifiConfiguration.class.getClassLoader());
-        mRequiredCapabilities = in.readParcelable(NetworkCapabilities.class.getClassLoader());
+        mDefaultConfig = in.readParcelable(WifiConfiguration.class.getClassLoader());
+        mConnectedConfig = in.readParcelable(WifiConfiguration.class.getClassLoader());
+
+        final int configCount = in.readInt();
+        if (configCount > 0) {
+            mConnectableConfigs = new WifiConfiguration[configCount];
+            final ClassLoader classLoader = WifiConfiguration.class.getClassLoader();
+            for (int i = 0; i < configCount; i++) {
+                mConnectableConfigs[i] = in.readParcelable(classLoader);
+            }
+        } else {
+            mConnectableConfigs = null;
+        }
+
+        mLastSelectedNetworkId = in.readInt();
+        mLastSelectedNetworkTimestamp = in.readLong();
     }
 
     @Override
@@ -135,8 +235,21 @@
         } else {
             dest.writeInt(0);
         }
-        dest.writeParcelable(mCurrentSelectedConfig, flags);
-        dest.writeParcelable(mRequiredCapabilities, flags);
+
+        dest.writeParcelable(mDefaultConfig, flags);
+        dest.writeParcelable(mConnectedConfig, flags);
+
+        if (mConnectableConfigs != null) {
+            dest.writeInt(mConnectableConfigs.length);
+            for (int i = 0; i < mConnectableConfigs.length; i++) {
+                dest.writeParcelable(mConnectableConfigs[i], flags);
+            }
+        } else {
+            dest.writeInt(0);
+        }
+
+        dest.writeInt(mLastSelectedNetworkId);
+        dest.writeLong(mLastSelectedNetworkTimestamp);
     }
 
     public static final Creator<RecommendationRequest> CREATOR =
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index cea56b5..ffc735c 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -96,6 +96,7 @@
 
     public boolean requestTime(InetAddress address, int port, int timeout) {
         DatagramSocket socket = null;
+        final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP);
         try {
             socket = new DatagramSocket();
             socket.setSoTimeout(timeout);
@@ -161,6 +162,7 @@
             if (socket != null) {
                 socket.close();
             }
+            TrafficStats.setThreadStatsTag(oldTag);
         }
 
         return true;
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index e7436be..fc66395 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -168,6 +168,24 @@
 
     /**
      * Set active tag to use when accounting {@link Socket} traffic originating
+     * from the current thread. Only one active tag per thread is supported.
+     * <p>
+     * Changes only take effect during subsequent calls to
+     * {@link #tagSocket(Socket)}.
+     * <p>
+     * Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
+     * used internally by system services like {@link DownloadManager} when
+     * performing traffic on behalf of an application.
+     *
+     * @return the current tag for the calling thread, which can be used to
+     *         restore any existing values after a nested operation is finished
+     */
+    public static int getAndSetThreadStatsTag(int tag) {
+        return NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
+    }
+
+    /**
+     * Set active tag to use when accounting {@link Socket} traffic originating
      * from the current thread. The tag used internally is well-defined to
      * distinguish all backup-related traffic.
      *
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 7396189..d5377c7 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1747,8 +1747,8 @@
      * begin with and a scheme component cannot be found.
      *
      * @return normalized Uri (never null)
-     * @see {@link android.content.Intent#setData}
-     * @see {@link android.content.Intent#setDataAndNormalize}
+     * @see android.content.Intent#setData
+     * @see android.content.Intent#setDataAndNormalize
      */
     public Uri normalizeScheme() {
         String scheme = getScheme();
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 83d17ba..bd32314 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -805,7 +805,7 @@
 
                 if (!mb && records.size() == 0 && !inChunk && !ignoreMbMe) {
                     throw new FormatException("expected MB flag");
-                } else if (mb && records.size() != 0 && !ignoreMbMe) {
+                } else if (mb && (records.size() != 0 || inChunk) && !ignoreMbMe) {
                     throw new FormatException("unexpected MB flag");
                 } else if (inChunk && il) {
                     throw new FormatException("unexpected IL flag in non-leading chunk");
@@ -839,6 +839,9 @@
 
                 if (cf && !inChunk) {
                     // first chunk
+                    if (typeLength == 0) {
+                        throw new FormatException("expected non-zero type length in first chunk");
+                    }
                     chunks.clear();
                     chunkTnf = tnf;
                 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index daf8b2d..80ecf97 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -887,6 +887,21 @@
             "eng".equals(getString("ro.build.type"));
 
     /**
+     * Whether this build is running inside a container.
+     *
+     * We should try to avoid checking this flag if possible to minimize
+     * unnecessarily diverging from non-container Android behavior.
+     * Checking this flag is acceptable when low-level resources being
+     * different, e.g. the availability of certain capabilities, access to
+     * system resources being restricted, and the fact that the host OS might
+     * handle some features for us.
+     * For higher-level behavior differences, other checks should be preferred.
+     * @hide
+     */
+    public static final boolean IS_CONTAINER =
+            SystemProperties.getBoolean("ro.boot.container", false);
+
+    /**
      * Specifies whether the permissions needed by a legacy app should be
      * reviewed before any of its components can run. A legacy app is one
      * with targetSdkVersion < 23, i.e apps using the old permission model.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 4616af8..8d9ceb6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -380,6 +380,24 @@
     }
 
     /**
+     * Returns location of preloaded cache directory for package name
+     * @see #getDataPreloadsDirectory()
+     * {@hide}
+     */
+    public static File getDataPreloadsFileCacheDirectory(String packageName) {
+        return new File(getDataPreloadsFileCacheDirectory(), packageName);
+    }
+
+    /**
+     * Returns location of preloaded cache directory.
+     * @see #getDataPreloadsDirectory()
+     * {@hide}
+     */
+    public static File getDataPreloadsFileCacheDirectory() {
+        return new File(getDataPreloadsDirectory(), "file_cache");
+    }
+
+    /**
      * Return the primary shared/external storage directory. This directory may
      * not currently be accessible if it has been mounted by the user on their
      * computer, has been removed from the device, or some other problem has
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 73c9462..7c015de 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -38,7 +38,6 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileWriter;
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
@@ -334,12 +333,7 @@
      * @throws IOException
      */
     public static void stringToFile(String filename, String string) throws IOException {
-        FileWriter out = new FileWriter(filename);
-        try {
-            out.write(string);
-        } finally {
-            out.close();
-        }
+        bytesToFile(filename, string.getBytes(StandardCharsets.UTF_8));
     }
 
     /**
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 4b130ed1..e4cdbce 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.opengl.EGL14;
 import android.os.SystemProperties;
 import android.util.Log;
 
@@ -34,6 +35,22 @@
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
 
     public static void setupGraphicsEnvironment(Context context) {
+        chooseDriver(context);
+
+        // Now that we've figured out which driver to use for this process, load and initialize it.
+        // This can take multiple frame periods, and it would otherwise happen as part of the first
+        // frame, increasing first-frame latency. Starting it here, as a low-priority background
+        // thread, means that it's usually done long before we start drawing the first frame,
+        // without significantly disrupting other activity launch work.
+        Thread eglInitThread = new Thread(
+                () -> {
+                    EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+                },
+                "EGL Init");
+        eglInitThread.start();
+    }
+
+    private static void chooseDriver(Context context) {
         String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
         if (driverPackageName == null || driverPackageName.isEmpty()) {
             return;
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 1ee83ae..c5ceecd 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -25,5 +25,5 @@
     boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
     boolean setupBcb(in String command);
     boolean clearBcb();
-    void rebootRecoveryWithCommand(in String command, in boolean update);
+    void rebootRecoveryWithCommand(in String command);
 }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 9513854..12f5396 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -19,6 +19,7 @@
 
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.UserManager;
 import android.content.pm.UserInfo;
 import android.content.IntentSender;
 import android.content.RestrictionEntry;
@@ -42,6 +43,7 @@
     void setUserEnabled(int userHandle);
     void evictCredentialEncryptionKey(int userHandle);
     boolean removeUser(int userHandle);
+    boolean removeUserEvenWhenDisallowed(int userHandle);
     void setUserName(int userHandle, String name);
     void setUserIcon(int userHandle, in Bitmap icon);
     ParcelFileDescriptor getUserIcon(int userHandle);
@@ -61,6 +63,7 @@
     int getUserSerialNumber(int userHandle);
     int getUserHandle(int userSerialNumber);
     int getUserRestrictionSource(String restrictionKey, int userHandle);
+    List<UserManager.EnforcingUser> getUserRestrictionSources(String restrictionKey, int userHandle);
     Bundle getUserRestrictions(int userHandle);
     boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
     boolean hasUserRestriction(in String restrictionKey, int userHandle);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index e15f086..1db685a 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -26,6 +26,9 @@
 import android.util.SizeF;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import libcore.util.SneakyThrow;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -250,6 +253,7 @@
     private static final int EX_NETWORK_MAIN_THREAD = -6;
     private static final int EX_UNSUPPORTED_OPERATION = -7;
     private static final int EX_SERVICE_SPECIFIC = -8;
+    private static final int EX_PARCELABLE = -9;
     private static final int EX_HAS_REPLY_HEADER = -128;  // special; see below
     // EX_TRANSACTION_FAILED is used exclusively in native code.
     // see libbinder's binder/Status.h
@@ -888,6 +892,21 @@
         }
     }
 
+    public final void writeSparseIntArray(SparseIntArray val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        int N = val.size();
+        writeInt(N);
+        int i=0;
+        while (i < N) {
+            writeInt(val.keyAt(i));
+            writeInt(val.valueAt(i));
+            i++;
+        }
+    }
+
     public final void writeBooleanArray(boolean[] val) {
         if (val != null) {
             int N = val.length;
@@ -1597,7 +1616,12 @@
      */
     public final void writeException(Exception e) {
         int code = 0;
-        if (e instanceof SecurityException) {
+        if (e instanceof Parcelable
+                && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {
+            // We only send Parcelable exceptions that are in the
+            // BootClassLoader to ensure that the receiver can unpack them
+            code = EX_PARCELABLE;
+        } else if (e instanceof SecurityException) {
             code = EX_SECURITY;
         } else if (e instanceof BadParcelableException) {
             code = EX_BAD_PARCELABLE;
@@ -1623,8 +1647,20 @@
             throw new RuntimeException(e);
         }
         writeString(e.getMessage());
-        if (e instanceof ServiceSpecificException) {
-            writeInt(((ServiceSpecificException)e).errorCode);
+        switch (code) {
+            case EX_SERVICE_SPECIFIC:
+                writeInt(((ServiceSpecificException) e).errorCode);
+                break;
+            case EX_PARCELABLE:
+                // Write parceled exception prefixed by length
+                final int sizePosition = dataPosition();
+                writeInt(0);
+                writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                final int payloadPosition = dataPosition();
+                setDataPosition(sizePosition);
+                writeInt(payloadPosition - sizePosition);
+                setDataPosition(payloadPosition);
+                break;
         }
     }
 
@@ -1722,6 +1758,13 @@
      */
     public final void readException(int code, String msg) {
         switch (code) {
+            case EX_PARCELABLE:
+                if (readInt() > 0) {
+                    SneakyThrow.sneakyThrow(
+                            (Exception) readParcelable(Parcelable.class.getClassLoader()));
+                } else {
+                    throw new RuntimeException(msg + " [missing Parcelable]");
+                }
             case EX_SECURITY:
                 throw new SecurityException(msg);
             case EX_BAD_PARCELABLE:
@@ -2127,6 +2170,20 @@
     }
 
     /**
+     * Read and return a new SparseIntArray object from the parcel at the current
+     * dataPosition(). Returns null if the previously written array object was null.
+     */
+    public final SparseIntArray readSparseIntArray() {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        SparseIntArray sa = new SparseIntArray(N);
+        readSparseIntArrayInternal(sa, N);
+        return sa;
+    }
+
+    /**
      * Read and return a new ArrayList containing a particular object type from
      * the parcel that was written with {@link #writeTypedList} at the
      * current dataPosition().  Returns null if the
@@ -2895,6 +2952,15 @@
         }
     }
 
+    private void readSparseIntArrayInternal(SparseIntArray outVal, int N) {
+        while (N > 0) {
+            int key = readInt();
+            int value = readInt();
+            outVal.append(key, value);
+            N--;
+        }
+    }
+
     /**
      * @hide For testing
      */
diff --git a/core/java/android/os/ParcelableException.java b/core/java/android/os/ParcelableException.java
new file mode 100644
index 0000000..d84d629
--- /dev/null
+++ b/core/java/android/os/ParcelableException.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.io.IOException;
+
+/**
+ * Wrapper class that offers to transport typical {@link Throwable} across a
+ * {@link Binder} call. This class is typically used to transport exceptions
+ * that cannot be modified to add {@link Parcelable} behavior, such as
+ * {@link IOException}.
+ * <ul>
+ * <li>The wrapped throwable must be defined as system class (that is, it must
+ * be in the same {@link ClassLoader} as {@link Parcelable}).
+ * <li>The wrapped throwable must support the
+ * {@link Throwable#Throwable(String)} constructor.
+ * <li>The receiver side must catch any thrown {@link ParcelableException} and
+ * call {@link #maybeRethrow(Class)} for all expected exception types.
+ * </ul>
+ *
+ * @hide
+ */
+public final class ParcelableException extends RuntimeException implements Parcelable {
+    public ParcelableException(Throwable t) {
+        super(t);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T extends Throwable> void maybeRethrow(Class<T> clazz) throws T {
+        if (clazz.isAssignableFrom(getCause().getClass())) {
+            throw (T) getCause();
+        }
+    }
+
+    /** {@hide} */
+    public static Throwable readFromParcel(Parcel in) {
+        final String name = in.readString();
+        final String msg = in.readString();
+        try {
+            final Class<?> clazz = Class.forName(name, true, Parcelable.class.getClassLoader());
+            return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(name + ": " + msg);
+        }
+    }
+
+    /** {@hide} */
+    public static void writeToParcel(Parcel out, Throwable t) {
+        out.writeString(t.getClass().getName());
+        out.writeString(t.getMessage());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        writeToParcel(dest, getCause());
+    }
+
+    public static final Creator<ParcelableException> CREATOR = new Creator<ParcelableException>() {
+        @Override
+        public ParcelableException createFromParcel(Parcel source) {
+            return new ParcelableException(readFromParcel(source));
+        }
+
+        @Override
+        public ParcelableException[] newArray(int size) {
+            return new ParcelableException[size];
+        }
+    };
+}
diff --git a/core/java/android/os/IProxyFileDescriptorCallback.java b/core/java/android/os/ProxyFileDescriptorCallback.java
similarity index 69%
rename from core/java/android/os/IProxyFileDescriptorCallback.java
rename to core/java/android/os/ProxyFileDescriptorCallback.java
index e41e194..2e9f8d9 100644
--- a/core/java/android/os/IProxyFileDescriptorCallback.java
+++ b/core/java/android/os/ProxyFileDescriptorCallback.java
@@ -17,18 +17,20 @@
 package android.os;
 
 import android.system.ErrnoException;
+import android.system.OsConstants;
 
 /**
  * Callback that handles file system requests from ProxyFileDescriptor.
- * @hide
  */
-public interface IProxyFileDescriptorCallback {
+public abstract class ProxyFileDescriptorCallback {
     /**
      * Returns size of bytes provided by the file descriptor.
      * @return Size of bytes
      * @throws ErrnoException
      */
-    long onGetSize() throws ErrnoException;
+    public long onGetSize() throws ErrnoException {
+        throw new ErrnoException("onGetSize", OsConstants.EBADF);
+    }
 
     /**
      * Provides bytes read from file descriptor.
@@ -39,7 +41,9 @@
      * @return Size of bytes returned by the function.
      * @throws ErrnoException
      */
-    int onRead(long offset, int size, byte[] data) throws ErrnoException;
+    public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+        throw new ErrnoException("onRead", OsConstants.EBADF);
+    }
 
     /**
      * Handles bytes written to file descriptor.
@@ -49,11 +53,20 @@
      * @return Size of bytes processed by the function.
      * @throws ErrnoException
      */
-    int onWrite(long offset, int size, byte[] data) throws ErrnoException;
+    public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+        throw new ErrnoException("onWrite", OsConstants.EBADF);
+    }
 
     /**
      * Processes fsync request.
      * @throws ErrnoException
      */
-    void onFsync() throws ErrnoException;
+    public void onFsync() throws ErrnoException {
+        throw new ErrnoException("onFsync", OsConstants.EINVAL);
+    }
+
+    /**
+     * Invoked after the file is closed.
+     */
+    abstract public void onRelease();
 }
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 7f9ea438..a8822c5 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -491,10 +491,15 @@
                 command += securityArg;
             }
 
-            // RECOVERY_SERVICE writes to BCB (bootloader control block) and triggers the reboot.
             RecoverySystem rs = (RecoverySystem) context.getSystemService(
                     Context.RECOVERY_SERVICE);
-            rs.rebootRecoveryWithCommand(command, true /* update */);
+            if (!rs.setupBcb(command)) {
+                throw new IOException("Setup BCB failed");
+            }
+
+            // Having set up the BCB (bootloader control block), go ahead and reboot
+            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+            pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE);
 
             throw new IOException("Reboot failed (no permissions?)");
         }
@@ -645,6 +650,18 @@
         bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
     }
 
+    /** {@hide} */
+    public static void rebootPromptAndWipeUserData(Context context, String reason)
+            throws IOException {
+        String reasonArg = null;
+        if (!TextUtils.isEmpty(reason)) {
+            reasonArg = "--reason=" + sanitizeArg(reason);
+        }
+
+        final String localeArg = "--locale=" + Locale.getDefault().toString();
+        bootCommand(context, null, "--prompt_and_wipe_data", reasonArg, localeArg);
+    }
+
     /**
      * Reboot into the recovery system to wipe the /cache partition.
      * @throws IOException if something goes wrong.
@@ -708,7 +725,7 @@
         // Write the command into BCB (bootloader control block) and boot from
         // there. Will not return unless failed.
         RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
-        rs.rebootRecoveryWithCommand(command.toString(), false);
+        rs.rebootRecoveryWithCommand(command.toString());
 
         throw new IOException("Reboot failed (no permissions?)");
     }
@@ -908,9 +925,9 @@
      * Talks to RecoverySystemService via Binder to set up the BCB command and
      * reboot into recovery accordingly.
      */
-    private void rebootRecoveryWithCommand(String command, boolean update) {
+    private void rebootRecoveryWithCommand(String command) {
         try {
-            mService.rebootRecoveryWithCommand(command, update);
+            mService.rebootRecoveryWithCommand(command);
         } catch (RemoteException ignored) {
         }
     }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 0da4bd1..ae981b7 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.net.TrafficStats;
 import android.net.Uri;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -245,11 +246,17 @@
      */
     private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 0x80 << 8;  // for VmPolicy
 
+    /**
+     * @hide
+     */
+    private static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24;  // for VmPolicy
+
     private static final int ALL_VM_DETECT_BITS =
             DETECT_VM_CURSOR_LEAKS | DETECT_VM_CLOSABLE_LEAKS |
             DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_INSTANCE_LEAKS |
             DETECT_VM_REGISTRATION_LEAKS | DETECT_VM_FILE_URI_EXPOSURE |
-            DETECT_VM_CLEARTEXT_NETWORK | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION;
+            DETECT_VM_CLEARTEXT_NETWORK | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION |
+            DETECT_VM_UNTAGGED_SOCKET;
 
     // Byte 3: Penalty
 
@@ -300,6 +307,8 @@
      */
     public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 0x04 << 24;
 
+    // CAUTION: we started stealing the top bits of Byte 4 for VM above
+
     /**
      * Mask of all the penalty bits valid for thread policies.
      */
@@ -715,7 +724,8 @@
             public Builder detectAll() {
                 int flags = DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_CURSOR_LEAKS
                         | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS
-                        | DETECT_VM_FILE_URI_EXPOSURE | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION;
+                        | DETECT_VM_FILE_URI_EXPOSURE | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION
+                        | DETECT_VM_UNTAGGED_SOCKET;
 
                 // TODO: always add DETECT_VM_CLEARTEXT_NETWORK once we have facility
                 // for apps to mark sockets that should be ignored
@@ -820,6 +830,22 @@
             }
 
             /**
+             * Detect any sockets in the calling app which have not been tagged
+             * using {@link TrafficStats}. Tagging sockets can help you
+             * investigate network usage inside your app, such as a narrowing
+             * down heavy usage to a specific library or component.
+             * <p>
+             * This currently does not detect sockets created in native code.
+             *
+             * @see TrafficStats#setThreadStatsTag(int)
+             * @see TrafficStats#tagSocket(java.net.Socket)
+             * @see TrafficStats#tagDatagramSocket(java.net.DatagramSocket)
+             */
+            public Builder detectUntaggedSockets() {
+                return enable(DETECT_VM_UNTAGGED_SOCKET);
+            }
+
+            /**
              * Crashes the whole process on violation. This penalty runs at the
              * end of all enabled penalties so you'll still get your logging or
              * other violations before the process dies.
@@ -1152,6 +1178,11 @@
             if (IS_ENG_BUILD) {
                 policyBuilder.penaltyLog();
             }
+            // All core system components need to tag their sockets to aid
+            // system health investigations
+            if (android.os.Process.myUid() < android.os.Process.FIRST_APPLICATION_UID) {
+                policyBuilder.detectUntaggedSockets();
+            }
             setVmPolicy(policyBuilder.build());
             setCloseGuardEnabled(vmClosableObjectLeaksEnabled());
         }
@@ -1832,6 +1863,13 @@
     /**
      * @hide
      */
+    public static boolean vmUntaggedSocketEnabled() {
+        return (sVmPolicyMask & DETECT_VM_UNTAGGED_SOCKET) != 0;
+    }
+
+    /**
+     * @hide
+     */
     public static void onSqliteObjectLeaked(String message, Throwable originStack) {
         onVmPolicyViolation(message, originStack);
     }
@@ -1911,6 +1949,14 @@
                 forceDeath);
     }
 
+    /**
+     * @hide
+     */
+    public static void onUntaggedSocket() {
+        onVmPolicyViolation(null, new Throwable("Untagged socket detected; use"
+                + " TrafficStats.setThreadSocketTag() to track all network usage"));
+    }
+
     // Map from VM violation fingerprint to uptime millis.
     private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
 
diff --git a/core/java/android/os/UserManager.aidl b/core/java/android/os/UserManager.aidl
new file mode 100644
index 0000000..2611b0f
--- /dev/null
+++ b/core/java/android/os/UserManager.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable UserManager.EnforcingUser;
\ No newline at end of file
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 3478eaa..c5c380c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1187,7 +1187,9 @@
      * @return The source of user restriction. Any combination of {@link #RESTRICTION_NOT_SET},
      *         {@link #RESTRICTION_SOURCE_SYSTEM}, {@link #RESTRICTION_SOURCE_DEVICE_OWNER}
      *         and {@link #RESTRICTION_SOURCE_PROFILE_OWNER}
+     * @deprecated use {@link #getUserRestrictionSources(String, int)} instead.
      */
+    @Deprecated
     @SystemApi
     @UserRestrictionSource
     public int getUserRestrictionSource(String restrictionKey, UserHandle userHandle) {
@@ -1199,6 +1201,25 @@
     }
 
     /**
+     * @hide
+     *
+     * Returns a list of users who set a user restriction on a given user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @return a list of user ids enforcing this restriction.
+     */
+    @SystemApi
+    public List<EnforcingUser> getUserRestrictionSources(
+            String restrictionKey, UserHandle userHandle) {
+        try {
+            return mService.getUserRestrictionSources(restrictionKey, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the user-wide restrictions imposed on this user.
      * @return a Bundle containing all the restrictions.
      */
@@ -2061,6 +2082,22 @@
     }
 
     /**
+     * Similar to {@link #removeUser(int)} except bypassing the checking of
+     * {@link UserManager#DISALLOW_REMOVE_USER}
+     * or {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE}.
+     *
+     * @see {@link #removeUser(int)}
+     * @hide
+     */
+    public boolean removeUserEvenWhenDisallowed(@UserIdInt int userHandle) {
+        try {
+            return mService.removeUserEvenWhenDisallowed(userHandle);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Updates the user's name.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
@@ -2310,8 +2347,8 @@
      * @hide
      * Checks if any uninitialized user has the specific seed account name and type.
      *
-     * @param mAccountName The account name to check for
-     * @param mAccountType The account type of the account to check for
+     * @param accountName The account name to check for
+     * @param accountType The account type of the account to check for
      * @return whether the seed account was found
      */
     public boolean someUserHasSeedAccount(String accountName, String accountType) {
@@ -2321,4 +2358,73 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * @hide
+     * User that enforces a restriction.
+     *
+     * @see #getUserRestrictionSources(String, UserHandle)
+     */
+    @SystemApi
+    public static final class EnforcingUser implements Parcelable {
+        private final @UserIdInt int userId;
+        private final @UserRestrictionSource int userRestrictionSource;
+
+        /**
+         * @hide
+         */
+        public EnforcingUser(
+                @UserIdInt int userId, @UserRestrictionSource int userRestrictionSource) {
+            this.userId = userId;
+            this.userRestrictionSource = userRestrictionSource;
+        }
+
+        private EnforcingUser(Parcel in) {
+            userId = in.readInt();
+            userRestrictionSource = in.readInt();
+        }
+
+        public static final Creator<EnforcingUser> CREATOR = new Creator<EnforcingUser>() {
+            @Override
+            public EnforcingUser createFromParcel(Parcel in) {
+                return new EnforcingUser(in);
+            }
+
+            @Override
+            public EnforcingUser[] newArray(int size) {
+                return new EnforcingUser[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(userId);
+            dest.writeInt(userRestrictionSource);
+        }
+
+        /**
+         * Returns an id of the enforcing user.
+         *
+         * <p> Will be UserHandle.USER_NULL when restriction is set by the system.
+         */
+        public UserHandle getUserHandle() {
+            return UserHandle.of(userId);
+        }
+
+        /**
+         * Returns the status of the enforcing user.
+         *
+         * <p> One of {@link #RESTRICTION_SOURCE_SYSTEM},
+         * {@link #RESTRICTION_SOURCE_DEVICE_OWNER} and
+         * {@link #RESTRICTION_SOURCE_PROFILE_OWNER}
+         */
+        public @UserRestrictionSource int getUserRestrictionSource() {
+            return userRestrictionSource;
+        }
+    }
 }
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 466a7e3..97da588 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -15,7 +15,6 @@
  */
 package android.os;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
@@ -24,6 +23,10 @@
  * @hide Only for use within the system server.
  */
 public abstract class UserManagerInternal {
+    public static final int CAMERA_NOT_DISABLED = 0;
+    public static final int CAMERA_DISABLED_LOCALLY = 1;
+    public static final int CAMERA_DISABLED_GLOBALLY = 2;
+
     public interface UserRestrictionsListener {
         /**
          * Called when a user restriction changes.
@@ -36,18 +39,19 @@
     }
 
     /**
-     * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
-     * to set per-user as well as global user restrictions.
+     * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set
+     * restrictions enforced by the user.
      *
      * @param userId target user id for the local restrictions.
-     * @param localRestrictions per-user restrictions.
-     *     Caller must not change it once passed to this method.
-     * @param globalRestrictions global restrictions set by DO.  Must be null when PO changed user
-     *     restrictions, in which case global restrictions won't change.
-     *     Caller must not change it once passed to this method.
+     * @param restrictions a bundle of user restrictions.
+     * @param isDeviceOwner whether {@code userId} corresponds to device owner user id.
+     * @param cameraRestrictionScope is camera disabled and if so what is the scope of restriction.
+     *        Should be one of {@link #CAMERA_NOT_DISABLED}, {@link #CAMERA_DISABLED_LOCALLY} or
+     *                               {@link #CAMERA_DISABLED_GLOBALLY}
      */
-    public abstract void setDevicePolicyUserRestrictions(int userId,
-            @NonNull Bundle localRestrictions, @Nullable Bundle globalRestrictions);
+    public abstract void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions,
+            boolean isDeviceOwner, int cameraRestrictionScope);
+
     /**
      * Returns the "base" user restrictions.
      *
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 27c0526..35a266b 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -25,6 +25,7 @@
 import android.os.storage.StorageVolume;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
+import com.android.internal.os.AppFuseMount;
 
 /**
  * WARNING! Update IMountService.h and IMountService.cpp if you change this
@@ -285,8 +286,11 @@
     void prepareUserStorage(in String volumeUuid, int userId, int serialNumber, int flags) = 66;
     void destroyUserStorage(in String volumeUuid, int userId, int flags) = 67;
     boolean isConvertibleToFBE() = 68;
-    ParcelFileDescriptor mountAppFuse(in String name) = 69;
     void addUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 70;
     void fixateNewestUserKeyAuth(int userId) = 71;
     void fstrim(int flags) = 72;
+    AppFuseMount mountProxyFileDescriptorBridge() = 73;
+    ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode) = 74;
+    long getCacheQuotaBytes(String volumeUuid, int uid) = 75;
+    long getCacheSizeBytes(String volumeUuid, int uid) = 76;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 03fd8d3..626d6f4 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -24,6 +24,7 @@
 import android.app.ActivityThread;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -33,18 +34,26 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.AppFuseMount;
+import com.android.internal.os.FuseAppLoop;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
@@ -56,12 +65,14 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.lang.ref.WeakReference;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -1314,14 +1325,304 @@
     }
 
     /** {@hide} */
-    public ParcelFileDescriptor mountAppFuse(String name) {
+    @VisibleForTesting
+    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+            int mode, ProxyFileDescriptorCallback callback, ThreadFactory factory)
+                    throws IOException {
+        // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before
+        // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount
+        // the bridge by calling mountProxyFileDescriptorBridge.
+        int retry = 3;
+        while (retry-- > 0) {
+            try {
+                synchronized (mFuseAppLoopLock) {
+                    if (mFuseAppLoop == null) {
+                        final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge();
+                        if (mount == null) {
+                            Log.e(TAG, "Failed to open proxy file bridge.");
+                            throw new IOException("Failed to open proxy file bridge.");
+                        }
+                        mFuseAppLoop = FuseAppLoop.open(mount.mountPointId, mount.fd, factory);
+                    }
+
+                    try {
+                        final int fileId = mFuseAppLoop.registerCallback(callback);
+                        final ParcelFileDescriptor pfd =
+                                mStorageManager.openProxyFileDescriptor(
+                                        mFuseAppLoop.getMountPointId(), fileId, mode);
+                        if (pfd != null) {
+                            return pfd;
+                        }
+                        // Probably the bridge is being unmounted but mFuseAppLoop has not been
+                        // noticed it yet.
+                        mFuseAppLoop.unregisterCallback(fileId);
+                    } catch (FuseAppLoop.UnmountedException error) {
+                        Log.d(TAG, "mFuseAppLoop has been already unmounted.");
+                        mFuseAppLoop = null;
+                        continue;
+                    }
+                }
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    break;
+                }
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+
+        throw new IOException("Failed to mount bridge.");
+    }
+
+    /**
+     * Opens seekable ParcelFileDescriptor that routes file operation requests to
+     * ProxyFileDescriptorCallback.
+     *
+     * @param mode The desired access mode, must be one of
+     *     {@link ParcelFileDescriptor#MODE_READ_ONLY},
+     *     {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
+     *     {@link ParcelFileDescriptor#MODE_READ_WRITE}
+     * @param callback Callback to process file operation requests issued on returned file
+     *     descriptor. The callback is invoked on a thread managed by the framework.
+     * @return Seekable ParcelFileDescriptor.
+     * @throws IOException
+     */
+    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+            int mode, ProxyFileDescriptorCallback callback)
+                    throws IOException {
+        return openProxyFileDescriptor(mode, callback, null);
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public int getProxyFileDescriptorMountPointId() {
+        synchronized (mFuseAppLoopLock) {
+            return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1;
+        }
+    }
+
+    /**
+     * Return quota size in bytes for cached data belonging to the calling app.
+     * <p>
+     * If your app goes above this quota, your cached files will be some of the
+     * first to be deleted when additional disk space is needed. Conversely, if
+     * your app stays under this quota, your cached files will be some of the
+     * last to be deleted when additional disk space is needed.
+     * <p>
+     * This quota may change over time depending on how frequently the user
+     * interacts with your app, and depending on how much disk space is used.
+     * <p>
+     * Cached data tracked by this method always includes
+     * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
+     * it also includes {@link Context#getExternalCacheDir()} if the primary
+     * shared/external storage is hosted on the same storage device as your
+     * private data.
+     * <p class="note">
+     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
+     * then cached data for all packages in your shared UID is tracked together
+     * as a single unit.
+     * </p>
+     *
+     * @see #getCacheQuotaBytes()
+     * @see #getCacheSizeBytes()
+     * @see #getExternalCacheQuotaBytes()
+     * @see #getExternalCacheSizeBytes()
+     */
+    public long getCacheQuotaBytes() {
         try {
-            return mStorageManager.mountAppFuse(name);
+            final ApplicationInfo app = mContext.getApplicationInfo();
+            return mStorageManager.getCacheQuotaBytes(app.volumeUuid, app.uid);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
+    /**
+     * Return total size in bytes of cached data belonging to the calling app.
+     * <p>
+     * Cached data tracked by this method always includes
+     * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
+     * it also includes {@link Context#getExternalCacheDir()} if the primary
+     * shared/external storage is hosted on the same storage device as your
+     * private data.
+     * <p class="note">
+     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
+     * then cached data for all packages in your shared UID is tracked together
+     * as a single unit.
+     * </p>
+     *
+     * @see #getCacheQuotaBytes()
+     * @see #getCacheSizeBytes()
+     * @see #getExternalCacheQuotaBytes()
+     * @see #getExternalCacheSizeBytes()
+     */
+    public long getCacheSizeBytes() {
+        try {
+            final ApplicationInfo app = mContext.getApplicationInfo();
+            return mStorageManager.getCacheSizeBytes(app.volumeUuid, app.uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return quota size in bytes for cached data on primary shared/external
+     * storage belonging to the calling app.
+     * <p>
+     * If primary shared/external storage is hosted on the same storage device
+     * as your private data, this method will return -1, since all data stored
+     * under {@link Context#getExternalCacheDir()} will be counted under
+     * {@link #getCacheQuotaBytes()}.
+     * <p class="note">
+     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
+     * then cached data for all packages in your shared UID is tracked together
+     * as a single unit.
+     * </p>
+     */
+    public long getExternalCacheQuotaBytes() {
+        final ApplicationInfo app = mContext.getApplicationInfo();
+        final String primaryUuid = getPrimaryStorageUuid();
+        if (Objects.equals(app.volumeUuid, primaryUuid)) {
+            return -1;
+        }
+        try {
+            return mStorageManager.getCacheQuotaBytes(primaryUuid, app.uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return total size in bytes of cached data on primary shared/external
+     * storage belonging to the calling app.
+     * <p>
+     * If primary shared/external storage is hosted on the same storage device
+     * as your private data, this method will return -1, since all data stored
+     * under {@link Context#getExternalCacheDir()} will be counted under
+     * {@link #getCacheQuotaBytes()}.
+     * <p class="note">
+     * Note: if your app uses the {@code android:sharedUserId} manifest feature,
+     * then cached data for all packages in your shared UID is tracked together
+     * as a single unit.
+     * </p>
+     */
+    public long getExternalCacheSizeBytes() {
+        final ApplicationInfo app = mContext.getApplicationInfo();
+        final String primaryUuid = getPrimaryStorageUuid();
+        if (Objects.equals(app.volumeUuid, primaryUuid)) {
+            return -1;
+        }
+        try {
+            return mStorageManager.getCacheSizeBytes(primaryUuid, app.uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private static final String XATTR_ATOMIC = "user.atomic";
+    private static final String XATTR_TOMBSTONE = "user.tombstone";
+
+    /** {@hide} */
+    private static void setCacheBehavior(File path, String name, boolean enabled)
+            throws IOException {
+        if (!path.isDirectory()) {
+            throw new IOException("Cache behavior can only be set on directories");
+        }
+        if (enabled) {
+            try {
+                Os.setxattr(path.getAbsolutePath(), name,
+                        "1".getBytes(StandardCharsets.UTF_8), 0);
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        } else {
+            try {
+                Os.removexattr(path.getAbsolutePath(), name);
+            } catch (ErrnoException e) {
+                if (e.errno != OsConstants.ENODATA) {
+                    throw e.rethrowAsIOException();
+                }
+            }
+        }
+    }
+
+    /** {@hide} */
+    private static boolean isCacheBehavior(File path, String name) throws IOException {
+        try {
+            Os.getxattr(path.getAbsolutePath(), name);
+            return true;
+        } catch (ErrnoException e) {
+            if (e.errno != OsConstants.ENODATA) {
+                throw e.rethrowAsIOException();
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Enable or disable special cache behavior that treats this directory and
+     * its contents as an atomic unit.
+     * <p>
+     * When enabled and this directory is considered for automatic deletion by
+     * the OS, all contained files will either be deleted together, or not at
+     * all. This is useful when you have a directory that contains several
+     * related metadata files that depend on each other, such as movie file and
+     * a subtitle file.
+     * <p>
+     * When enabled, the <em>newest</em> {@link File#lastModified()} value of
+     * any contained files is considered the modified time of the entire
+     * directory.
+     * <p>
+     * This behavior can only be set on a directory, and it applies recursively
+     * to all contained files and directories.
+     */
+    public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException {
+        setCacheBehavior(path, XATTR_ATOMIC, atomic);
+    }
+
+    /**
+     * Read the current value set by
+     * {@link #setCacheBehaviorAtomic(File, boolean)}.
+     */
+    public boolean isCacheBehaviorAtomic(File path) throws IOException {
+        return isCacheBehavior(path, XATTR_ATOMIC);
+    }
+
+    /**
+     * Enable or disable special cache behavior that leaves deleted cache files
+     * intact as tombstones.
+     * <p>
+     * When enabled and a file contained in this directory is automatically
+     * deleted by the OS, the file will be truncated to have a length of 0 bytes
+     * instead of being fully deleted. This is useful if you need to distinguish
+     * between a file that was deleted versus one that never existed.
+     * <p>
+     * This behavior can only be set on a directory, and it applies recursively
+     * to all contained files and directories.
+     * <p class="note">
+     * Note: this behavior is ignored completely if the user explicitly requests
+     * that all cached data be cleared.
+     * </p>
+     */
+    public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException {
+        setCacheBehavior(path, XATTR_TOMBSTONE, tombstone);
+    }
+
+    /**
+     * Read the current value set by
+     * {@link #setCacheBehaviorTombstone(File, boolean)}.
+     */
+    public boolean isCacheBehaviorTombstone(File path) throws IOException {
+        return isCacheBehavior(path, XATTR_TOMBSTONE);
+    }
+
+    private final Object mFuseAppLoopLock = new Object();
+
+    @GuardedBy("mFuseAppLoopLock")
+    private @Nullable FuseAppLoop mFuseAppLoop = null;
+
     /// Consts to match the password types in cryptfs.h
     /** @hide */
     public static final int CRYPT_TYPE_PASSWORD = 0;
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 8303bca..089eaec 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -145,6 +145,8 @@
 
     private List<Preference> mDependents;
 
+    private PreferenceGroup mParentGroup;
+
     private boolean mBaseMethodCalled;
 
     /**
@@ -1238,6 +1240,16 @@
         registerDependency();
     }
 
+    /**
+     * Assigns a {@link PreferenceGroup} as the parent of this Preference. Set null to remove
+     * the current parent.
+     *
+     * @param parentGroup Parent preference group of this Preference or null if none.
+     */
+    void assignParent(@Nullable PreferenceGroup parentGroup) {
+        mParentGroup = parentGroup;
+    }
+
     private void registerDependency() {
 
         if (TextUtils.isEmpty(mDependencyKey)) return;
@@ -1401,6 +1413,17 @@
     }
 
     /**
+     * Returns the {@link PreferenceGroup} which is this Preference assigned to or null if this
+     * preference is not assigned to any group or is a root Preference.
+     *
+     * @return The parent PreferenceGroup or null if not attached to any.
+     */
+    @Nullable
+    public PreferenceGroup getParent() {
+        return mParentGroup;
+    }
+
+    /**
      * Called when this Preference is being removed from the hierarchy. You
      * should remove any references to this Preference that you know about. Make
      * sure to call through to the superclass implementation.
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index f17506b..f135b26 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -29,14 +29,14 @@
  * A container for multiple
  * {@link Preference} objects. It is a base class for  Preference objects that are
  * parents, such as {@link PreferenceCategory} and {@link PreferenceScreen}.
- * 
+ *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For information about building a settings UI with Preferences,
  * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
  * guide.</p>
  * </div>
- * 
+ *
  * @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
  */
 public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent<Preference> {
@@ -80,7 +80,7 @@
      * <p>
      * If this is called after preferences are added, they will not be
      * re-ordered in the order they were added, hence call this method early on.
-     * 
+     *
      * @param orderingAsAdded Whether to order according to the order added.
      * @see Preference#setOrder(int)
      */
@@ -90,7 +90,7 @@
 
     /**
      * Whether this group is ordering preferences in the order they are added.
-     * 
+     *
      * @return Whether this group orders based on the order the children are added.
      * @see #setOrderingAsAdded(boolean)
      */
@@ -115,7 +115,7 @@
 
     /**
      * Returns the {@link Preference} at a particular index.
-     * 
+     *
      * @param index The index of the {@link Preference} to retrieve.
      * @return The {@link Preference}.
      */
@@ -126,7 +126,7 @@
     /**
      * Adds a {@link Preference} at the correct position based on the
      * preference's order.
-     * 
+     *
      * @param preference The preference to add.
      * @return Whether the preference is now in this group.
      */
@@ -135,7 +135,7 @@
             // Exists
             return true;
         }
-        
+
         if (preference.getOrder() == Preference.DEFAULT_ORDER) {
             if (mOrderingAsAdded) {
                 preference.setOrder(mCurrentPreferenceOrder++);
@@ -161,11 +161,12 @@
         }
 
         preference.onAttachedToHierarchy(getPreferenceManager());
-        
+        preference.assignParent(this);
+
         if (mAttachedToActivity) {
             preference.onAttachedToActivity();
         }
-        
+
         notifyHierarchyChanged();
 
         return true;
@@ -173,7 +174,7 @@
 
     /**
      * Removes a {@link Preference} from this group.
-     * 
+     *
      * @param preference The preference to remove.
      * @return Whether the preference was found and removed.
      */
@@ -186,10 +187,13 @@
     private boolean removePreferenceInt(Preference preference) {
         synchronized(this) {
             preference.onPrepareForRemoval();
+            if (preference.getParent() == this) {
+                preference.assignParent(null);
+            }
             return mPreferenceList.remove(preference);
         }
     }
-    
+
     /**
      * Removes all {@link Preference Preferences} from this group.
      */
@@ -202,10 +206,10 @@
         }
         notifyHierarchyChanged();
     }
-    
+
     /**
      * Prepares a {@link Preference} to be added to the group.
-     * 
+     *
      * @param preference The preference to add.
      * @return Whether to allow adding the preference (true), or not (false).
      */
@@ -223,7 +227,7 @@
      * <p>
      * This will recursively search for the preference into children that are
      * also {@link PreferenceGroup PreferenceGroups}.
-     * 
+     *
      * @param key The key of the preference to retrieve.
      * @return The {@link Preference} with the key, or null.
      */
@@ -239,7 +243,7 @@
             if (curKey != null && curKey.equals(key)) {
                 return preference;
             }
-            
+
             if (preference instanceof PreferenceGroup) {
                 final Preference returnedPreference = ((PreferenceGroup)preference)
                         .findPreference(key);
@@ -255,14 +259,14 @@
     /**
      * Whether this preference group should be shown on the same screen as its
      * contained preferences.
-     * 
+     *
      * @return True if the contained preferences should be shown on the same
      *         screen as this preference.
      */
     protected boolean isOnSameScreenAsChildren() {
         return true;
     }
-    
+
     @Override
     protected void onAttachedToActivity() {
         super.onAttachedToActivity();
@@ -270,7 +274,7 @@
         // Mark as attached so if a preference is later added to this group, we
         // can tell it we are already attached
         mAttachedToActivity = true;
-        
+
         // Dispatch to all contained preferences
         final int preferenceCount = getPreferenceCount();
         for (int i = 0; i < preferenceCount; i++) {
@@ -281,7 +285,7 @@
     @Override
     protected void onPrepareForRemoval() {
         super.onPrepareForRemoval();
-        
+
         // We won't be attached to the activity anymore
         mAttachedToActivity = false;
     }
@@ -297,7 +301,7 @@
             getPreference(i).onParentChanged(this, disableDependents);
         }
     }
-    
+
     void sortPreferences() {
         synchronized (this) {
             Collections.sort(mPreferenceList);
@@ -314,7 +318,7 @@
             getPreference(i).dispatchSaveInstanceState(container);
         }
     }
-    
+
     @Override
     protected void dispatchRestoreInstanceState(Bundle container) {
         super.dispatchRestoreInstanceState(container);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index a07aee5..1b512c6 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8170,11 +8170,29 @@
         /**
          * The content:// style URI for this table.  Requests to this URI can be
          * performed on the UI thread because they are always unblocking.
+         *
+         * <p>Note when you listen on this URI (or any other sub-URIs), you'll be notified for
+         * regular contact change notifications too, which will be sent on the root URI.
+         * If you only want to be notified for provider status change notifications, listen on
+         * {@link #STATUS_CHANGE_NOTIFICATION_CONTENT_URI} instead.
          */
         public static final Uri CONTENT_URI =
                 Uri.withAppendedPath(AUTHORITY_URI, "provider_status");
 
         /**
+         * URI to listen to provider status changes without listening to regular
+         * contact changes.  If a client only wants to monitor {@link ProviderStatus} with
+         * {@link android.app.job.JobScheduler}, then this URI should be used instead of
+         * {@link #CONTENT_URI}, because a job on {@link #CONTENT_URI} will also be invoked
+         * when contacts are changed.
+         *
+         * <p>Note this URI cannot be queried.  A query should be always made on
+         * {@link #CONTENT_URI}.
+         */
+        public static final Uri STATUS_CHANGE_NOTIFICATION_CONTENT_URI =
+                Uri.parse("content://com.android.contacts.provider_status");
+
+        /**
          * The MIME-type of {@link #CONTENT_URI} providing a directory of
          * settings.
          */
@@ -8201,6 +8219,13 @@
          * on the device.
          */
         public static final int STATUS_EMPTY = 2;
+
+        /**
+         * Timestamp (milliseconds since epoch) of when the provider's database was created.
+         *
+         * <P>Type: long
+         */
+        public static final String DATABASE_CREATION_TIMESTAMP = "database_creation_timestamp";
     }
 
     /**
@@ -8768,7 +8793,14 @@
         /**
          * This is the intent that is fired when the contacts database is created. <p> The
          * READ_CONTACT permission is required to receive these broadcasts.
+         *
+         * <p>As of O, this broadcast will no longer be sent.  Applications can use
+         * use {@link android.app.job.JobScheduler} to monitor
+         * {@link ProviderStatus#STATUS_CHANGE_NOTIFICATION_CONTENT_URI}, and read
+         * {@link ProviderStatus#DATABASE_CREATION_TIMESTAMP} to get when
+         * the contacts database was initialized.
          */
+        @Deprecated
         public static final String CONTACTS_DATABASE_CREATED =
                 "android.provider.Contacts.DATABASE_CREATED";
 
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index ded715f..a6e6fda 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -28,6 +28,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ResolveInfo;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
@@ -422,6 +423,14 @@
         public static final int FLAG_SUPPORTS_SETTINGS = 1 << 11;
 
         /**
+         * Flag indicating that a Web link can be obtained for the document.
+         *
+         * @see #COLUMN_FLAGS
+         * @see DocumentsContract#createWebLinkIntent(PackageManager, Uri, Bundle)
+         */
+        public static final int FLAG_WEB_LINKABLE = 1 << 12;
+
+        /**
          * Flag indicating that a document is not complete, likely its
          * contents are being downloaded. Partial files cannot be opened,
          * copied, moved in the UI. But they can be deleted and retried
@@ -685,12 +694,20 @@
     public static final String METHOD_EJECT_ROOT = "android:ejectRoot";
     /** {@hide} */
     public static final String METHOD_FIND_DOCUMENT_PATH = "android:findDocumentPath";
+    /** {@hide} */
+    public static final String METHOD_CREATE_WEB_LINK_INTENT = "android:createWebLinkIntent";
 
     /** {@hide} */
     public static final String EXTRA_PARENT_URI = "parentUri";
     /** {@hide} */
     public static final String EXTRA_URI = "uri";
 
+    /**
+     * @see #createWebLinkIntent(ContentResolver, Uri, Bundle)
+     * {@hide}
+     */
+    public static final String EXTRA_OPTIONS = "options";
+
     private static final String PATH_ROOT = "root";
     private static final String PATH_RECENT = "recent";
     private static final String PATH_DOCUMENT = "document";
@@ -1401,6 +1418,85 @@
     }
 
     /**
+     * Creates an intent for obtaining a web link for the specified document.
+     *
+     * <p>Note, that due to internal limitations, if there is already a web link
+     * intent created for the specified document but with different options,
+     * then it may be overriden.
+     *
+     * <p>Providers are required to show confirmation UI for all new permissions granted
+     * for the linked document.
+     *
+     * <p>If list of recipients is known, then it should be passed in options as
+     * {@link Intent#EXTRA_EMAIL} as either a string or list of strings. Note, that
+     * this is just a hint for the provider, which can ignore the list. In either
+     * case the provider is required to show a UI for letting the user confirm
+     * any new permission grants.
+     *
+     * <p>Since this API may show a UI, it cannot be called from background.
+     *
+     * <p>In order to obtain the Web Link use code like this:
+     * <pre><code>
+     * void onSomethingHappened() {
+     *   IntentSender sender = DocumentsContract.createWebLinkIntent(<i>...</i>);
+     *   if (sender != null) {
+     *     startIntentSenderForResult(
+     *         DocumentsContract.createWebLinkIntent(<i>...</i>),
+     *         WEB_LINK_REQUEST_CODE,
+     *         null, 0, 0, 0, null);
+     *   }
+     * }
+     *
+     * <i>(...)</i>
+     *
+     * void onActivityResult(int requestCode, int resultCode, Intent data) {
+     *   if (requestCode == WEB_LINK_REQUEST_CODE && resultCode == RESULT_OK) {
+     *     Uri weblinkUri = data.getData();
+     *     <i>...</i>
+     *   }
+     * }
+     * </code></pre>
+     *
+     * @param uri uri for the document to create a link to.
+     * @param options Extra information for generating the link.
+     * @return an intent sender to obtain the web link, or null if the document
+     *      is not linkable, or creating the intent sender failed.
+     * @see DocumentsProvider#createWebLinkIntent(String, Bundle)
+     * @see Intent#EXTRA_EMAIL
+     */
+    public static IntentSender createWebLinkIntent(ContentResolver resolver, Uri uri,
+            Bundle options) {
+        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
+                uri.getAuthority());
+        try {
+            return createWebLinkIntent(client, uri, options);
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to create a web link intent", e);
+            return null;
+        } finally {
+            ContentProviderClient.releaseQuietly(client);
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public static IntentSender createWebLinkIntent(ContentProviderClient client, Uri uri,
+            Bundle options) throws RemoteException {
+        final Bundle in = new Bundle();
+        in.putParcelable(DocumentsContract.EXTRA_URI, uri);
+
+        // Options may be provider specific, so put them in a separate bundle to
+        // avoid overriding the Uri.
+        if (options != null) {
+            in.putBundle(EXTRA_OPTIONS, options);
+        }
+
+        final Bundle out = client.call(METHOD_CREATE_WEB_LINK_INTENT, null, in);
+        return out.getParcelable(DocumentsContract.EXTRA_RESULT);
+    }
+
+    /**
      * Open the given image for thumbnail purposes, using any embedded EXIF
      * thumbnail if available, and providing orientation hints from the parent
      * image.
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 8bc03ee..f5e558a 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -18,6 +18,7 @@
 
 import static android.provider.DocumentsContract.METHOD_COPY_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
+import static android.provider.DocumentsContract.METHOD_CREATE_WEB_LINK_INTENT;
 import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_EJECT_ROOT;
 import static android.provider.DocumentsContract.METHOD_FIND_DOCUMENT_PATH;
@@ -43,6 +44,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.UriMatcher;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
@@ -363,6 +365,33 @@
     }
 
     /**
+     * Creates an intent sender for a web link, if the document is web linkable.
+     * <p>
+     * Before any new permissions are granted for the linked document, a visible
+     * UI must be shown, so the user can explicitly confirm whether the permission
+     * grants are expected. The user must be able to cancel the operation.
+     * <p>
+     * Options passed as an argument may include a list of recipients, such
+     * as email addresses. The provider should reflect these options if possible,
+     * but it's acceptable to ignore them. In either case, confirmation UI must
+     * be shown before any new permission grants are granted.
+     * <p>
+     * It is all right to generate a web link without granting new permissions,
+     * if opening the link would result in a page for requesting permission
+     * access. If it's impossible then the operation must fail by throwing an exception.
+     *
+     * @param documentId the document to create a web link intent for.
+     * @param options additional information, such as list of recipients. Optional.
+     *
+     * @see DocumentsContract.Document#FLAG_WEB_LINKABLE
+     * @see android.app.PendingIntent#getIntentSender
+     */
+    public IntentSender createWebLinkIntent(String documentId, @Nullable Bundle options)
+            throws FileNotFoundException {
+        throw new UnsupportedOperationException("createWebLink is not supported.");
+    }
+
+    /**
      * Return all roots currently provided. To display to users, you must define
      * at least one root. You should avoid making network requests to keep this
      * request fast.
@@ -649,8 +678,18 @@
         throw new UnsupportedOperationException("Pre-Android-O query format not supported.");
     }
 
+    /**
+     * WARNING: Sub-classes should not override this method. This method is non-final
+     * solely for the purposes of backwards compatibility.
+     *
+     * @see #queryChildDocuments(String, String[], Bundle),
+     *      {@link #queryDocument(String, String[])},
+     *      {@link #queryRecentDocuments(String, String[])},
+     *      {@link #queryRoots(String[])}, and
+     *      {@link #querySearchDocuments(String, String, String[])}.
+     */
     @Override
-    public final Cursor query(Uri uri, String[] projection, String selection,
+    public Cursor query(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
         // As of Android-O, ContentProvider#query (w/ bundle arg) is the primary
         // transport method. We override that, and don't ever delegate to this metohd.
@@ -900,6 +939,14 @@
                     newDocumentId);
             out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
 
+        } else if (METHOD_CREATE_WEB_LINK_INTENT.equals(method)) {
+            enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+
+            final Bundle options = extras.getBundle(DocumentsContract.EXTRA_OPTIONS);
+            final IntentSender intentSender = createWebLinkIntent(documentId, options);
+
+            out.putParcelable(DocumentsContract.EXTRA_RESULT, intentSender);
+
         } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
             enforceWritePermissionInner(documentUri, getCallingPackage(), null);
 
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
new file mode 100644
index 0000000..90e710f
--- /dev/null
+++ b/core/java/android/provider/FontsContract.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.provider;
+
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.graphics.fonts.FontRequest;
+import android.graphics.fonts.FontResult;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * Utility class to deal with Font ContentProviders.
+ */
+public class FontsContract {
+    private static final String TAG = "FontsContract";
+
+    /**
+     * Defines the constants used in a response from a Font Provider. The cursor returned from the
+     * query should have the ID column populated with the content uri ID for the resulting font.
+     * This should point to a real file or shared memory, as the client will mmap the given file
+     * descriptor. Pipes, sockets and other non-mmap-able file descriptors will fail to load in the
+     * client application.
+     */
+    public static final class Columns implements BaseColumns {
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * should have this column populated with an int for the ttc index for the resulting font.
+         */
+        public static final String TTC_INDEX = "font_ttc_index";
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * may populate this column with the font variation settings String information for the
+         * font.
+         */
+        public static final String VARIATION_SETTINGS = "font_variation_settings";
+        /**
+         * Constant used to request data from a font provider. The cursor returned from the query
+         * should have this column populated with the int style for the resulting font. This should
+         * be one of {@link android.graphics.Typeface#NORMAL},
+         * {@link android.graphics.Typeface#BOLD}, {@link android.graphics.Typeface#ITALIC} or
+         * {@link android.graphics.Typeface#BOLD_ITALIC}
+         */
+        public static final String STYLE = "font_style";
+    }
+
+    /**
+     * Constant used to identify the List of {@link ParcelFileDescriptor} item in the Bundle
+     * returned to the ResultReceiver in getFont.
+     * @hide
+     */
+    public static final String PARCEL_FONT_RESULTS = "font_results";
+
+    /** @hide */
+    public static final int RESULT_CODE_OK = 0;
+    /** @hide */
+    public static final int RESULT_CODE_FONT_NOT_FOUND = 1;
+    /** @hide */
+    public static final int RESULT_CODE_PROVIDER_NOT_FOUND = 2;
+
+    private static final int THREAD_RENEWAL_THRESHOLD_MS = 10000;
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private Handler mHandler;
+    @GuardedBy("mLock")
+    private HandlerThread mThread;
+
+    /** @hide */
+    public FontsContract() {
+        // TODO: investigate if the system context is the best option here. ApplicationContext or
+        // the one passed by developer?
+        // TODO: Looks like ActivityThread.currentActivityThread() can return null. Check when it
+        // returns null and check if we need to handle null case.
+        mContext = ActivityThread.currentActivityThread().getSystemContext();
+        mPackageManager = mContext.getPackageManager();
+    }
+
+    // We use a background thread to post the content resolving work for all requests on. This
+    // thread should be quit/stopped after all requests are done.
+    private final Runnable mReplaceDispatcherThreadRunnable = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                if (mThread != null) {
+                    mThread.quitSafely();
+                    mThread = null;
+                    mHandler = null;
+                }
+            }
+        }
+    };
+
+    /**
+     * @hide
+     */
+    public void getFont(FontRequest request, ResultReceiver receiver) {
+        synchronized (mLock) {
+            if (mHandler == null) {
+                mThread = new HandlerThread("fonts", Process.THREAD_PRIORITY_BACKGROUND);
+                mThread.start();
+                mHandler = new Handler(mThread.getLooper());
+            }
+            mHandler.post(() -> {
+                String providerAuthority = request.getProviderAuthority();
+                // TODO: Implement cert checking for non-system apps
+                ProviderInfo providerInfo = mPackageManager.resolveContentProvider(
+                        providerAuthority, PackageManager.MATCH_SYSTEM_ONLY);
+                if (providerInfo == null) {
+                    receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null);
+                    return;
+                }
+                Bundle result = getFontFromProvider(request, receiver, providerInfo);
+                if (result == null) {
+                    receiver.send(RESULT_CODE_FONT_NOT_FOUND, null);
+                    return;
+                }
+                receiver.send(RESULT_CODE_OK, result);
+            });
+            mHandler.removeCallbacks(mReplaceDispatcherThreadRunnable);
+            mHandler.postDelayed(mReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS);
+        }
+    }
+
+    private Bundle getFontFromProvider(FontRequest request, ResultReceiver receiver,
+            ProviderInfo providerInfo) {
+        ArrayList<FontResult> result = null;
+        Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(providerInfo.authority)
+                .build();
+        try (Cursor cursor = mContext.getContentResolver().query(uri, new String[] { Columns._ID,
+                        Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE },
+                "query = ?", new String[] { request.getQuery() }, null);) {
+            // TODO: Should we restrict the amount of fonts that can be returned?
+            // TODO: Write documentation explaining that all results should be from the same family.
+            if (cursor != null && cursor.getCount() > 0) {
+                result = new ArrayList<>();
+                final int idColumnIndex = cursor.getColumnIndex(Columns._ID);
+                final int ttcIndexColumnIndex = cursor.getColumnIndex(Columns.TTC_INDEX);
+                final int vsColumnIndex = cursor.getColumnIndex(Columns.VARIATION_SETTINGS);
+                final int styleColumnIndex = cursor.getColumnIndex(Columns.STYLE);
+                while (cursor.moveToNext()) {
+                    long id = cursor.getLong(idColumnIndex);
+                    Uri fileUri = ContentUris.withAppendedId(uri, id);
+                    try {
+                        ParcelFileDescriptor pfd =
+                                mContext.getContentResolver().openFileDescriptor(fileUri, "r");
+                        final int ttcIndex = cursor.getInt(ttcIndexColumnIndex);
+                        final String variationSettings = cursor.getString(vsColumnIndex);
+                        final int style = cursor.getInt(styleColumnIndex);
+                        result.add(new FontResult(pfd, ttcIndex, variationSettings, style));
+                    } catch (FileNotFoundException e) {
+                        Log.e(TAG, "FileNotFoundException raised when interacting with content "
+                                + "provider " + providerInfo.authority, e);
+                    }
+                }
+            }
+        }
+        if (result != null && !result.isEmpty()) {
+            Bundle bundle = new Bundle();
+            bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result);
+            return bundle;
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1865652..d41b3c5 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -30,6 +30,7 @@
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.Application;
+import android.app.NotificationChannel;
 import android.app.SearchManager;
 import android.app.WallpaperManager;
 import android.content.ComponentName;
@@ -50,6 +51,7 @@
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
+import android.os.Binder;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.DropBoxManager;
@@ -57,6 +59,7 @@
 import android.os.LocaleList;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.speech.tts.TextToSpeech;
@@ -161,13 +164,13 @@
      * In some cases, a matching Activity may not exist, so ensure you
      * safeguard against this.
      * <p>
-     * Input: {@link ConnectivityManager.EXTRA_TETHER_TYPE} should be included to specify which type
-     * of tethering should be checked. {@link ConnectivityManager.EXTRA_PROVISION_CALLBACK} should
+     * Input: {@link ConnectivityManager#EXTRA_TETHER_TYPE} should be included to specify which type
+     * of tethering should be checked. {@link ConnectivityManager#EXTRA_PROVISION_CALLBACK} should
      * contain a {@link ResultReceiver} which will be called back with a tether result code.
      * <p>
      * Output: The result of the provisioning check.
-     * {@link ConnectivityManager.TETHER_ERROR_NO_ERROR} if successful,
-     * {@link ConnectivityManager.TETHER_ERROR_PROVISION_FAILED} for failure.
+     * {@link ConnectivityManager#TETHER_ERROR_NO_ERROR} if successful,
+     * {@link ConnectivityManager#TETHER_ERROR_PROVISION_FAILED} for failure.
      *
      * @hide
      */
@@ -286,6 +289,20 @@
             "android.settings.SECURITY_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of trusted external sources
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_EXTERNAL_SOURCES =
+            "android.settings.action.MANAGE_EXTERNAL_SOURCES";
+
+    /**
      * Activity Action: Show trusted credentials settings, opening to the user tab,
      * to allow management of installed credentials.
      * <p>
@@ -1258,6 +1275,7 @@
     /**
      * Activity Action: Show notification settings for a single app.
      *
+     * Input: Optionally, {@link #EXTRA_CHANNEL_ID}, to highlight that channel.
      * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -1265,6 +1283,35 @@
             = "android.settings.APP_NOTIFICATION_SETTINGS";
 
     /**
+     * Activity Action: Show notification settings for a single {@link NotificationChannel}.
+     * <p>
+     * Must be called from an activity.
+     * <p>
+     *     Input: {@link #EXTRA_APP_PACKAGE}, the package containing the channel to display.
+     *     Input: {@link #EXTRA_CHANNEL_ID}, the id of the channel to display.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CHANNEL_NOTIFICATION_SETTINGS
+            = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
+
+    /**
+     * Activity Extra: The package owner of the notification channel settings to display.
+     * <p>
+     * This must be passed as an extra field to the {@link #ACTION_CHANNEL_NOTIFICATION_SETTINGS}.
+     */
+    public static final String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
+
+    /**
+     * Activity Extra: The {@link NotificationChannel#getId()} of the notification channel settings
+     * to display.
+     * <p>
+     * This must be passed as an extra field to the {@link #ACTION_CHANNEL_NOTIFICATION_SETTINGS}.
+     */
+    public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
+
+    /**
      * Activity Action: Show notification redaction settings.
      *
      * @hide
@@ -1274,7 +1321,6 @@
             = "android.settings.ACTION_APP_NOTIFICATION_REDACTION";
 
     /** @hide */ public static final String EXTRA_APP_UID = "app_uid";
-    /** @hide */ public static final String EXTRA_APP_PACKAGE = "app_package";
 
     /**
      * Activity Action: Show a dialog with disabled by policy message.
@@ -1318,6 +1364,19 @@
             = "android.settings.VR_LISTENER_SETTINGS";
 
     /**
+     * Activity Action: Show Picture-in-picture settings.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PICTURE_IN_PICTURE_SETTINGS
+            = "android.settings.PICTURE_IN_PICTURE_SETTINGS";
+
+    /**
      * Activity Action: Show Storage Manager settings.
      * <p>
      * Input: Nothing.
@@ -1581,6 +1640,24 @@
     // with a partial enable/disable state in multi-threaded situations.
     private static final Object mLocationSettingsLock = new Object();
 
+    // Used in system server calling uid workaround in call()
+    private static boolean sInSystemServer = false;
+    private static final Object sInSystemServerLock = new Object();
+
+    /** @hide */
+    public static void setInSystemServer() {
+        synchronized (sInSystemServerLock) {
+            sInSystemServer = true;
+        }
+    }
+
+    /** @hide */
+    public static boolean isInSystemServer() {
+        synchronized (sInSystemServerLock) {
+            return sInSystemServer;
+        }
+    }
+
     public static class SettingNotFoundException extends AndroidException {
         public SettingNotFoundException(String msg) {
             super(msg);
@@ -1789,7 +1866,23 @@
                             }
                         }
                     }
-                    Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
+                    Bundle b;
+                    // If we're in system server and in a binder transaction we need to clear the
+                    // calling uid. This works around code in system server that did not call
+                    // clearCallingIdentity, previously this wasn't needed because reading settings
+                    // did not do permission checking but thats no longer the case.
+                    // Long term this should be removed and callers should properly call
+                    // clearCallingIdentity or use a ContentResolver from the caller as needed.
+                    if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    } else {
+                        b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
+                    }
                     if (b != null) {
                         String value = b.getString(Settings.NameValueTable.VALUE);
                         // Don't update our cache for reads of other users' data
@@ -1849,7 +1942,19 @@
             try {
                 Bundle queryArgs = ContentResolver.createSqlQueryBundle(
                         NAME_EQ_PLACEHOLDER, new String[]{name}, null);
-                c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs, null);
+                // Same workaround as above.
+                if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs,
+                                null);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                } else {
+                    c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE_PROJECTION, queryArgs,
+                            null);
+                }
                 if (c == null) {
                     Log.w(TAG, "Can't get key " + name + " from " + mUri);
                     return null;
@@ -4006,6 +4111,22 @@
         }
 
         /**
+         * System settings which can be accessed by ephemeral apps.
+         * @hide
+         */
+        public static final Set<String> EPHEMERAL_SETTINGS = new ArraySet<>();
+        static {
+            EPHEMERAL_SETTINGS.add(TEXT_AUTO_REPLACE);
+            EPHEMERAL_SETTINGS.add(TEXT_AUTO_CAPS);
+            EPHEMERAL_SETTINGS.add(TEXT_AUTO_PUNCTUATE);
+            EPHEMERAL_SETTINGS.add(TEXT_SHOW_PASSWORD);
+            EPHEMERAL_SETTINGS.add(DATE_FORMAT);
+            EPHEMERAL_SETTINGS.add(FONT_SCALE);
+            EPHEMERAL_SETTINGS.add(HAPTIC_FEEDBACK_ENABLED);
+            EPHEMERAL_SETTINGS.add(TIME_12_24);
+        }
+
+        /**
          * When to use Wi-Fi calling
          *
          * @see android.telephony.TelephonyManager.WifiCallingChoices
@@ -5070,6 +5191,9 @@
          *
          * <p>1 = permit app installation via the system package installer intent
          * <p>0 = do not allow use of the package installer
+         * @deprecated Starting from {@link android.os.Build.VERSION_CODES#O}, apps should use
+         * {@link PackageManager#canRequestPackageInstalls()}
+         * @see PackageManager#canRequestPackageInstalls()
          */
         public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
 
@@ -5313,6 +5437,21 @@
         public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
 
         /**
+         * Setting specifying if the accessibility shortcut dialog has been shown to this user.
+         * @hide
+         */
+        public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN =
+                "accessibility_shortcut_dialog_shown";
+
+        /**
+         * Setting specifying the the accessibility service to be toggled via the accessibility
+         * shortcut. Must be its flattened {@link ComponentName}.
+         * @hide
+         */
+        public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
+                "accessibility_shortcut_target_service";
+
+        /**
          * If touch exploration is enabled.
          */
         public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
@@ -6782,6 +6921,8 @@
             TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
             TOUCH_EXPLORATION_ENABLED,
             ACCESSIBILITY_ENABLED,
+            ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+            ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
             ACCESSIBILITY_SPEAK_PASSWORD,
             ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
             ACCESSIBILITY_CAPTIONING_PRESET,
@@ -6867,6 +7008,20 @@
         }
 
         /**
+         * Secure settings which can be accessed by ephemeral apps.
+         * @hide
+         */
+        public static final Set<String> EPHEMERAL_SETTINGS = new ArraySet<>();
+        static {
+            EPHEMERAL_SETTINGS.add(ENABLED_ACCESSIBILITY_SERVICES);
+            EPHEMERAL_SETTINGS.add(ACCESSIBILITY_SPEAK_PASSWORD);
+            EPHEMERAL_SETTINGS.add(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+
+            EPHEMERAL_SETTINGS.add(DEFAULT_INPUT_METHOD);
+            EPHEMERAL_SETTINGS.add(ENABLED_INPUT_METHODS);
+        }
+
+        /**
          * Helper method for determining if a location provider is enabled.
          *
          * @param cr the content resolver to use
@@ -7071,7 +7226,9 @@
          * Setting whether the global gesture for enabling accessibility is enabled.
          * If this gesture is enabled the user will be able to perfrom it to enable
          * the accessibility state without visiting the settings app.
+         *
          * @hide
+         * No longer used. Should be removed once all dependencies have been updated.
          */
         public static final String ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED =
                 "enable_accessibility_global_gesture_enabled";
@@ -7490,6 +7647,14 @@
                "hdmi_control_auto_device_off_enabled";
 
        /**
+        * The interval in milliseconds at which location requests will be throttled when they are
+        * coming from the background.
+        * @hide
+        */
+       public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS =
+                "location_background_throttle_interval_ms";
+
+       /**
         * Whether TV will switch to MHL port when a mobile device is plugged in.
         * (0 = false, 1 = true)
         * @hide
@@ -8019,6 +8184,26 @@
         public static final String NETWORK_RECOMMENDATIONS_ENABLED =
                 "network_recommendations_enabled";
 
+        /**
+         * Value to specify if the Wi-Fi Framework should defer to
+         * {@link com.android.server.NetworkScoreService} for evaluating saved open networks.
+         *
+         * Type: int (0 for false, 1 for true)
+         * @hide
+         */
+        @SystemApi
+        public static final String CURATE_SAVED_OPEN_NETWORKS = "curate_saved_open_networks";
+
+        /**
+         * The number of milliseconds the {@link com.android.server.NetworkScoreService}
+         * will give a recommendation request to complete before returning a default response.
+         *
+         * Type: long
+         * @hide
+         */
+        public static final String NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS =
+                "network_recommendation_request_timeout_ms";
+
        /**
         * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
         * connectivity.
@@ -8603,6 +8788,26 @@
                 BLUETOOTH_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_";
 
         /**
+         * Activity manager specific settings.
+         * This is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "enforce_bg_check=true,max_cached_processes=24"
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * enforce_bg_check                     (boolean)
+         * max_cached_processes                 (int)
+         * </pre>
+         *
+         * <p>
+         * Type: string
+         * @hide
+         * @see com.android.server.am.ActivityManagerConstants
+         */
+        public static final String ACTIVITY_MANAGER_CONSTANTS = "activity_manager_constants";
+
+        /**
          * Device Idle (Doze) specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -8668,6 +8873,25 @@
         public static final String APP_IDLE_CONSTANTS = "app_idle_constants";
 
         /**
+         * Power manager specific settings.
+         * This is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "no_cached_wake_locks=1"
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * no_cached_wake_locks                 (boolean)
+         * </pre>
+         *
+         * <p>
+         * Type: string
+         * @hide
+         * @see com.android.server.power.PowerManagerConstants
+         */
+        public static final String POWER_MANAGER_CONSTANTS = "power_manager_constants";
+
+        /**
          * Alarm manager specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -9216,7 +9440,7 @@
         /**
          * WFC mode on roaming network.
          * <p>
-         * Type: int - see {@link WFC_IMS_MODE} for values
+         * Type: int - see {@link #WFC_IMS_MODE} for values
          *
          * @hide
          */
@@ -9362,11 +9586,11 @@
             DOCK_SOUNDS_ENABLED,
             CHARGING_SOUNDS_ENABLED,
             USB_MASS_STORAGE_ENABLED,
-            ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
+            NETWORK_RECOMMENDATIONS_ENABLED,
+            CURATE_SAVED_OPEN_NETWORKS,
+            WIFI_WAKEUP_ENABLED,
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
-            WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
             WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
-            WIFI_NUM_OPEN_NETWORKS_KEPT,
             EMERGENCY_TONE,
             CALL_AUTO_RETRY,
             DOCK_AUDIO_MEDIA_ENABLED,
@@ -9863,6 +10087,31 @@
          * @hide
          */
         public static final String CELL_ON = "cell_on";
+
+        /**
+         * Global settings which can be accessed by ephemeral apps.
+         * @hide
+         */
+        public static final Set<String> EPHEMERAL_SETTINGS = new ArraySet<>();
+        static {
+            EPHEMERAL_SETTINGS.add(WAIT_FOR_DEBUGGER);
+            EPHEMERAL_SETTINGS.add(DEVICE_PROVISIONED);
+            EPHEMERAL_SETTINGS.add(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
+            EPHEMERAL_SETTINGS.add(DEVELOPMENT_FORCE_RTL);
+            EPHEMERAL_SETTINGS.add(EPHEMERAL_COOKIE_MAX_SIZE_BYTES);
+        }
+
+        /**
+         * Whether to show the high temperature warning notification.
+         * @hide
+         */
+        public static final String SHOW_TEMPERATURE_WARNING = "show_temperature_warning";
+
+        /**
+         * Temperature at which the high temperature warning notification should be shown.
+         * @hide
+         */
+        public static final String WARNING_TEMPERATURE = "warning_temperature";
     }
 
     /**
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index c3cbcdc..a8b094e 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -30,6 +30,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.Voicemail;
+
 import java.util.List;
 
 /**
@@ -279,6 +280,44 @@
         public static final String LAST_MODIFIED = "last_modified";
 
         /**
+         * Flag to indicate the voicemail was backed up. The value will be 1 if backed up, 0 if
+         * not.
+         *
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String BACKED_UP = "backed_up";
+
+        /**
+         * Flag to indicate the voicemail was restored from a backup. The value will be 1 if
+         * restored, 0 if not.
+         *
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String RESTORED = "restored";
+
+        /**
+         * Flag to indicate the voicemail was marked as archived. Archived voicemail should not be
+         * deleted even if it no longer exist on the server. The value will be 1 if archived true, 0
+         * if not.
+         *
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String ARCHIVED = "archived";
+
+        /**
+         * Flag to indicate the voicemail is a OMTP voicemail handled by the {@link
+         * android.telephony.VisualVoicemailService}. The UI should only show OMTP voicemails from
+         * the current visual voicemail package. For example, the selection could be
+         * {@code WHERE (IS_OMTP_VOICEMAIL == 0) OR ( IS_OMTP_VOICEMAIL == 1 AND SOURCE_PACKAGE ==
+         * "current.vvm.package")}
+         *
+         * <P>Type: INTEGER (boolean)</P>
+         *
+         * @see android.telephony.TelephonyManager#getVisualVoicemailPackageName
+         */
+        public static final String IS_OMTP_VOICEMAIL = "is_omtp_voicemail";
+
+        /**
          * A convenience method to build voicemail URI specific to a source package by appending
          * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI.
          */
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index eb3d031..5461e6b 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -83,6 +83,12 @@
     public static final int KM_TAG_ROOT_OF_TRUST = KM_BYTES | 704;
     public static final int KM_TAG_UNIQUE_ID = KM_BYTES | 707;
     public static final int KM_TAG_ATTESTATION_CHALLENGE = KM_BYTES | 708;
+    public static final int KM_TAG_ATTESTATION_ID_BRAND = KM_BYTES | 710;
+    public static final int KM_TAG_ATTESTATION_ID_DEVICE = KM_BYTES | 711;
+    public static final int KM_TAG_ATTESTATION_ID_PRODUCT = KM_BYTES | 712;
+    public static final int KM_TAG_ATTESTATION_ID_SERIAL = KM_BYTES | 713;
+    public static final int KM_TAG_ATTESTATION_ID_IMEI = KM_BYTES | 714;
+    public static final int KM_TAG_ATTESTATION_ID_MEID = KM_BYTES | 715;
 
     public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000;
     public static final int KM_TAG_NONCE = KM_BYTES | 1001;
@@ -132,6 +138,8 @@
     public static final int KM_PURPOSE_DECRYPT = 1;
     public static final int KM_PURPOSE_SIGN = 2;
     public static final int KM_PURPOSE_VERIFY = 3;
+    public static final int KM_PURPOSE_DERIVE_KEY = 4;
+    public static final int KM_PURPOSE_WRAP_KEY = 5;
 
     // Key formats.
     public static final int KM_KEY_FORMAT_X509 = 0;
@@ -202,6 +210,7 @@
     public static final int KM_ERROR_INVALID_MAC_LENGTH = -57;
     public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
     public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
+    public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
     public static final int KM_ERROR_UNIMPLEMENTED = -100;
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -247,6 +256,7 @@
                 "Caller-provided IV not permitted");
         sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH,
                 "Invalid MAC or authentication tag length");
+        sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids");
         sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
         sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
     }
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index c2e980c..b5cb8f8 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -15,8 +15,6 @@
  */
 package android.service.autofill;
 
-import static android.service.voice.VoiceInteractionSession.KEY_FLAGS;
-import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE;
 import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
 import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
 
@@ -30,17 +28,21 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
 import android.util.Log;
 import android.view.autofill.AutoFillId;
+import android.view.autofill.Dataset;
 import android.view.autofill.FillResponse;
 
 import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
 
-// TODO(b/33197203): improve javadoc (of both class and methods); in particular, make sure the
-// life-cycle (and how state could be maintained on server-side) is well documented.
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+//TODO(b/33197203): improve javadoc (of both class and methods); in particular, make sure the
+//life-cycle (and how state could be maintained on server-side) is well documented.
 
 /**
  * Top-level service of the current auto-fill service for a given user.
@@ -49,8 +51,11 @@
  */
 public abstract class AutoFillService extends Service {
 
-    static final String TAG = "AutoFillService";
-    static final boolean DEBUG = true; // TODO: set to false once stable
+    private static final String TAG = "AutoFillService";
+    static final boolean DEBUG = true; // TODO(b/33197203): set to false once stable
+
+    // TODO(b/33197203): check for device's memory size instead of DEBUG?
+    static final boolean DEBUG_PENDING_CALLBACKS = DEBUG;
 
     /**
      * The {@link Intent} that must be declared as handled by the service.
@@ -61,9 +66,23 @@
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
 
+    /**
+     * Name under which a AutoFillService component publishes information about itself.
+     * This meta-data should reference an XML resource containing a
+     * <code>&lt;{@link
+     * android.R.styleable#AutoFillService autofill-service}&gt;</code> tag.
+     * This is a a sample XML file configuring an AutoFillService:
+     * <pre> &lt;autofill-service
+     *     android:settingsActivity="foo.bar.SettingsActivity"
+     *     . . .
+     * /&gt;</pre>
+     */
+    public static final String SERVICE_META_DATA = "android.autofill";
+
     // Internal bundle keys.
     /** @hide */ public static final String KEY_CALLBACK = "callback";
     /** @hide */ public static final String KEY_SAVABLE_IDS = "savable_ids";
+    /** @hide */ public static final String EXTRA_CRYPTO_OBJECT_ID = "crypto_object_id";
 
     // Prefix for public bundle keys.
     private static final String KEY_PREFIX = "android.service.autofill.extra.";
@@ -84,10 +103,32 @@
      */
     public static final String EXTRA_DATASET_EXTRAS = KEY_PREFIX + "DATASET_EXTRAS";
 
+    /**
+     * Used to indicate the user selected an action that requires authentication.
+     */
+    public static final int FLAG_AUTHENTICATION_REQUESTED = 1 << 0;
+
+    /**
+     * Used to indicate the user authentication succeeded.
+     */
+    public static final int FLAG_AUTHENTICATION_SUCCESS = 1 << 1;
+
+    /**
+     * Used to indicate the user authentication failed.
+     */
+    public static final int FLAG_AUTHENTICATION_ERROR = 1 << 2;
+
+    /**
+     * Used when the service requested Fingerprint authentication but such option is not available.
+     */
+    public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE  = 1 << 3;
+
     // Handler messages.
     private static final int MSG_CONNECT = 1;
-    private static final int MSG_AUTO_FILL_ACTIVITY = 2;
-    private static final int MSG_DISCONNECT = 3;
+    private static final int MSG_DISCONNECT = 2;
+    private static final int MSG_AUTO_FILL_ACTIVITY = 3;
+    private static final int MSG_AUTHENTICATE_FILL_RESPONSE = 4;
+    private static final int MSG_AUTHENTICATE_DATASET = 5;
 
     private final IAutoFillService mInterface = new IAutoFillService.Stub() {
 
@@ -100,6 +141,22 @@
         }
 
         @Override
+        public void authenticateFillResponse(Bundle extras, int flags) {
+            final Message msg = mHandlerCaller.obtainMessage(MSG_AUTHENTICATE_FILL_RESPONSE);
+            msg.arg1 = flags;
+            msg.obj = extras;
+            mHandlerCaller.sendMessage(msg);
+        }
+
+        @Override
+        public void authenticateDataset(Bundle extras, int flags) {
+            final Message msg = mHandlerCaller.obtainMessage(MSG_AUTHENTICATE_DATASET);
+            msg.arg1 = flags;
+            msg.obj = extras;
+            mHandlerCaller.sendMessage(msg);
+        }
+
+        @Override
         public void onConnected() {
             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CONNECT));
         }
@@ -126,6 +183,16 @@
                     final IAutoFillServerCallback callback = (IAutoFillServerCallback) args.arg3;
                     requestAutoFill(callback, structure, extras, flags);
                     break;
+                } case MSG_AUTHENTICATE_FILL_RESPONSE: {
+                    final int flags = msg.arg1;
+                    final Bundle extras = (Bundle) msg.obj;
+                    onFillResponseAuthenticationRequest(extras, flags);
+                    break;
+                } case MSG_AUTHENTICATE_DATASET: {
+                    final int flags = msg.arg1;
+                    final Bundle extras = (Bundle) msg.obj;
+                    onDatasetAuthenticationRequest(extras, flags);
+                    break;
                 } case MSG_DISCONNECT: {
                     onDisconnected();
                     break;
@@ -138,6 +205,10 @@
 
     private HandlerCaller mHandlerCaller;
 
+    // User for debugging purposes
+    private final List<CallbackHelper.Dumpable> mPendingCallbacks =
+            DEBUG_PENDING_CALLBACKS ? new ArrayList<>() : null;
+
     /**
      * {@inheritDoc}
      *
@@ -202,16 +273,84 @@
     public abstract void onSaveRequest(AssistStructure structure,
             Bundle data, CancellationSignal cancellationSignal, SaveCallback callback);
 
+    /**
+     * Called as result of the user action for a {@link FillResponse} that required authentication.
+     *
+     * <p>When the {@link FillResponse} required authentication through
+     * {@link android.view.autofill.FillResponse.Builder#requiresCustomAuthentication(Bundle, int)}, this
+     * call indicates the user is requesting the service to authenticate him/her (and {@code flags}
+     * contains {@link #FLAG_AUTHENTICATION_REQUESTED}), and {@code extras} contains the
+     * {@link Bundle} passed to that method.
+     *
+     * <p>When the {@link FillResponse} required authentication through
+     * {@link android.view.autofill.FillResponse.Builder#requiresFingerprintAuthentication(
+     * android.hardware.fingerprint.FingerprintManager.CryptoObject, Bundle, int)},
+     * {@code flags} this call contains the result of the fingerprint authentication (such as
+     * {@link #FLAG_AUTHENTICATION_SUCCESS}, {@link #FLAG_AUTHENTICATION_ERROR}, and
+     * {@link #FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}) and {@code extras} contains the
+     * {@link Bundle} passed to that method.
+     */
+    public void onFillResponseAuthenticationRequest(@SuppressWarnings("unused") Bundle extras,
+            int flags) {
+        if (DEBUG) Log.d(TAG, "onFillResponseAuthenticationRequest(): flags=" + flags);
+    }
+
+    /**
+     * Called as result of the user action for a {@link Dataset} that required authentication.
+     *
+     * <p>When the {@link Dataset} required authentication through
+     * {@link android.view.autofill.Dataset.Builder#requiresCustomAuthentication(Bundle, int)}, this
+     * call indicates the user is requesting the service to authenticate him/her (and {@code flags}
+     * contains {@link #FLAG_AUTHENTICATION_REQUESTED}), and {@code extras} contains the
+     * {@link Bundle} passed to that method.
+     *
+     * <p>When the {@link Dataset} required authentication through
+     * {@link android.view.autofill.Dataset.Builder#requiresFingerprintAuthentication(
+     * android.hardware.fingerprint.FingerprintManager.CryptoObject, Bundle, int)},
+     * {@code flags} this call contains the result of the fingerprint authentication (such as
+     * {@link #FLAG_AUTHENTICATION_SUCCESS}, {@link #FLAG_AUTHENTICATION_ERROR}, and
+     * {@link #FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}) and {@code extras} contains the
+     * {@link Bundle} passed to that method.
+     */
+    public void onDatasetAuthenticationRequest(@SuppressWarnings("unused") Bundle extras,
+            int flags) {
+        if (DEBUG) Log.d(TAG, "onDatasetAuthenticationRequest(): flags=" + flags);
+    }
+
+    // TODO(b/33197203): make it final and create another method classes could extend so it's
+    // guaranteed to dump the pending callbacks?
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mPendingCallbacks != null) {
+            pw.print("Number of pending callbacks: "); pw.println(mPendingCallbacks.size());
+            final String prefix = "  ";
+            for (int i = 0; i < mPendingCallbacks.size(); i++) {
+                final CallbackHelper.Dumpable cb = mPendingCallbacks.get(i);
+                pw.print('#'); pw.print(i + 1); pw.println(':');
+                cb.dump(prefix, pw);
+            }
+            pw.println();
+        } else {
+            pw.println("Dumping disabled");
+        }
+    }
+
     private void requestAutoFill(IAutoFillServerCallback callback, AssistStructure structure,
             Bundle data, int flags) {
         switch (flags) {
             case AUTO_FILL_FLAG_TYPE_FILL:
                 final FillCallback fillCallback = new FillCallback(callback);
+                if (DEBUG_PENDING_CALLBACKS) {
+                    addPendingCallback(fillCallback);
+                }
                 // TODO(b/33197203): hook up the cancelationSignal
                 onFillRequest(structure, data, new CancellationSignal(), fillCallback);
                 break;
             case AUTO_FILL_FLAG_TYPE_SAVE:
                 final SaveCallback saveCallback = new SaveCallback(callback);
+                if (DEBUG_PENDING_CALLBACKS) {
+                    addPendingCallback(saveCallback);
+                }
                 // TODO(b/33197203): hook up the cancelationSignal
                 onSaveRequest(structure, data, new CancellationSignal(), saveCallback);
                 break;
@@ -220,6 +359,22 @@
         }
     }
 
+    private void addPendingCallback(CallbackHelper.Dumpable callback) {
+        if (mPendingCallbacks == null) {
+            // Shouldn't happend since call is controlled by DEBUG_PENDING_CALLBACKS guard.
+            Log.wtf(TAG, "addPendingCallback(): mPendingCallbacks not set");
+            return;
+        }
+
+        if (DEBUG) Log.d(TAG, "Adding pending callback: " + callback);
+
+        callback.setFinalizer(() -> {
+            if (DEBUG) Log.d(TAG, "Removing pending callback: " + callback);
+            mPendingCallbacks.remove(callback);
+        });
+        mPendingCallbacks.add(callback);
+    }
+
     /**
      * Called when the Android system disconnects from the service.
      *
diff --git a/core/java/android/service/autofill/AutoFillServiceInfo.java b/core/java/android/service/autofill/AutoFillServiceInfo.java
index fe21615..fd957f1 100644
--- a/core/java/android/service/autofill/AutoFillServiceInfo.java
+++ b/core/java/android/service/autofill/AutoFillServiceInfo.java
@@ -16,20 +16,45 @@
 package android.service.autofill;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.os.RemoteException;
+import android.util.AndroidException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
-/** @hide */
+import com.android.internal.R;
+
+import java.io.IOException;
+
+// TODO(b/33197203 , b/33802548): add CTS tests
+/**
+ * {@link ServiceInfo} and meta-data about an {@link AutoFillService}.
+ *
+ * <p>Upon construction, if {@link #getParseError()} is {@code null}, then the service is configured
+ * correctly. Otherwise, {@link #getParseError()} indicates the parsing error.
+ *
+ * @hide
+ */
 public final class AutoFillServiceInfo {
+    static final String TAG = "AutoFillServiceInfo";
 
     private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
         try {
-            final ServiceInfo si =
-                    AppGlobals.getPackageManager().getServiceInfo(comp, 0, userHandle);
+            ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(
+                    comp,
+                    PackageManager.GET_META_DATA,
+                    userHandle);
             if (si != null) {
                 return si;
             }
@@ -38,29 +63,91 @@
         throw new PackageManager.NameNotFoundException(comp.toString());
     }
 
-    private String mParseError;
+    @Nullable
+    private final String mParseError;
 
-    private ServiceInfo mServiceInfo;
+    private final ServiceInfo mServiceInfo;
+    @Nullable
+    private final String mSettingsActivity;
 
-    private  AutoFillServiceInfo(ServiceInfo si) {
-        if (si == null) {
-            mParseError = "Service not available";
-            return;
-        }
-        if (!Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) {
-            mParseError = "Service does not require permission "
-                    + Manifest.permission.BIND_AUTO_FILL;
-            return;
-        }
-
-        mServiceInfo = si;
-    }
-
-    public AutoFillServiceInfo(ComponentName comp, int userHandle)
+    public AutoFillServiceInfo(PackageManager pm, ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
-        this(getServiceInfoOrThrow(comp, userHandle));
+        this(pm, getServiceInfoOrThrow(comp, userHandle));
     }
 
+    public AutoFillServiceInfo(PackageManager pm, ServiceInfo si) {
+        mServiceInfo = si;
+        TypedArray metaDataArray;
+        try {
+            metaDataArray = getMetaDataArray(pm, si);
+        } catch (AndroidException e) {
+            mParseError = e.getMessage();
+            mSettingsActivity = null;
+            Log.w(TAG, mParseError, e);
+            return;
+        }
+
+        mParseError = null;
+        if (metaDataArray != null) {
+            mSettingsActivity =
+                    metaDataArray.getString(R.styleable.AutoFillService_settingsActivity);
+            metaDataArray.recycle();
+        } else {
+            mSettingsActivity = null;
+        }
+    }
+
+    /**
+     * Gets the meta-data as a TypedArray, or null if not provided, or throws if invalid.
+     */
+    @Nullable
+    private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si)
+            throws AndroidException {
+        // Check for permissions.
+        if (!Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) {
+            throw new AndroidException(
+                "Service does not require permission " + Manifest.permission.BIND_AUTO_FILL);
+        }
+
+        // Get the AutoFill metadata, if declared.
+        XmlResourceParser parser = si.loadXmlMetaData(pm, AutoFillService.SERVICE_META_DATA);
+        if (parser == null) {
+            return null;
+        }
+
+        // Parse service info and get the <autofill-service> tag as an AttributeSet.
+        AttributeSet attrs;
+        try {
+            // Move the XML parser to the first tag.
+            try {
+                int type;
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && type != XmlPullParser.START_TAG) {
+                }
+            } catch (XmlPullParserException | IOException e) {
+                throw new AndroidException("Error parsing auto fill service meta-data: " + e, e);
+            }
+
+            if (!"autofill-service".equals(parser.getName())) {
+                throw new AndroidException("Meta-data does not start with autofill-service tag");
+            }
+            attrs = Xml.asAttributeSet(parser);
+
+            // Get resources required to read the AttributeSet.
+            Resources res;
+            try {
+                res = pm.getResourcesForApplication(si.applicationInfo);
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new AndroidException("Error getting application resources: " + e, e);
+            }
+
+            return res.obtainAttributes(attrs, R.styleable.AutoFillService);
+        } finally {
+            parser.close();
+        }
+    }
+
+    @Nullable
     public String getParseError() {
         return mParseError;
     }
@@ -68,4 +155,9 @@
     public ServiceInfo getServiceInfo() {
         return mServiceInfo;
     }
+
+    @Nullable
+    public String getSettingsActivity() {
+        return mSettingsActivity;
+    }
 }
diff --git a/core/java/android/service/autofill/CallbackHelper.java b/core/java/android/service/autofill/CallbackHelper.java
new file mode 100644
index 0000000..ded8f97
--- /dev/null
+++ b/core/java/android/service/autofill/CallbackHelper.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import java.io.PrintWriter;
+
+final class CallbackHelper {
+
+    static interface Dumpable {
+        void dump(String prefix, PrintWriter pw);
+        void setFinalizer(Finalizer f);
+    }
+
+    static interface Finalizer {
+        void gone();
+    }
+}
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 5a9a9f6..7cab7ae 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -17,28 +17,49 @@
 package android.service.autofill;
 
 import static android.service.autofill.AutoFillService.DEBUG;
+import static android.util.DebugUtils.flagsToString;
 
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.RemoteException;
+import android.service.autofill.CallbackHelper.Dumpable;
+import android.service.autofill.CallbackHelper.Finalizer;
 import android.util.Log;
+import android.view.autofill.Dataset;
 import android.view.autofill.FillResponse;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
+import java.io.PrintWriter;
+
 /**
  * Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being
  * auto-filled.
+ *
+ * <p>This class is thread safe.
  */
-public final class FillCallback {
+public final class FillCallback implements Dumpable {
 
     private static final String TAG = "FillCallback";
 
+    // NOTE: constants below are public so they can be used by flagsToString()
+    /** @hide */ public static final int STATE_INITIAL = 1 << 0;
+    /** @hide */ public static final int STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE = 1 << 1;
+    /** @hide */ public static final int STATE_WAITING_DATASET_AUTH_RESPONSE = 1 << 2;
+    /** @hide */ public static final int STATE_FINISHED_OK = 1 << 3;
+    /** @hide */ public static final int STATE_FINISHED_FAILURE = 1 << 4;
+    /** @hide */ public static final int STATE_FINISHED_ERROR = 1 << 5;
+    /** @hide */ public static final int STATE_FINISHED_AUTHENTICATED = 1 << 6;
+
     private final IAutoFillServerCallback mCallback;
 
-    private boolean mReplied = false;
+    @GuardedBy("mCallback")
+    private int mState = STATE_INITIAL;
+
+    @GuardedBy("mCallback")
+    private Finalizer mFinalizer;
 
     /** @hide */
     FillCallback(IAutoFillServerCallback callback) {
@@ -47,51 +68,174 @@
 
     /**
      * Notifies the Android System that an
-     * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, FillCallback)}
-     * was successfully fulfilled by the service.
+     * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
+     * android.os.CancellationSignal, FillCallback)} was successfully fulfilled by the service.
      *
      * @param response auto-fill information for that activity, or {@code null} when the activity
-     * cannot be auto-filled (for example, if it only contains read-only fields).
-     *
-     * @throws RuntimeException if an error occurred while calling the Android System.
+     * cannot be auto-filled (for example, if it only contains read-only fields). See
+     * {@link FillResponse} for examples.
      */
     public void onSuccess(@Nullable FillResponse response) {
-        if (DEBUG) Log.d(TAG, "onSuccess(): respose=" + response);
+        final boolean authRequired = response != null && response.isAuthRequired();
 
-        checkNotRepliedYet();
+        if (DEBUG) Log.d(TAG, "onSuccess(): authReq= " + authRequired + ", resp=" + response);
 
-        try {
-            mCallback.showResponse(response);
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
+        synchronized (mCallback) {
+            if (authRequired) {
+                assertOnStateLocked(STATE_INITIAL);
+            } else {
+                assertOnStateLocked(STATE_INITIAL | STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE
+                        | STATE_WAITING_DATASET_AUTH_RESPONSE);
+            }
+
+            try {
+                mCallback.showResponse(response);
+                if (authRequired) {
+                    mState = STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE;
+                } else {
+                    // Check if at least one dataset requires authentication.
+                    boolean waitingAuth = false;
+                    if (response != null) {
+                        for (Dataset dataset : response.getDatasets()) {
+                            if (dataset.isAuthRequired()) {
+                                waitingAuth = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (waitingAuth) {
+                        mState = STATE_WAITING_DATASET_AUTH_RESPONSE;
+                    } else {
+                        setFinalStateLocked(STATE_FINISHED_OK);
+                    }
+                }
+            } catch (RemoteException e) {
+                setFinalStateLocked(STATE_FINISHED_ERROR);
+                e.rethrowAsRuntimeException();
+            }
         }
     }
 
     /**
      * Notifies the Android System that an
-     * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, FillCallback)}
+     * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure,
+     * Bundle, android.os.CancellationSignal, FillCallback)}
      * could not be fulfilled by the service.
      *
      * @param message error message to be displayed to the user.
-     *
-     * @throws RuntimeException if an error occurred while calling the Android System.
      */
     public void onFailure(CharSequence message) {
         if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
 
-        checkNotRepliedYet();
         Preconditions.checkArgument(message != null, "message cannot be null");
 
-        try {
-            mCallback.showError(message.toString());
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
+        synchronized (mCallback) {
+            assertOnStateLocked(STATE_INITIAL | STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE
+                    | STATE_WAITING_DATASET_AUTH_RESPONSE);
+
+            try {
+                mCallback.showError(message);
+                setFinalStateLocked(STATE_FINISHED_FAILURE);
+            } catch (RemoteException e) {
+                setFinalStateLocked(STATE_FINISHED_ERROR);
+                e.rethrowAsRuntimeException();
+            }
         }
     }
 
-    // There can be only one!!
-    private void checkNotRepliedYet() {
-        Preconditions.checkState(!mReplied, "already replied");
-        mReplied = true;
+    /**
+     * Notifies the Android System when the user authenticated a {@link FillResponse} previously
+     * passed to {@link #onSuccess(FillResponse)}.
+     *
+     * @param flags must contain either
+     * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} or
+     * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}.
+     */
+    public void onFillResponseAuthentication(int flags) {
+        if (DEBUG) Log.d(TAG, "onFillResponseAuthentication(): flags=" + flags);
+
+        synchronized (mCallback) {
+            assertOnStateLocked(STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE);
+
+            try {
+                mCallback.unlockFillResponse(flags);
+                setFinalStateLocked(STATE_FINISHED_AUTHENTICATED);
+            } catch (RemoteException e) {
+                setFinalStateLocked(STATE_FINISHED_ERROR);
+                e.rethrowAsRuntimeException();
+            }
+        }
+    }
+
+    /**
+     * Notifies the Android System when the user authenticated a {@link Dataset} previously passed
+     * to {@link #onSuccess(FillResponse)}.
+     *
+     * @param dataset values to fill the activity with in case of successful authentication of a
+     * previously locked (and empty) dataset).
+     * @param flags must contain either
+     * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} or
+     * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}.
+     */
+    public void onDatasetAuthentication(@Nullable Dataset dataset, int flags) {
+        if (DEBUG) Log.d(TAG, "onDatasetAuthentication(): dataset=" + dataset + ", flags=" + flags);
+
+        synchronized (mCallback) {
+            assertOnStateLocked(STATE_WAITING_DATASET_AUTH_RESPONSE);
+
+            try {
+                mCallback.unlockDataset(dataset, flags);
+                setFinalStateLocked(STATE_FINISHED_AUTHENTICATED);
+            } catch (RemoteException e) {
+                setFinalStateLocked(STATE_FINISHED_ERROR);
+                e.rethrowAsRuntimeException();
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (!DEBUG) return super.toString();
+
+        return "FillCallback: [mState = " + mState + "]";
+    }
+
+    /** @hide */
+    @Override
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("FillCallback: mState="); pw.println(mState);
+    }
+
+    /** @hide */
+    @Override
+    public void setFinalizer(Finalizer f) {
+        synchronized (mCallback) {
+            mFinalizer = f;
+        }
+    }
+
+    /**
+     * Sets a final state (where the callback cannot be used anymore) and notifies the
+     * {@link Finalizer} (if any).
+     */
+    private void setFinalStateLocked(int state) {
+        if (DEBUG) Log.d(TAG, "setFinalState(): " + state);
+        mState = state;
+
+        if (mFinalizer != null) {
+            mFinalizer.gone();
+        }
+    }
+
+    // TODO(b/33197203): move and/or re-add state check logic on server side to avoid malicious app
+    // calling the callback on wrong state.
+
+    // Make sure callback method is called during the proper lifecycle state.
+    private void assertOnStateLocked(int flags) {
+        if (DEBUG) Log.d(TAG, "assertOnState(): current=" + mState + ", required=" + flags);
+
+        Preconditions.checkState((flags & mState) != 0,
+                "invalid state: required " + flagsToString(FillCallback.class, "STATE_", flags)
+                + ", current is " + flagsToString(FillCallback.class, "STATE_", mState));
     }
 }
diff --git a/core/java/android/service/autofill/IAutoFillAppCallback.aidl b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
index 629b1f0..cc83776 100644
--- a/core/java/android/service/autofill/IAutoFillAppCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
@@ -21,8 +21,15 @@
 import android.view.autofill.Dataset;
 
 /**
+ * Object running in the application process and responsible for auto-filling it.
+ *
  * @hide
  */
+// TODO(b/33197203): rename methods to make them more consistent with a callback, or rename class
+// itself
 oneway interface IAutoFillAppCallback {
+    /**
+      * Auto-fills the activity with the contents of a dataset.
+      */
     void autoFill(in Dataset dataset);
 }
diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl
index f8ae57b..ce42107 100644
--- a/core/java/android/service/autofill/IAutoFillManagerService.aidl
+++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl
@@ -16,7 +16,9 @@
 
 package android.service.autofill;
 
+import android.graphics.Rect;
 import android.os.Bundle;
+import android.view.autofill.AutoFillId;
 
 /**
  * Mediator between apps being auto-filled and auto-fill service implementations.
@@ -24,5 +26,9 @@
  * {@hide}
  */
 oneway interface IAutoFillManagerService {
+
+    void showAutoFillInput(in AutoFillId id, in Rect boundaries);
+
+    // TODO(b/33197203): remove it and refactor onShellCommand
     void requestAutoFill(IBinder activityToken, int userId, in Bundle extras, int flags);
 }
diff --git a/core/java/android/service/autofill/IAutoFillServerCallback.aidl b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
index 9d58c99..185c8f3 100644
--- a/core/java/android/service/autofill/IAutoFillServerCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
@@ -18,14 +18,23 @@
 
 import java.util.List;
 
+import android.os.Bundle;
 import android.view.autofill.AutoFillId;
+import android.view.autofill.Dataset;
 import android.view.autofill.FillResponse;
 
 /**
+ * Object running in the AutoFillService process and used to communicate back with system_server.
+ *
  * @hide
  */
+// TODO(b/33197203): rename methods to make them more consistent with a callback, or rename class
+// itself
 oneway interface IAutoFillServerCallback {
+    // TODO(b/33197203): document methods
     void showResponse(in FillResponse response);
-    void showError(String message);
+    void showError(CharSequence message);
     void highlightSavedFields(in AutoFillId[] ids);
+    void unlockFillResponse(int flags);
+    void unlockDataset(in Dataset dataset, int flags);
 }
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index a1f22bf..fa9786a 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -24,9 +24,13 @@
 /**
  * @hide
  */
+// TODO(b/33197203): document class and methods
 oneway interface IAutoFillService {
+    // TODO(b/33197203): rename method to make them more consistent
     void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback,
                   in Bundle extras, int flags);
+    void authenticateFillResponse(in Bundle extras, int flags);
+    void authenticateDataset(in Bundle extras, int flags);
     void onConnected();
     void onDisconnected();
 }
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 627d74c..d5022d8 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -21,25 +21,35 @@
 import android.app.Activity;
 import android.app.assist.AssistStructure.ViewNode;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.RemoteException;
+import android.service.autofill.CallbackHelper.Dumpable;
+import android.service.autofill.CallbackHelper.Finalizer;
 import android.util.Log;
 import android.view.autofill.AutoFillId;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
+import java.io.PrintWriter;
+
 /**
  * Handles save requests from the {@link AutoFillService} into the {@link Activity} being
  * auto-filled.
+ *
+ * <p>This class is thread safe.
  */
-public final class SaveCallback {
+public final class SaveCallback implements Dumpable {
 
     private static final String TAG = "SaveCallback";
 
     private final IAutoFillServerCallback mCallback;
 
+    @GuardedBy("mCallback")
     private boolean mReplied = false;
 
+    @GuardedBy("mCallback")
+    private Finalizer mFinalizer;
+
     /** @hide */
     SaveCallback(IAutoFillServerCallback callback) {
         mCallback = callback;
@@ -47,8 +57,8 @@
 
     /**
      * Notifies the Android System that an
-     * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, SaveCallback)}
-     * was successfully fulfilled by the service.
+     * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
+     * android.os.CancellationSignal, SaveCallback)} was successfully fulfilled by the service.
      *
      * @param ids ids ({@link ViewNode#getAutoFillId()}) of the fields that were saved.
      *
@@ -58,21 +68,24 @@
         if (DEBUG) Log.d(TAG, "onSuccess(): ids=" + ((ids == null) ? "null" : ids.length));
 
         Preconditions.checkArgument(ids != null, "ids cannot be null");
-        checkNotRepliedYet();
-
         Preconditions.checkArgument(ids.length > 0, "ids cannot be empty");
 
-        try {
-            mCallback.highlightSavedFields(ids);
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
+        synchronized (mCallback) {
+            checkNotRepliedYetLocked();
+            try {
+                mCallback.highlightSavedFields(ids);
+            } catch (RemoteException e) {
+                e.rethrowAsRuntimeException();
+            } finally {
+                setRepliedLocked();
+            }
         }
     }
 
     /**
      * Notifies the Android System that an
-     * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, SaveCallback)}
-     * could not be fulfilled by the service.
+     * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
+     * android.os.CancellationSignal, SaveCallback)} could not be fulfilled by the service.
      *
      * @param message error message to be displayed to the user.
      *
@@ -82,18 +95,53 @@
         if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
 
         Preconditions.checkArgument(message != null, "message cannot be null");
-        checkNotRepliedYet();
 
-        try {
-            mCallback.showError(message.toString());
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
+        synchronized (mCallback) {
+            checkNotRepliedYetLocked();
+
+            try {
+                mCallback.showError(message);
+            } catch (RemoteException e) {
+                e.rethrowAsRuntimeException();
+            } finally {
+                setRepliedLocked();
+            }
         }
     }
 
+    /** @hide */
+    @Override
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("SaveCallback: mReplied="); pw.println(mReplied);
+    }
+
+    /** @hide */
+    @Override
+    public void setFinalizer(Finalizer f) {
+        synchronized (mCallback) {
+            mFinalizer = f;
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (!DEBUG) return super.toString();
+
+        return "SaveCallback: [mReplied= " + mReplied + "]";
+    }
+
     // There can be only one!!
-    private void checkNotRepliedYet() {
+    private void checkNotRepliedYetLocked() {
         Preconditions.checkState(!mReplied, "already replied");
+    }
+
+    private void setRepliedLocked() {
+        if (DEBUG) Log.d(TAG, "setReplied()");
+
         mReplied = true;
+
+        if (mFinalizer != null) {
+            mFinalizer.gone();
+        }
     }
 }
diff --git a/core/java/android/service/gatekeeper/GateKeeperResponse.java b/core/java/android/service/gatekeeper/GateKeeperResponse.java
index a512957..287dc76 100644
--- a/core/java/android/service/gatekeeper/GateKeeperResponse.java
+++ b/core/java/android/service/gatekeeper/GateKeeperResponse.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Response object for a GateKeeper verification request.
  * @hide
@@ -35,12 +37,28 @@
     private byte[] mPayload;
     private boolean mShouldReEnroll;
 
+    /** Default constructor for response with generic response code **/
     private GateKeeperResponse(int responseCode) {
         mResponseCode = responseCode;
     }
 
-    private GateKeeperResponse(int responseCode, int timeout) {
-        mResponseCode = responseCode;
+    @VisibleForTesting
+    public static GateKeeperResponse createGenericResponse(int responseCode) {
+        return new GateKeeperResponse(responseCode);
+    }
+
+    private static GateKeeperResponse createRetryResponse(int timeout) {
+        GateKeeperResponse response = new GateKeeperResponse(RESPONSE_RETRY);
+        response.mTimeout = timeout;
+        return response;
+    }
+
+    @VisibleForTesting
+    public static GateKeeperResponse createOkResponse(byte[] payload, boolean shouldReEnroll) {
+        GateKeeperResponse response = new GateKeeperResponse(RESPONSE_OK);
+        response.mPayload = payload;
+        response.mShouldReEnroll = shouldReEnroll;
+        return response;
     }
 
     @Override
@@ -53,17 +71,20 @@
         @Override
         public GateKeeperResponse createFromParcel(Parcel source) {
             int responseCode = source.readInt();
-            GateKeeperResponse response = new GateKeeperResponse(responseCode);
+            final GateKeeperResponse response;
             if (responseCode == RESPONSE_RETRY) {
-                response.setTimeout(source.readInt());
+                response = createRetryResponse(source.readInt());
             } else if (responseCode == RESPONSE_OK) {
-                response.setShouldReEnroll(source.readInt() == 1);
+                final boolean shouldReEnroll = source.readInt() == 1;
+                byte[] payload = null;
                 int size = source.readInt();
                 if (size > 0) {
-                    byte[] payload = new byte[size];
+                    payload = new byte[size];
                     source.readByteArray(payload);
-                    response.setPayload(payload);
                 }
+                response = createOkResponse(payload, shouldReEnroll);
+            } else {
+                response = createGenericResponse(responseCode);
             }
             return response;
         }
@@ -104,17 +125,4 @@
     public int getResponseCode() {
         return mResponseCode;
     }
-
-    private void setTimeout(int timeout) {
-        mTimeout = timeout;
-    }
-
-    private void setShouldReEnroll(boolean shouldReEnroll) {
-        mShouldReEnroll = shouldReEnroll;
-    }
-
-    private void setPayload(byte[] payload) {
-        mPayload = payload;
-    }
-
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index d7a02a8..cecdbee 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -119,6 +119,24 @@
     }
 
     /**
+     * Inform the notification manager about un-snoozing a specific notification.
+     * <p>
+     * This should only be used for notifications snoozed by this listener using
+     * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
+     * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
+     * notification.
+     * @param key The key of the notification to snooze
+     */
+    public final void unsnoozeNotification(String key) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().unsnoozeNotificationFromAssistant(mWrapper, key);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
      * Creates a notification channel that notifications can be posted to for a given package.
      *
      * @param pkg The package to create a channel for.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 694837e..22ad83a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -183,8 +183,8 @@
     public static final int REASON_CHANNEL_BANNED = 17;
     /** Notification was snoozed. */
     public static final int REASON_SNOOZED = 18;
-    /** Notification no longer visible because of user switch */
-    public static final int REASON_USER_SWITCH = 19;
+    /** Notification was canceled due to timeout */
+    public static final int REASON_TIMEOUT = 19;
 
     /**
      * The full trim of the StatusBarNotification including all its features.
@@ -562,43 +562,6 @@
         }
     }
 
-    /**
-     * Inform the notification manager about snoozing a specific notification.
-     * <p>
-     * Use this to snooze a notification for an indeterminate time.  Upon being informed, the
-     * notification manager will actually remove the notification and you will get an
-     * {@link #onNotificationRemoved(StatusBarNotification)} callback. When the
-     * snoozing period expires, you will get a
-     * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
-     * notification. Use {@link #unsnoozeNotification(String)} to restore the notification.
-     * @param key The key of the notification to snooze
-     */
-    public final void snoozeNotification(String key) {
-        if (!isBound()) return;
-        try {
-            getNotificationInterface().snoozeNotificationFromListener(mWrapper, key);
-        } catch (android.os.RemoteException ex) {
-            Log.v(TAG, "Unable to contact notification manager", ex);
-        }
-    }
-
-    /**
-     * Inform the notification manager about un-snoozing a specific notification.
-     * <p>
-     * This should only be used for notifications snoozed by this listener using
-     * {@link #snoozeNotification(String)}. Once un-snoozed, you will get a
-     * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
-     * notification.
-     * @param key The key of the notification to snooze
-     */
-    public final void unsnoozeNotification(String key) {
-        if (!isBound()) return;
-        try {
-            getNotificationInterface().unsnoozeNotificationFromListener(mWrapper, key);
-        } catch (android.os.RemoteException ex) {
-            Log.v(TAG, "Unable to contact notification manager", ex);
-        }
-    }
 
     /**
      * Inform the notification manager that these notifications have been viewed by the
@@ -663,6 +626,26 @@
     }
 
     /**
+     * Like {@link #getActiveNotifications()}, but returns the list of currently snoozed
+     * notifications, for all users this listener has access to.
+     *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
+     * @return An array of active notifications, sorted in natural order.
+     */
+    public final StatusBarNotification[] getSnoozedNotifications() {
+        try {
+            ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
+                    .getSnoozedNotificationsFromListener(mWrapper, TRIM_FULL);
+            return cleanUpNotificationList(parceledList);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+        return null;
+    }
+
+    /**
      * Request the list of outstanding notifications (that is, those that are visible to the
      * current user). Useful when you don't know what's already been posted.
      *
@@ -711,36 +694,41 @@
         try {
             ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
                     .getActiveNotificationsFromListener(mWrapper, keys, trim);
-            List<StatusBarNotification> list = parceledList.getList();
-            ArrayList<StatusBarNotification> corruptNotifications = null;
-            int N = list.size();
-            for (int i = 0; i < N; i++) {
-                StatusBarNotification sbn = list.get(i);
-                Notification notification = sbn.getNotification();
-                try {
-                    // convert icon metadata to legacy format for older clients
-                    createLegacyIconExtras(notification);
-                    // populate remote views for older clients.
-                    maybePopulateRemoteViews(notification);
-                } catch (IllegalArgumentException e) {
-                    if (corruptNotifications == null) {
-                        corruptNotifications = new ArrayList<>(N);
-                    }
-                    corruptNotifications.add(sbn);
-                    Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
-                            sbn.getPackageName());
-                }
-            }
-            if (corruptNotifications != null) {
-                list.removeAll(corruptNotifications);
-            }
-            return list.toArray(new StatusBarNotification[list.size()]);
+            return cleanUpNotificationList(parceledList);
         } catch (android.os.RemoteException ex) {
             Log.v(TAG, "Unable to contact notification manager", ex);
         }
         return null;
     }
 
+    private StatusBarNotification[] cleanUpNotificationList(
+            ParceledListSlice<StatusBarNotification> parceledList) {
+        List<StatusBarNotification> list = parceledList.getList();
+        ArrayList<StatusBarNotification> corruptNotifications = null;
+        int N = list.size();
+        for (int i = 0; i < N; i++) {
+            StatusBarNotification sbn = list.get(i);
+            Notification notification = sbn.getNotification();
+            try {
+                // convert icon metadata to legacy format for older clients
+                createLegacyIconExtras(notification);
+                // populate remote views for older clients.
+                maybePopulateRemoteViews(notification);
+            } catch (IllegalArgumentException e) {
+                if (corruptNotifications == null) {
+                    corruptNotifications = new ArrayList<>(N);
+                }
+                corruptNotifications.add(sbn);
+                Log.w(TAG, "get(Active/Snoozed)Notifications: can't rebuild notification from " +
+                        sbn.getPackageName());
+            }
+        }
+        if (corruptNotifications != null) {
+            list.removeAll(corruptNotifications);
+        }
+        return list.toArray(new StatusBarNotification[list.size()]);
+    }
+
     /**
      * Gets the set of hints representing current state.
      *
@@ -937,7 +925,7 @@
     }
 
     /**
-     * Request that the listener be rebound, after a previous call to (@link requestUnbind).
+     * Request that the listener be rebound, after a previous call to {@link #requestUnbind}.
      *
      * <p>This method will fail for listeners that have
      * not been granted the permission by the user.
@@ -1166,11 +1154,12 @@
         // System specified group key.
         private String mOverrideGroupKey;
         // Notification assistant channel override.
-        private NotificationChannel mOverrideChannel;
+        private NotificationChannel mChannel;
         // Notification assistant people override.
         private ArrayList<String> mOverridePeople;
         // Notification assistant snooze criteria.
         private ArrayList<SnoozeCriterion> mSnoozeCriteria;
+        private boolean mShowBadge;
 
         public Ranking() {}
 
@@ -1200,7 +1189,7 @@
         }
 
         /**
-         * Returns the user specificed visibility for the package that posted
+         * Returns the user specified visibility for the package that posted
          * this notification, or
          * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
          * no such preference has been expressed.
@@ -1233,7 +1222,7 @@
          * Returns the importance of the notification, which dictates its
          * modes of presentation, see: {@link NotificationManager#IMPORTANCE_DEFAULT}, etc.
          *
-         * @return the rank of the notification
+         * @return the importance of the notification
          */
         public @NotificationManager.Importance int getImportance() {
             return mImportance;
@@ -1258,12 +1247,11 @@
         }
 
         /**
-         * If the {@link NotificationAssistantService} has overridden the channel this notification
-         * was posted to, then this will not match the channel provided by the posting application
-         * and this should be used to determine the interruptiveness of the notification instead.
+         * Returns the notification channel this notification was posted to, which dictates
+         * notification behavior and presentation.
          */
         public NotificationChannel getChannel() {
-            return mOverrideChannel;
+            return mChannel;
         }
 
         /**
@@ -1283,11 +1271,20 @@
             return mSnoozeCriteria;
         }
 
+        /**
+         * Returns whether this notification can be displayed as a badge.
+         *
+         * @return true if the notification can be displayed as a badge, false otherwise.
+         */
+        public boolean canShowBadge() {
+            return mShowBadge;
+        }
+
         private void populate(String key, int rank, boolean matchesInterruptionFilter,
                 int visibilityOverride, int suppressedVisualEffects, int importance,
                 CharSequence explanation, String overrideGroupKey,
-                NotificationChannel overrideChannel, ArrayList<String> overridePeople,
-                ArrayList<SnoozeCriterion> snoozeCriteria) {
+                NotificationChannel channel, ArrayList<String> overridePeople,
+                ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1297,9 +1294,10 @@
             mImportance = importance;
             mImportanceExplanation = explanation;
             mOverrideGroupKey = overrideGroupKey;
-            mOverrideChannel = overrideChannel;
+            mChannel = channel;
             mOverridePeople = overridePeople;
             mSnoozeCriteria = snoozeCriteria;
+            mShowBadge = showBadge;
         }
 
         /**
@@ -1343,9 +1341,10 @@
         private ArrayMap<String, Integer> mImportance;
         private ArrayMap<String, String> mImportanceExplanation;
         private ArrayMap<String, String> mOverrideGroupKeys;
-        private ArrayMap<String, NotificationChannel> mOverrideChannels;
+        private ArrayMap<String, NotificationChannel> mChannels;
         private ArrayMap<String, ArrayList<String>> mOverridePeople;
         private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
+        private ArrayMap<String, Boolean> mShowBadge;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1373,7 +1372,8 @@
             outRanking.populate(key, rank, !isIntercepted(key),
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
-                    getOverrideChannel(key), getOverridePeople(key), getSnoozeCriteria(key));
+                    getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
+                    getShowBadge(key));
             return rank >= 0;
         }
 
@@ -1453,13 +1453,13 @@
             return mOverrideGroupKeys.get(key);
         }
 
-        private NotificationChannel getOverrideChannel(String key) {
+        private NotificationChannel getChannel(String key) {
             synchronized (this) {
-                if (mOverrideChannels == null) {
-                    buildOverrideChannelsLocked();
+                if (mChannels == null) {
+                    buildChannelsLocked();
                 }
             }
-            return mOverrideChannels.get(key);
+            return mChannels.get(key);
         }
 
         private ArrayList<String> getOverridePeople(String key) {
@@ -1480,6 +1480,16 @@
             return mSnoozeCriteria.get(key);
         }
 
+        private boolean getShowBadge(String key) {
+            synchronized (this) {
+                if (mShowBadge == null) {
+                    buildShowBadgeLocked();
+                }
+            }
+            Boolean showBadge = mShowBadge.get(key);
+            return showBadge == null ? false : showBadge.booleanValue();
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1544,11 +1554,11 @@
         }
 
         // Locked by 'this'
-        private void buildOverrideChannelsLocked() {
-            Bundle overrideChannels = mRankingUpdate.getOverrideChannels();
-            mOverrideChannels = new ArrayMap<>(overrideChannels.size());
-            for (String key : overrideChannels.keySet()) {
-                mOverrideChannels.put(key, overrideChannels.getParcelable(key));
+        private void buildChannelsLocked() {
+            Bundle channels = mRankingUpdate.getChannels();
+            mChannels = new ArrayMap<>(channels.size());
+            for (String key : channels.keySet()) {
+                mChannels.put(key, channels.getParcelable(key));
             }
         }
 
@@ -1570,6 +1580,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildShowBadgeLocked() {
+            Bundle showBadge = mRankingUpdate.getShowBadge();
+            mShowBadge = new ArrayMap<>(showBadge.size());
+            for (String key : showBadge.keySet()) {
+                mShowBadge.put(key, showBadge.getBoolean(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index a2cdeff..326b212 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -31,14 +31,16 @@
     private final int[] mImportance;
     private final Bundle mImportanceExplanation;
     private final Bundle mOverrideGroupKeys;
-    private final Bundle mOverrideChannels;
+    private final Bundle mChannels;
     private final Bundle mOverridePeople;
     private final Bundle mSnoozeCriteria;
+    private final Bundle mShowBadge;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
-            Bundle overrideChannels, Bundle overridePeople, Bundle snoozeCriteria) {
+            Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
+            Bundle showBadge) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -46,9 +48,10 @@
         mImportance = importance;
         mImportanceExplanation = explanation;
         mOverrideGroupKeys = overrideGroupKeys;
-        mOverrideChannels = overrideChannels;
+        mChannels = channels;
         mOverridePeople = overridePeople;
         mSnoozeCriteria = snoozeCriteria;
+        mShowBadge = showBadge;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -60,9 +63,10 @@
         in.readIntArray(mImportance);
         mImportanceExplanation = in.readBundle();
         mOverrideGroupKeys = in.readBundle();
-        mOverrideChannels = in.readBundle();
+        mChannels = in.readBundle();
         mOverridePeople = in.readBundle();
         mSnoozeCriteria = in.readBundle();
+        mShowBadge = in.readBundle();
     }
 
     @Override
@@ -79,9 +83,10 @@
         out.writeIntArray(mImportance);
         out.writeBundle(mImportanceExplanation);
         out.writeBundle(mOverrideGroupKeys);
-        out.writeBundle(mOverrideChannels);
+        out.writeBundle(mChannels);
         out.writeBundle(mOverridePeople);
         out.writeBundle(mSnoozeCriteria);
+        out.writeBundle(mShowBadge);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -123,8 +128,8 @@
         return mOverrideGroupKeys;
     }
 
-    public Bundle getOverrideChannels() {
-        return mOverrideChannels;
+    public Bundle getChannels() {
+        return mChannels;
     }
 
     public Bundle getOverridePeople() {
@@ -134,4 +139,8 @@
     public Bundle getSnoozeCriteria() {
         return mSnoozeCriteria;
     }
+
+    public Bundle getShowBadge() {
+        return mShowBadge;
+    }
 }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 6276af3..85baf4e 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -43,21 +43,18 @@
     private final Notification notification;
     private final UserHandle user;
     private final long postTime;
-    private final NotificationChannel channel;
 
     private Context mContext; // used for inflation & icon expansion
 
     /** @hide */
-    public StatusBarNotification(String pkg, String opPkg, NotificationChannel channel, int id,
+    public StatusBarNotification(String pkg, String opPkg, int id,
             String tag, int uid, int initialPid, Notification notification, UserHandle user,
             String overrideGroupKey, long postTime) {
         if (pkg == null) throw new NullPointerException();
         if (notification == null) throw new NullPointerException();
-        if (channel == null) throw new IllegalArgumentException();
 
         this.pkg = pkg;
         this.opPkg = opPkg;
-        this.channel = channel;
         this.id = id;
         this.tag = tag;
         this.uid = uid;
@@ -88,7 +85,6 @@
         this.postTime = postTime;
         this.key = key();
         this.groupKey = groupKey();
-        this.channel = null;
     }
 
     public StatusBarNotification(Parcel in) {
@@ -112,7 +108,6 @@
         }
         this.key = key();
         this.groupKey = groupKey();
-        this.channel = NotificationChannel.CREATOR.createFromParcel(in);
     }
 
     private String key() {
@@ -182,7 +177,6 @@
         } else {
             out.writeInt(0);
         }
-        this.channel.writeToParcel(out, flags);
     }
 
     public int describeContents() {
@@ -209,14 +203,14 @@
     public StatusBarNotification cloneLight() {
         final Notification no = new Notification();
         this.notification.cloneInto(no, false); // light copy
-        return new StatusBarNotification(this.pkg, this.opPkg, this.channel,
+        return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
                 no, this.user, this.overrideGroupKey, this.postTime);
     }
 
     @Override
     public StatusBarNotification clone() {
-        return new StatusBarNotification(this.pkg, this.opPkg, this.channel,
+        return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
                 this.notification.clone(), this.user, this.overrideGroupKey, this.postTime);
     }
@@ -336,13 +330,6 @@
     }
 
     /**
-     * Returns the channel this notification was posted to.
-     */
-    public NotificationChannel getNotificationChannel() {
-        return channel;
-    }
-
-    /**
      * @hide
      */
     public Context getPackageContext(Context context) {
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index f07d0d0..22b4d09 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -24,6 +24,7 @@
  */
 interface ITrustAgentService {
     oneway void onUnlockAttempt(boolean successful);
+    oneway void onUnlockLockout(int timeoutMs);
     oneway void onTrustTimeout();
     oneway void onDeviceLocked();
     oneway void onDeviceUnlocked();
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 9d7ffad..0d5177d 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -123,6 +123,7 @@
     private static final int MSG_TRUST_TIMEOUT = 3;
     private static final int MSG_DEVICE_LOCKED = 4;
     private static final int MSG_DEVICE_UNLOCKED = 5;
+    private static final int MSG_UNLOCK_LOCKOUT = 6;
 
     /**
      * Class containing raw data for a given configuration request.
@@ -151,6 +152,9 @@
                 case MSG_UNLOCK_ATTEMPT:
                     onUnlockAttempt(msg.arg1 != 0);
                     break;
+                case MSG_UNLOCK_LOCKOUT:
+                    onDeviceUnlockLockout(msg.arg1);
+                    break;
                 case MSG_CONFIGURE:
                     ConfigurationData data = (ConfigurationData) msg.obj;
                     boolean result = onConfigure(data.options);
@@ -226,6 +230,21 @@
     public void onDeviceUnlocked() {
     }
 
+    /**
+     * Called when the device enters a temporary unlock lockout.
+     *
+     * <p>This occurs when the user has consecutively failed to unlock the device too many times,
+     * and must wait until a timeout has passed to perform another attempt. The user may then only
+     * use strong authentication mechanisms (PIN, pattern or password) to unlock the device.
+     * Calls to {@link #grantTrust(CharSequence, long, int)} will be ignored until the user has
+     * unlocked the device and {@link #onDeviceUnlocked()} is called.
+     *
+     * @param timeoutMs The amount of time, in milliseconds, that needs to elapse before the user
+     *    can attempt to unlock the device again.
+     */
+    public void onDeviceUnlockLockout(long timeoutMs) {
+    }
+
     private void onError(String msg) {
         Slog.v(TAG, "Remote exception while " + msg);
     }
@@ -366,6 +385,11 @@
             mHandler.obtainMessage(MSG_UNLOCK_ATTEMPT, successful ? 1 : 0, 0).sendToTarget();
         }
 
+        @Override
+        public void onUnlockLockout(int timeoutMs) {
+            mHandler.obtainMessage(MSG_UNLOCK_LOCKOUT, timeoutMs, 0).sendToTarget();
+        }
+
         @Override /* Binder API */
         public void onTrustTimeout() {
             mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT);
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index 9920ea1..be5851c 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -164,7 +164,7 @@
         // all data from the audioTrack has been sent to the mixer, so
         // it's safe to release at this point.
         if (DBG) Log.d(TAG, "Releasing audio track [" + track.hashCode() + "]");
-        synchronized(mAudioTrackLock) {
+        synchronized (mAudioTrackLock) {
             mAudioTrack = null;
         }
         track.release();
@@ -340,4 +340,25 @@
         return value < min ? min : (value < max ? value : max);
     }
 
+    /**
+     * @see
+     *     AudioTrack#setPlaybackPositionUpdateListener(AudioTrack.OnPlaybackPositionUpdateListener).
+     */
+    public void setPlaybackPositionUpdateListener(
+            AudioTrack.OnPlaybackPositionUpdateListener listener) {
+        synchronized (mAudioTrackLock) {
+            if (mAudioTrack != null) {
+                mAudioTrack.setPlaybackPositionUpdateListener(listener);
+            }
+        }
+    }
+
+    /** @see AudioTrack#setNotificationMarkerPosition(int). */
+    public void setNotificationMarkerPosition(int frames) {
+        synchronized (mAudioTrackLock) {
+            if (mAudioTrack != null) {
+                mAudioTrack.setNotificationMarkerPosition(frames);
+            }
+        }
+    }
 }
diff --git a/core/java/android/speech/tts/ITextToSpeechCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
index 4e3acf6..edb6e48 100644
--- a/core/java/android/speech/tts/ITextToSpeechCallback.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
@@ -83,4 +83,19 @@
      * callback.
      */
     void onAudioAvailable(String utteranceId, in byte[] audio);
+
+    /**
+     * Tells the client that the engine is about to speak the specified range of the utterance.
+     *
+     * <p>
+     * Only called if the engine supplies timing information by calling
+     * {@link SynthesisCallback#rangeStart(int, int, int)} and only when the request is played back
+     * by the service, not when using {@link android.speech.tts.TextToSpeech#synthesizeToFile}.
+     * </p>
+     *
+     * @param utteranceId Unique id identifying the synthesis request.
+     * @param start The start character index of the range in the utterance text.
+     * @param end The end character index of the range (exclusive) in the utterance text.
+     */
+    void onUtteranceRangeStart(String utteranceId, int start, int end);
 }
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index 778aa86..9e24b09 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -271,4 +271,12 @@
             mStatusCode = errorCode;
         }
     }
+
+    public void rangeStart(int markerInFrames, int start, int end) {
+        if (mItem == null) {
+            Log.e(TAG, "mItem is null");
+            return;
+        }
+        mItem.rangeStart(markerInFrames, start, end);
+    }
 }
diff --git a/core/java/android/speech/tts/SynthesisCallback.java b/core/java/android/speech/tts/SynthesisCallback.java
index 2fd8499..8b74ed7 100644
--- a/core/java/android/speech/tts/SynthesisCallback.java
+++ b/core/java/android/speech/tts/SynthesisCallback.java
@@ -142,4 +142,26 @@
      * <p>Useful for checking if a fallback from network request is possible.
      */
     boolean hasFinished();
+
+    /**
+     * The service may call this method to provide timing information about the spoken text.
+     *
+     * <p>Calling this method means that at the given audio frame, the given range of the input is
+     * about to be spoken. If this method is called the client will receive a callback on the
+     * listener ({@link UtteranceProgressListener#onUtteranceRangeStart}) at the moment that frame
+     * has been reached by the playback head.
+     *
+     * <p>The markerInFrames is a frame index into the audio for this synthesis request, i.e. into
+     * the concatenation of the audio bytes sent to audioAvailable for this synthesis request. The
+     * definition of a frame depends on the format given by {@link #start}. See {@link AudioFormat}
+     * for more information.
+     *
+     * <p>This method should only be called on the synthesis thread, while in {@link
+     * TextToSpeechService#onSynthesizeText}.
+     *
+     * @param markerInFrames The position in frames in the audio where this range is spoken.
+     * @param start The start index of the range in the input text.
+     * @param end The end index (exclusive) of the range in the input text.
+     */
+    default void rangeStart(int markerInFrames, int start, int end) {}
 }
diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
index 7423933..cb5f220 100644
--- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
@@ -17,18 +17,21 @@
 
 import android.speech.tts.TextToSpeechService.AudioOutputParams;
 import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
+import android.media.AudioTrack;
 import android.util.Log;
 
 import java.util.LinkedList;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
- * Manages the playback of a list of byte arrays representing audio data
- * that are queued by the engine to an audio track.
+ * Manages the playback of a list of byte arrays representing audio data that are queued by the
+ * engine to an audio track.
  */
-final class SynthesisPlaybackQueueItem extends PlaybackQueueItem {
+final class SynthesisPlaybackQueueItem extends PlaybackQueueItem
+        implements AudioTrack.OnPlaybackPositionUpdateListener {
     private static final String TAG = "TTS.SynthQueueItem";
     private static final boolean DBG = false;
 
@@ -63,6 +66,10 @@
     private final BlockingAudioTrack mAudioTrack;
     private final AbstractEventLogger mLogger;
 
+    // Stores a queue of markers. When the marker in front is reached the client is informed and we
+    // wait for the next one.
+    private ConcurrentLinkedQueue<ProgressMarker> markerList = new ConcurrentLinkedQueue<>();
+
     SynthesisPlaybackQueueItem(AudioOutputParams audioParams, int sampleRate,
             int audioFormat, int channelCount, UtteranceProgressDispatcher dispatcher,
             Object callerIdentity, AbstractEventLogger logger) {
@@ -89,6 +96,8 @@
             return;
         }
 
+        mAudioTrack.setPlaybackPositionUpdateListener(this);
+
         try {
             byte[] buffer = null;
 
@@ -172,6 +181,55 @@
         }
     }
 
+    /** Convenience class for passing around TTS markers. */
+    private class ProgressMarker {
+        // The index in frames of this marker.
+        public final int frames;
+        // The start index in the text of the utterance.
+        public final int start;
+        // The end index (exclusive) in the text of the utterance.
+        public final int end;
+
+        public ProgressMarker(int frames, int start, int end) {
+            this.frames = frames;
+            this.start = start;
+            this.end = end;
+        }
+    }
+
+    /** Set a callback for the first marker in the queue. */
+    void updateMarker() {
+        ProgressMarker marker = markerList.peek();
+        if (marker != null) {
+            // Zero is used to disable the marker. The documentation recommends to use a non-zero
+            // position near zero such as 1.
+            int markerInFrames = marker.frames == 0 ? 1 : marker.frames;
+            mAudioTrack.setNotificationMarkerPosition(markerInFrames);
+        }
+    }
+
+    /** Informs us that at markerInFrames, the range between start and end is about to be spoken. */
+    void rangeStart(int markerInFrames, int start, int end) {
+        markerList.add(new ProgressMarker(markerInFrames, start, end));
+        updateMarker();
+    }
+
+    @Override
+    public void onMarkerReached(AudioTrack track) {
+        ProgressMarker marker = markerList.poll();
+        if (marker == null) {
+            Log.e(TAG, "onMarkerReached reached called but no marker in queue");
+            return;
+        }
+        // Inform the client.
+        getDispatcher().dispatchOnUtteranceRangeStart(marker.start, marker.end);
+        // Listen for the next marker.
+        // It's ok if this marker is in the past, in that case onMarkerReached will be called again.
+        updateMarker();
+    }
+
+    @Override
+    public void onPeriodicNotification(AudioTrack track) {}
 
     void put(byte[] buffer) throws InterruptedException {
         try {
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 24cad95..9a157b7 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -2103,55 +2103,69 @@
 
         private boolean mEstablished;
 
-        private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() {
-            public void onStop(String utteranceId, boolean isStarted) throws RemoteException {
-                UtteranceProgressListener listener = mUtteranceProgressListener;
-                if (listener != null) {
-                    listener.onStop(utteranceId, isStarted);
-                }
-            };
+        private final ITextToSpeechCallback.Stub mCallback =
+                new ITextToSpeechCallback.Stub() {
+                    public void onStop(String utteranceId, boolean isStarted)
+                            throws RemoteException {
+                        UtteranceProgressListener listener = mUtteranceProgressListener;
+                        if (listener != null) {
+                            listener.onStop(utteranceId, isStarted);
+                        }
+                    };
 
-            @Override
-            public void onSuccess(String utteranceId) {
-                UtteranceProgressListener listener = mUtteranceProgressListener;
-                if (listener != null) {
-                    listener.onDone(utteranceId);
-                }
-            }
+                    @Override
+                    public void onSuccess(String utteranceId) {
+                        UtteranceProgressListener listener = mUtteranceProgressListener;
+                        if (listener != null) {
+                            listener.onDone(utteranceId);
+                        }
+                    }
 
-            @Override
-            public void onError(String utteranceId, int errorCode) {
-                UtteranceProgressListener listener = mUtteranceProgressListener;
-                if (listener != null) {
-                    listener.onError(utteranceId);
-                }
-            }
+                    @Override
+                    public void onError(String utteranceId, int errorCode) {
+                        UtteranceProgressListener listener = mUtteranceProgressListener;
+                        if (listener != null) {
+                            listener.onError(utteranceId);
+                        }
+                    }
 
-            @Override
-            public void onStart(String utteranceId) {
-                UtteranceProgressListener listener = mUtteranceProgressListener;
-                if (listener != null) {
-                    listener.onStart(utteranceId);
-                }
-            }
+                    @Override
+                    public void onStart(String utteranceId) {
+                        UtteranceProgressListener listener = mUtteranceProgressListener;
+                        if (listener != null) {
+                            listener.onStart(utteranceId);
+                        }
+                    }
 
-            @Override
-            public void onBeginSynthesis(String utteranceId, int sampleRateInHz, int audioFormat,
-                                     int channelCount) {
-                UtteranceProgressListener listener = mUtteranceProgressListener;
-                if (listener != null) {
-                    listener.onBeginSynthesis(utteranceId, sampleRateInHz, audioFormat, channelCount);
-                }
-            }
+                    @Override
+                    public void onBeginSynthesis(
+                            String utteranceId,
+                            int sampleRateInHz,
+                            int audioFormat,
+                            int channelCount) {
+                        UtteranceProgressListener listener = mUtteranceProgressListener;
+                        if (listener != null) {
+                            listener.onBeginSynthesis(
+                                    utteranceId, sampleRateInHz, audioFormat, channelCount);
+                        }
+                    }
 
-            @Override
-            public void onAudioAvailable(String utteranceId, byte[] audio) {
-                UtteranceProgressListener listener = mUtteranceProgressListener;
-                if (listener != null) {
-                    listener.onAudioAvailable(utteranceId, audio);
-                }
-            }
-        };
+                    @Override
+                    public void onAudioAvailable(String utteranceId, byte[] audio) {
+                        UtteranceProgressListener listener = mUtteranceProgressListener;
+                        if (listener != null) {
+                            listener.onAudioAvailable(utteranceId, audio);
+                        }
+                    }
+
+                    @Override
+                    public void onUtteranceRangeStart(String utteranceId, int start, int end) {
+                        UtteranceProgressListener listener = mUtteranceProgressListener;
+                        if (listener != null) {
+                            listener.onUtteranceRangeStart(utteranceId, start, end);
+                        }
+                    }
+                };
 
         private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> {
             private final ComponentName mName;
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 55da52b..80d3c8a 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -663,6 +663,8 @@
         void dispatchOnBeginSynthesis(int sampleRateInHz, int audioFormat, int channelCount);
 
         void dispatchOnAudioAvailable(byte[] audio);
+
+        public void dispatchOnUtteranceRangeStart(int start, int end);
     }
 
     /** Set of parameters affecting audio output. */
@@ -882,6 +884,15 @@
             }
         }
 
+        @Override
+        public void dispatchOnUtteranceRangeStart(int start, int end) {
+            final String utteranceId = getUtteranceId();
+            if (utteranceId != null) {
+                mCallbacks.dispatchOnUtteranceRangeStart(
+                        getCallerIdentity(), utteranceId, start, end);
+            }
+        }
+
         abstract public String getUtteranceId();
 
         String getStringParam(Bundle params, String key, String defaultValue) {
@@ -1559,6 +1570,17 @@
             }
         }
 
+        public void dispatchOnUtteranceRangeStart(
+                Object callerIdentity, String utteranceId, int start, int end) {
+            ITextToSpeechCallback cb = getCallbackFor(callerIdentity);
+            if (cb == null) return;
+            try {
+                cb.onUtteranceRangeStart(utteranceId, start, end);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Callback dispatchOnUtteranceRangeStart(String, int, int) failed: " + e);
+            }
+        }
+
         @Override
         public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) {
             IBinder caller = (IBinder) cookie;
diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java
index 72a5228..0ee3769 100644
--- a/core/java/android/speech/tts/UtteranceProgressListener.java
+++ b/core/java/android/speech/tts/UtteranceProgressListener.java
@@ -122,8 +122,24 @@
     }
 
     /**
-     * Wraps an old deprecated OnUtteranceCompletedListener with a shiny new
-     * progress listener.
+     * This is called when the TTS service is about to speak the specified range of the utterance
+     * with the given utteranceId.
+     *
+     * <p>This method is called when the audio is expected to start playing on the speaker. Note
+     * that this is different from {@link #onAudioAvailable} which is called as soon as the audio is
+     * generated.
+     *
+     * <p>Only called if the engine supplies timing information by calling {@link
+     * SynthesisCallback#rangeStart(int, int, int)}.
+     *
+     * @param utteranceId Unique id identifying the synthesis request.
+     * @param start The start index of the range in the utterance text.
+     * @param end The end index of the range (exclusive) in the utterance text.
+     */
+    public void onUtteranceRangeStart(String utteranceId, int start, int end) {}
+
+    /**
+     * Wraps an old deprecated OnUtteranceCompletedListener with a shiny new progress listener.
      *
      * @hide
      */
diff --git a/core/java/android/text/Editable.java b/core/java/android/text/Editable.java
index c0948a6..3396bce 100644
--- a/core/java/android/text/Editable.java
+++ b/core/java/android/text/Editable.java
@@ -94,10 +94,10 @@
     public Editable append(char text);
 
     /**
-     * Convenience for replace(0, length(), "", 0, 0)
-     * @see #replace(int, int, CharSequence, int, int)
+     * Convenience for replace(0, length(), "", 0, 0).
      * Note that this clears the text, not the spans;
      * use {@link #clearSpans} if you need that.
+     * @see #replace(int, int, CharSequence, int, int)
      */
     public void clear();
 
diff --git a/core/java/android/text/FontConfig.aidl b/core/java/android/text/FontConfig.aidl
new file mode 100644
index 0000000..17a5ca2
--- /dev/null
+++ b/core/java/android/text/FontConfig.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+/** @hide */
+parcelable FontConfig;
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
new file mode 100644
index 0000000..df694ff
--- /dev/null
+++ b/core/java/android/text/FontConfig.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Font configuration descriptions for System fonts.
+ */
+public final class FontConfig implements Parcelable {
+    private final List<Family> mFamilies = new ArrayList<>();
+    private final List<Alias> mAliases = new ArrayList<>();
+
+    public FontConfig() {
+    }
+
+    public FontConfig(FontConfig config) {
+        for (int i = 0; i < config.mFamilies.size(); i++) {
+            mFamilies.add(new Family(config.mFamilies.get(i)));
+        }
+        mAliases.addAll(config.mAliases);
+    }
+
+    /**
+     * Returns the ordered list of families included in the system fonts.
+     */
+    public List<Family> getFamilies() {
+        return mFamilies;
+    }
+
+    /**
+     * Returns the list of aliases defined for the font families in the system fonts.
+     */
+    public List<Alias> getAliases() {
+        return mAliases;
+    }
+
+    /**
+     * @hide
+     */
+    public FontConfig(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flag) {
+        out.writeInt(mFamilies.size());
+        for (int i = 0; i < mFamilies.size(); i++) {
+            mFamilies.get(i).writeToParcel(out, flag);
+        }
+        out.writeInt(mAliases.size());
+        for (int i = 0; i < mAliases.size(); i++) {
+            mAliases.get(i).writeToParcel(out, flag);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void readFromParcel(Parcel in) {
+        int size = in.readInt();
+        for (int i = 0; i < size; i++) {
+            mFamilies.add(new Family(in));
+        }
+        size = in.readInt();
+        for (int i = 0; i < size; i++) {
+            mAliases.add(new Alias(in));
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<FontConfig> CREATOR = new Parcelable.Creator() {
+        public FontConfig createFromParcel(Parcel in) {
+            return new FontConfig(in);
+        }
+        public FontConfig[] newArray(int size) {
+            return new FontConfig[size];
+        }
+    };
+
+    /**
+     * Class that holds information about a Font axis.
+     */
+    public static final class Axis implements Parcelable {
+        private final int mTag;
+        private final float mStyleValue;
+
+        public Axis(int tag, float styleValue) {
+            this.mTag = tag;
+            this.mStyleValue = styleValue;
+        }
+
+        /**
+         * Returns the variable font axis tag associated to this axis.
+         */
+        public int getTag() {
+            return mTag;
+        }
+
+        /**
+         * Returns the style value associated to the given axis for this font.
+         */
+        public float getStyleValue() {
+            return mStyleValue;
+        }
+
+        /**
+         * @hide
+         */
+        public Axis(Parcel in) {
+            mTag = in.readInt();
+            mStyleValue = in.readFloat();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flag) {
+            out.writeInt(mTag);
+            out.writeFloat(mStyleValue);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Axis> CREATOR = new Creator<Axis>() {
+            @Override
+            public Axis createFromParcel(Parcel in) {
+                return new Axis(in);
+            }
+
+            @Override
+            public Axis[] newArray(int size) {
+                return new Axis[size];
+            }
+        };
+    }
+
+    /**
+     * Class that holds information about a Font.
+     */
+    public static final class Font implements Parcelable {
+        private final String mFontName;
+        private final int mTtcIndex;
+        private final List<Axis> mAxes;
+        private final int mWeight;
+        private final boolean mIsItalic;
+        private ParcelFileDescriptor mFd;
+
+        public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
+            mFontName = fontName;
+            mTtcIndex = ttcIndex;
+            mAxes = axes;
+            mWeight = weight;
+            mIsItalic = isItalic;
+            mFd = null;
+        }
+
+        public Font(Font origin) {
+            mFontName = origin.mFontName;
+            mTtcIndex = origin.mTtcIndex;
+            mAxes = new ArrayList<>(origin.mAxes);
+            mWeight = origin.mWeight;
+            mIsItalic = origin.mIsItalic;
+            if (origin.mFd != null) {
+                try {
+                    mFd = origin.mFd.dup();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        /**
+         * Returns the name associated by the system to this font.
+         */
+        public String getFontName() {
+            return mFontName;
+        }
+
+        /**
+         * Returns the index to be used to access this font when accessing a TTC file.
+         */
+        public int getTtcIndex() {
+            return mTtcIndex;
+        }
+
+        /**
+         * Returns the list of axes associated to this font.
+         */
+        public List<Axis> getAxes() {
+            return mAxes;
+        }
+
+        /**
+         * Returns the weight value for this font.
+         */
+        public int getWeight() {
+            return mWeight;
+        }
+
+        /**
+         * Returns whether this font is italic.
+         */
+        public boolean isItalic() {
+            return mIsItalic;
+        }
+
+        /**
+         * Returns a file descriptor to access the specified font. This should be closed after use.
+         */
+        public ParcelFileDescriptor getFd() {
+            return mFd;
+        }
+
+        /**
+         * @hide
+         */
+        public void setFd(ParcelFileDescriptor fd) {
+            mFd = fd;
+        }
+
+        /**
+         * @hide
+         */
+        public Font(Parcel in) {
+            mFontName = in.readString();
+            mTtcIndex = in.readInt();
+            final int numAxes = in.readInt();
+            mAxes = new ArrayList<>();
+            for (int i = 0; i < numAxes; i++) {
+                mAxes.add(new Axis(in));
+            }
+            mWeight = in.readInt();
+            mIsItalic = in.readInt() == 1;
+            if (in.readInt() == 1) { /* has FD */
+                mFd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+            } else {
+                mFd = null;
+            }
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flag) {
+            out.writeString(mFontName);
+            out.writeInt(mTtcIndex);
+            out.writeInt(mAxes.size());
+            for (int i = 0; i < mAxes.size(); i++) {
+                mAxes.get(i).writeToParcel(out, flag);
+            }
+            out.writeInt(mWeight);
+            out.writeInt(mIsItalic ? 1 : 0);
+            out.writeInt(mFd == null ? 0 : 1);
+            if (mFd != null) {
+                mFd.writeToParcel(out, flag);
+            }
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Font> CREATOR = new Creator<Font>() {
+            @Override
+            public Font createFromParcel(Parcel in) {
+                return new Font(in);
+            }
+
+            @Override
+            public Font[] newArray(int size) {
+                return new Font[size];
+            }
+        };
+    }
+
+    /**
+     * Class that holds information about a Font alias.
+     */
+    public static final class Alias implements Parcelable {
+        private final String mName;
+        private final String mToName;
+        private final int mWeight;
+
+        public Alias(String name, String toName, int weight) {
+            this.mName = name;
+            this.mToName = toName;
+            this.mWeight = weight;
+        }
+
+        /**
+         * Returns the new name for the alias.
+         */
+        public String getName() {
+            return mName;
+        }
+
+        /**
+         * Returns the existing name to which this alias points to.
+         */
+        public String getToName() {
+            return mToName;
+        }
+
+        /**
+         * Returns the weight associated with this alias.
+         */
+        public int getWeight() {
+            return mWeight;
+        }
+
+        /**
+         * @hide
+         */
+        public Alias(Parcel in) {
+            mName = in.readString();
+            mToName = in.readString();
+            mWeight = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flag) {
+            out.writeString(mName);
+            out.writeString(mToName);
+            out.writeInt(mWeight);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Alias> CREATOR = new Creator<Alias>() {
+            @Override
+            public Alias createFromParcel(Parcel in) {
+                return new Alias(in);
+            }
+
+            @Override
+            public Alias[] newArray(int size) {
+                return new Alias[size];
+            }
+        };
+    }
+
+    /**
+     * Class that holds information about a Font family.
+     */
+    public static final class Family implements Parcelable {
+        private final String mName;
+        private final List<Font> mFonts;
+        private final String mLanguage;
+        private final String mVariant;
+
+        public Family(String name, List<Font> fonts, String language, String variant) {
+            this.mName = name;
+            this.mFonts = fonts;
+            this.mLanguage = language;
+            this.mVariant = variant;
+        }
+
+        public Family(Family origin) {
+            this.mName = origin.mName;
+            this.mLanguage = origin.mLanguage;
+            this.mVariant = origin.mVariant;
+            this.mFonts = new ArrayList<>();
+            for (int i = 0; i < origin.mFonts.size(); i++) {
+                mFonts.add(new Font(origin.mFonts.get(i)));
+            }
+        }
+
+        /**
+         * Returns the name given by the system to this font family.
+         */
+        public String getName() {
+            return mName;
+        }
+
+        /**
+         * Returns the list of fonts included in this family.
+         */
+        public List<Font> getFonts() {
+            return mFonts;
+        }
+
+        /**
+         * Returns the language for this family. May be null.
+         */
+        public String getLanguage() {
+            return mLanguage;
+        }
+
+        /**
+         * Returns the font variant for this family, e.g. "elegant" or "compact". May be null.
+         */
+        public String getVariant() {
+            return mVariant;
+        }
+
+        /**
+         * @hide
+         */
+        public Family(Parcel in) {
+            mName = in.readString();
+            final int size = in.readInt();
+            mFonts = new ArrayList<>();
+            for (int i = 0; i < size; i++) {
+                mFonts.add(new Font(in));
+            }
+            mLanguage = in.readString();
+            mVariant = in.readString();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flag) {
+            out.writeString(mName);
+            out.writeInt(mFonts.size());
+            for (int i = 0; i < mFonts.size(); i++) {
+                mFonts.get(i).writeToParcel(out, flag);
+            }
+            out.writeString(mLanguage);
+            out.writeString(mVariant);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<Family> CREATOR = new Creator<Family>() {
+            @Override
+            public Family createFromParcel(Parcel in) {
+                return new Family(in);
+            }
+
+            @Override
+            public Family[] newArray(int size) {
+                return new Family[size];
+            }
+        };
+    }
+}
diff --git a/core/java/android/text/FontManager.java b/core/java/android/text/FontManager.java
new file mode 100644
index 0000000..b61cbf3
--- /dev/null
+++ b/core/java/android/text/FontManager.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.os.RemoteException;
+
+import com.android.internal.font.IFontManager;
+
+/**
+ * Interact with the Font service.
+ */
+public final class FontManager {
+    private static final String TAG = "FontManager";
+
+    private final IFontManager mService;
+
+    /**
+     * @hide
+     */
+    public FontManager(IFontManager service) {
+        mService = service;
+    }
+
+    /**
+     * Retrieve the system fonts data. This loads the fonts.xml data if needed and loads all system
+     * fonts in to memory, providing file descriptors for them.
+     */
+    public FontConfig getSystemFonts() {
+        try {
+            return mService.getSystemFonts();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/text/LangId.java b/core/java/android/text/LangId.java
new file mode 100644
index 0000000..ed6e909
--- /dev/null
+++ b/core/java/android/text/LangId.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.text;
+
+/**
+ *  Java wrapper for LangId native library interface.
+ *  This class is used to detect languages in text.
+ *  @hide
+ */
+public final class LangId {
+    // TODO: Move this to android.view.textclassifier and make it package-private.
+    // We'll have to update the native library code to do this.
+
+    static {
+        System.loadLibrary("smart-selection_jni");
+    }
+
+    private final long mModelPtr;
+
+    /**
+     * Creates a new instance of LangId predictor, using the provided model image.
+     */
+    public LangId(int fd) {
+        mModelPtr = nativeNew(fd);
+    }
+
+    /**
+     * Detects the language for given text.
+     */
+    public String findLanguage(String text) {
+        return nativeFindLanguage(mModelPtr, text);
+    }
+
+    /**
+     * Frees up the allocated memory.
+     */
+    public void close() {
+        nativeClose(mModelPtr);
+    }
+
+    private static native long nativeNew(int fd);
+
+    private static native String nativeFindLanguage(long context, String text);
+
+    private static native void nativeClose(long context);
+}
+
diff --git a/core/java/android/text/SmartSelection.java b/core/java/android/text/SmartSelection.java
new file mode 100644
index 0000000..97ef514
--- /dev/null
+++ b/core/java/android/text/SmartSelection.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+/**
+ *  Java wrapper for SmartSelection native library interface.
+ *  This library is used for detecting entities in text.
+ *  @hide
+ */
+public final class SmartSelection {
+    // TODO: Move this to android.view.textclassifier and make it package-private.
+    // We'll have to update the native library code to do this.
+
+    static {
+        System.loadLibrary("smart-selection_jni");
+    }
+
+    private final long mCtx;
+
+    /**
+     * Creates a new instance of SmartSelect predictor, using the provided model image,
+     * given as a file descriptor.
+     */
+    public SmartSelection(int fd) {
+        mCtx = nativeNew(fd);
+    }
+
+    /**
+     * Given a string context and current selection, computes the SmartSelection suggestion.
+     *
+     * The begin and end are character indices into the context UTF8 string. selectionBegin is the
+     * character index where the selection begins, and selectionEnd is the index of one character
+     * past the selection span.
+     *
+     * The return value is an array of two ints: suggested selection beginning and end, with the
+     * same semantics as the input selectionBeginning and selectionEnd.
+     */
+    public int[] suggest(String context, int selectionBegin, int selectionEnd) {
+        return nativeSuggest(mCtx, context, selectionBegin, selectionEnd);
+    }
+
+    /**
+     * Given a string context and current selection, classifies the type of the selected text.
+     *
+     * The begin and end params are character indices in the context string.
+     *
+     * Returns the type of the selection, e.g. "email", "address", "phone".
+     */
+    public String classifyText(String context, int selectionBegin, int selectionEnd) {
+        return nativeClassifyText(mCtx, context, selectionBegin, selectionEnd);
+    }
+
+    /**
+     * Frees up the allocated memory.
+     */
+    public void close() {
+        nativeClose(mCtx);
+    }
+
+    private static native long nativeNew(int fd);
+
+    private static native int[] nativeSuggest(
+            long context, String text, int selectionBegin, int selectionEnd);
+
+    private static native String nativeClassifyText(
+            long context, String text, int selectionBegin, int selectionEnd);
+
+    private static native void nativeClose(long context);
+}
+
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 186d96b..5f01f7b 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -21,6 +21,7 @@
 import android.graphics.Paint;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 
@@ -73,8 +74,6 @@
         mSpanFlags = EmptyArray.INT;
         mSpanMax = EmptyArray.INT;
         mSpanOrder = EmptyArray.INT;
-        mPrioSortBuffer = EmptyArray.INT;
-        mOrderSortBuffer = EmptyArray.INT;
 
         if (text instanceof Spanned) {
             Spanned sp = (Spanned) text;
@@ -856,14 +855,14 @@
      * @param queryStart Start index.
      * @param queryEnd End index.
      * @param kind Class type to search for.
-     * @param sort If true the results are sorted by the insertion order.
+     * @param sortByInsertionOrder If true the results are sorted by the insertion order.
      * @param <T>
      * @return Array of the spans. Empty array if no results are found.
      *
      * @hide
      */
     public <T> T[] getSpans(int queryStart, int queryEnd, @Nullable Class<T> kind,
-                                 boolean sort) {
+            boolean sortByInsertionOrder) {
         if (kind == null) return (T[]) ArrayUtils.emptyArray(Object.class);
         if (mSpanCount == 0) return ArrayUtils.emptyArray(kind);
         int count = countSpans(queryStart, queryEnd, kind, treeRoot());
@@ -873,13 +872,15 @@
 
         // Safe conversion, but requires a suppressWarning
         T[] ret = (T[]) Array.newInstance(kind, count);
-        if (sort) {
-            mPrioSortBuffer = checkSortBuffer(mPrioSortBuffer, count);
-            mOrderSortBuffer = checkSortBuffer(mOrderSortBuffer, count);
+        final int[] prioSortBuffer = sortByInsertionOrder ? obtain(count) : EmptyArray.INT;
+        final int[] orderSortBuffer = sortByInsertionOrder ? obtain(count) : EmptyArray.INT;
+        getSpansRec(queryStart, queryEnd, kind, treeRoot(), ret, prioSortBuffer,
+                orderSortBuffer, 0, sortByInsertionOrder);
+        if (sortByInsertionOrder) {
+            sort(ret, prioSortBuffer, orderSortBuffer);
+            recycle(prioSortBuffer);
+            recycle(orderSortBuffer);
         }
-        getSpansRec(queryStart, queryEnd, kind, treeRoot(), ret, mPrioSortBuffer,
-                mOrderSortBuffer, 0, sort);
-        if (sort) sort(ret, mPrioSortBuffer, mOrderSortBuffer);
         return ret;
     }
 
@@ -992,15 +993,63 @@
     }
 
     /**
+     * Obtain a temporary sort buffer.
+     *
+     * @param elementCount the size of the int[] to be returned
+     * @return an int[] with elementCount length
+     */
+    private static int[] obtain(final int elementCount) {
+        int[] result = null;
+        synchronized (sCachedIntBuffer) {
+            // try finding a tmp buffer with length of at least elementCount
+            // if not get the first available one
+            int candidateIndex = -1;
+            for (int i = sCachedIntBuffer.length - 1; i >= 0; i--) {
+                if (sCachedIntBuffer[i] != null) {
+                    if (sCachedIntBuffer[i].length >= elementCount) {
+                        candidateIndex = i;
+                        break;
+                    } else if (candidateIndex == -1) {
+                        candidateIndex = i;
+                    }
+                }
+            }
+
+            if (candidateIndex != -1) {
+                result = sCachedIntBuffer[candidateIndex];
+                sCachedIntBuffer[candidateIndex] = null;
+            }
+        }
+        result = checkSortBuffer(result, elementCount);
+        return result;
+    }
+
+    /**
+     * Recycle sort buffer.
+     *
+     * @param buffer buffer to be recycled
+     */
+    private static void recycle(int[] buffer) {
+        synchronized (sCachedIntBuffer) {
+            for (int i = 0; i < sCachedIntBuffer.length; i++) {
+                if (sCachedIntBuffer[i] == null || buffer.length > sCachedIntBuffer[i].length) {
+                    sCachedIntBuffer[i] = buffer;
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
      * Check the size of the buffer and grow if required.
      *
-     * @param buffer Buffer to be checked.
-     * @param size Required size.
+     * @param buffer buffer to be checked.
+     * @param size   required size.
      * @return Same buffer instance if the current size is greater than required size. Otherwise a
      * new instance is created and returned.
      */
-    private final int[] checkSortBuffer(int[] buffer, int size) {
-        if(size > buffer.length) {
+    private static int[] checkSortBuffer(int[] buffer, int size) {
+        if (buffer == null || size > buffer.length) {
             return ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(size));
         }
         return buffer;
@@ -1025,16 +1074,19 @@
         }
 
         for (int i = size - 1; i > 0; i--) {
-            T v = array[0];
-            int prio = priority[0];
-            int insertOrder = insertionOrder[0];
+            final T tmpSpan =  array[0];
             array[0] = array[i];
+            array[i] = tmpSpan;
+
+            final int tmpPriority =  priority[0];
             priority[0] = priority[i];
+            priority[i] = tmpPriority;
+
+            final int tmpOrder =  insertionOrder[0];
             insertionOrder[0] = insertionOrder[i];
+            insertionOrder[i] = tmpOrder;
+
             siftDown(0, array, i, priority, insertionOrder);
-            array[i] = v;
-            priority[i] = prio;
-            insertionOrder[i] = insertOrder;
         }
     }
 
@@ -1050,10 +1102,6 @@
      */
     private final <T> void siftDown(int index, T[] array, int size, int[] priority,
                                     int[] insertionOrder) {
-        T v = array[index];
-        int prio = priority[index];
-        int insertOrder = insertionOrder[index];
-
         int left = 2 * index + 1;
         while (left < size) {
             if (left < size - 1 && compareSpans(left, left + 1, priority, insertionOrder) < 0) {
@@ -1062,15 +1110,22 @@
             if (compareSpans(index, left, priority, insertionOrder) >= 0) {
                 break;
             }
+
+            final T tmpSpan =  array[index];
             array[index] = array[left];
+            array[left] = tmpSpan;
+
+            final int tmpPriority =  priority[index];
             priority[index] = priority[left];
+            priority[left] = tmpPriority;
+
+            final int tmpOrder =  insertionOrder[index];
             insertionOrder[index] = insertionOrder[left];
+            insertionOrder[left] = tmpOrder;
+
             index = left;
             left = 2 * index + 1;
         }
-        array[index] = v;
-        priority[index] = prio;
-        insertionOrder[index] = insertOrder;
     }
 
     /**
@@ -1704,6 +1759,10 @@
     }
 
     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
+
+    @GuardedBy("sCachedIntBuffer")
+    private static final int[][] sCachedIntBuffer = new int[6][0];
+
     private InputFilter[] mFilters = NO_FILTERS;
 
     private char[] mText;
@@ -1717,8 +1776,6 @@
     private int[] mSpanFlags;
     private int[] mSpanOrder;  // store the order of span insertion
     private int mSpanInsertCount;  // counter for the span insertion
-    private int[] mPrioSortBuffer;  // buffer used to sort getSpans result
-    private int[] mOrderSortBuffer;  // buffer used to sort getSpans result
 
     private int mSpanCount;
     private IdentityHashMap<Object, Integer> mIndexOfSpan;
diff --git a/core/java/android/text/TextAssistant.java b/core/java/android/text/TextAssistant.java
deleted file mode 100644
index b044981..0000000
--- a/core/java/android/text/TextAssistant.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-/**
- * Interface for providing text assistant features.
- */
-public interface TextAssistant {
-
-    /**
-     * NO_OP TextAssistant. This will act as the default TextAssistant until we implement a
-     * TextClassificationManager.
-     * @hide
-     */
-    TextAssistant NO_OP = new TextAssistant() {
-
-        private final TextSelection mTextSelection = new TextSelection();
-
-        @Override
-        public TextSelection suggestSelection(
-                CharSequence text, int selectionStartIndex, int selectionEndIndex) {
-            mTextSelection.mStartIndex = selectionStartIndex;
-            mTextSelection.mEndIndex = selectionEndIndex;
-            return mTextSelection;
-        }
-
-        @Override
-        public void addLinks(Spannable text, int linkMask) {}
-    };
-
-    /**
-     * Returns suggested text selection indices, recognized types and their associated confidence
-     * scores. The selections are ordered from highest to lowest scoring.
-     */
-    TextSelection suggestSelection(
-            CharSequence text, int selectionStartIndex, int selectionEndIndex);
-
-    /**
-     * Adds assistance clickable spans to the provided text.
-     */
-    void addLinks(Spannable text, int linkMask);
-}
diff --git a/core/java/android/text/TextClassification.java b/core/java/android/text/TextClassification.java
deleted file mode 100644
index bb226da..0000000
--- a/core/java/android/text/TextClassification.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * Information about entities that a specific piece of text is classified as.
- */
-public class TextClassification {
-
-    /** @hide */
-    public static final TextClassification NO_OP = new TextClassification();
-
-    private Map<String, Float> mTypeConfidence = Collections.unmodifiableMap(Collections.EMPTY_MAP);
-
-    /**
-     * Returns a map of text classification types to their respective confidence scores.
-     * The scores range from 0 (low confidence) to 1 (high confidence). The items are ordered from
-     * high scoring items to low scoring items.
-     */
-    public Map<String, Float> getTypeConfidence() {
-        return mTypeConfidence;
-    }
-}
diff --git a/core/java/android/text/TextClassificationManager.java b/core/java/android/text/TextClassificationManager.java
deleted file mode 100644
index d4548f0..0000000
--- a/core/java/android/text/TextClassificationManager.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.annotation.NonNull;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Interface to the text classification service.
- * This class uses machine learning techniques to infer things about text.
- * Unless otherwise stated, methods of this class are blocking operations and should most likely not
- * be called on the UI thread.
- *
- * <p> You do not instantiate this class directly; instead, retrieve it through
- * {@link android.content.Context#getSystemService}.
- *
- * The TextClassificationManager serves as the default TextAssistant if none has been set.
- * @see android.app.Activity#setTextAssistant(TextAssistant).
- */
-public final class TextClassificationManager implements TextAssistant {
-    // TODO: Consider not making this class implement TextAssistant.
-
-    /** @hide */
-    public TextClassificationManager() {}
-
-    /**
-     * Returns information containing languages that were detected in the provided text.
-     * This is a blocking operation and should most likely not be called on the UI thread.
-     */
-    public List<TextLanguage> detectLanguages(@NonNull CharSequence text) {
-        // TODO: Implement this using the cld3 library.
-        return Collections.emptyList();
-    }
-
-    @Override
-    public TextSelection suggestSelection(
-            @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex) {
-        // TODO: Implement.
-        return TextAssistant.NO_OP.suggestSelection(text, selectionStartIndex, selectionEndIndex);
-    }
-
-    @Override
-    public void addLinks(@NonNull Spannable text, int linkMask) {
-        // TODO: Implement.
-    }
-}
diff --git a/core/java/android/text/TextLanguage.java b/core/java/android/text/TextLanguage.java
deleted file mode 100644
index eb834f1..0000000
--- a/core/java/android/text/TextLanguage.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.annotation.NonNull;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * Specifies detected languages for a section of text indicated by a start and end index.
- */
-public final class TextLanguage {
-
-    private final int mStartIndex;
-    private final int mEndIndex;
-    private final Map<String, Float> mLanguageConfidence;
-
-    /**
-     * Initializes a TextLanguage object.
-     *
-     * @param startIndex the start index of the detected languages in the text provided to generate
-     *      this object.
-     * @param endIndex the end index of the detected languages in the text provided to generate this
-     *      object.
-     * @param languageConfidence a map of detected language to confidence score. The language string
-     *      is a BCP-47 language tag.
-     * @throws NullPointerException if languageConfidence is null or contains a null key or value.
-     */
-    public TextLanguage(int startIndex, int endIndex,
-            @NonNull Map<String, Float> languageConfidence) {
-        mStartIndex = startIndex;
-        mEndIndex = endIndex;
-
-        Map<String, Float> map = new LinkedHashMap<>();
-        Preconditions.checkNotNull(languageConfidence).entrySet().stream()
-                .sorted(Map.Entry.comparingByValue())
-                .forEach(entry -> map.put(
-                        Preconditions.checkNotNull(entry.getKey()),
-                        Preconditions.checkNotNull(entry.getValue())));
-        mLanguageConfidence = Collections.unmodifiableMap(map);
-    }
-
-    /**
-     * Returns the start index of the detected languages in the text provided to generate this
-     * object.
-     */
-    public int getStartIndex() {
-        return mStartIndex;
-    }
-
-    /**
-     * Returns the end index of the detected languages in the text provided to generate this object.
-     */
-    public int getEndIndex() {
-        return mEndIndex;
-    }
-
-    /**
-     * Returns an unmodifiable map of detected language to confidence score. The map entries are
-     * ordered from high confidence score (1) to low confidence score (0). The language string is a
-     * BCP-47 language tag.
-     */
-    @NonNull
-    public Map<String, Float> getLanguageConfidence() {
-        return mLanguageConfidence;
-    }
-}
diff --git a/core/java/android/text/TextSelection.java b/core/java/android/text/TextSelection.java
deleted file mode 100644
index 9400458..0000000
--- a/core/java/android/text/TextSelection.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-/**
- * Text selection information.
- */
-public class TextSelection {
-
-    /** @hide */
-    int mStartIndex;
-    /** @hide */
-    int mEndIndex;
-
-    private TextClassification mTextClassification = TextClassification.NO_OP;
-
-    /**
-     * Returns the start index of the text selection.
-     */
-    public int getSelectionStartIndex() {
-        return mStartIndex;
-    }
-
-    /**
-     * Returns the end index of the text selection.
-     */
-    public int getSelectionEndIndex() {
-        return mEndIndex;
-    }
-
-    /**
-     * Returns information about what the text selection is classified as.
-     */
-    public TextClassification getTextClassification() {
-        return mTextClassification;
-    }
-}
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index 856dd0b..b0bff68 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -43,7 +43,7 @@
     }
 
     /**
-     * @deprecated Use {@link #ImageSpan(Context, Bitmap, int) instead.
+     * @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead.
      */
     @Deprecated
     public ImageSpan(Bitmap b, int verticalAlignment) {
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index c5e5df0..d72a48d 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -798,7 +798,7 @@
         /**
          * Creates a TtsSpan of type {@link #TYPE_DECIMAL} and sets the
          * {@link #ARG_INTEGER_PART} and {@link #ARG_FRACTIONAL_PART} arguments.
-         * @see {@link #setArgumentsFromDouble(double, int, int)
+         * @see #setArgumentsFromDouble(double, int, int)
          */
         public DecimalBuilder(double number,
                               int minimumFractionDigits,
@@ -1082,7 +1082,7 @@
          * Sets the {@link #ARG_UNIT} argument.
          * @param unit The unit of the measure.
          * @return This instance.
-         * @see {@link TtsSpan.ARG_UNIT}
+         * @see TtsSpan.ARG_UNIT
          */
         public MeasureBuilder setUnit(String unit) {
             return setStringArgument(TtsSpan.ARG_UNIT, unit);
@@ -1116,7 +1116,7 @@
          * Sets the {@link #ARG_HOURS} argument.
          * @param hours The value to be set for hours. See {@link #ARG_HOURS}.
          * @return This instance.
-         * @see {@link #ARG_HOURS}
+         * @see #ARG_HOURS
          */
         public TimeBuilder setHours(int hours) {
             return setIntArgument(TtsSpan.ARG_HOURS, hours);
@@ -1127,7 +1127,7 @@
          * @param minutes The value to be set for minutes. See
          *     {@link #ARG_MINUTES}.
          * @return This instance.
-         * @see {@link #ARG_MINUTES}
+         * @see #ARG_MINUTES
          */
         public TimeBuilder setMinutes(int minutes) {
             return setIntArgument(TtsSpan.ARG_MINUTES, minutes);
@@ -1177,7 +1177,7 @@
          * @param weekday The value to be set for weekday. See
          *     {@link #ARG_WEEKDAY}.
          * @return This instance.
-         * @see {@link #ARG_WEEKDAY}
+         * @see #ARG_WEEKDAY
          */
         public DateBuilder setWeekday(int weekday) {
             return setIntArgument(TtsSpan.ARG_WEEKDAY, weekday);
@@ -1187,7 +1187,7 @@
          * Sets the {@link #ARG_DAY} argument.
          * @param day The value to be set for day. See {@link #ARG_DAY}.
          * @return This instance.
-         * @see {@link #ARG_DAY}
+         * @see #ARG_DAY
          */
         public DateBuilder setDay(int day) {
             return setIntArgument(TtsSpan.ARG_DAY, day);
@@ -1197,7 +1197,7 @@
          * Sets the {@link #ARG_MONTH} argument.
          * @param month The value to be set for month. See {@link #ARG_MONTH}.
          * @return This instance.
-         * @see {@link #ARG_MONTH}
+         * @see #ARG_MONTH
          */
         public DateBuilder setMonth(int month) {
             return setIntArgument(TtsSpan.ARG_MONTH, month);
@@ -1207,7 +1207,7 @@
          * Sets the {@link #ARG_YEAR} argument.
          * @param year The value to be set for year. See {@link #ARG_YEAR}.
          * @return This instance.
-         * @see {@link #ARG_YEAR}
+         * @see #ARG_YEAR
          */
         public DateBuilder setYear(int year) {
             return setIntArgument(TtsSpan.ARG_YEAR, year);
diff --git a/core/java/android/util/ByteStringUtils.java b/core/java/android/util/ByteStringUtils.java
new file mode 100644
index 0000000..333208d
--- /dev/null
+++ b/core/java/android/util/ByteStringUtils.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * A utility class for common byte array to hex string operations and vise versa.
+ *
+ * @hide
+ */
+public final class ByteStringUtils {
+  private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+
+  private ByteStringUtils() {
+    /* hide constructor */
+  }
+
+  /**
+   * Returns the hex encoded string representation of bytes.
+   * @param bytes Byte array to encode.
+   * @return Hex encoded string representation of bytes.
+   */
+  public static String toHexString(byte[] bytes) {
+    if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) {
+      return null;
+    }
+
+    final int byteLength = bytes.length;
+    final int charCount = 2 * byteLength;
+    final char[] chars = new char[charCount];
+
+    for (int i = 0; i < byteLength; i++) {
+      final int byteHex = bytes[i] & 0xFF;
+      chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
+      chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
+    }
+    return new String(chars);
+  }
+
+  /**
+   * Returns the decoded byte array representation of str.
+   * @param str Hex encoded string to decode.
+   * @return Decoded byte array representation of str.
+   */
+  public static byte[] fromHexToByteArray(String str) {
+    if (str == null || str.length() == 0 || str.length() % 2 != 0) {
+      return null;
+    }
+
+    final char[] chars = str.toCharArray();
+    final int charLength = chars.length;
+    final byte[] bytes = new byte[charLength / 2];
+
+    for (int i = 0; i < bytes.length; i++) {
+      bytes[i] =
+          (byte)(((getIndex(chars[i * 2]) << 4) & 0xF0) | (getIndex(chars[i * 2 + 1]) & 0x0F));
+    }
+    return bytes;
+  }
+
+  private static int getIndex(char c) {
+    for (int i = 0; i < HEX_ARRAY.length; i++) {
+      if (HEX_ARRAY[i] == c) {
+        return i;
+      }
+    }
+    return -1;
+  }
+}
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 79d16fb..92c70bd 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -16,6 +16,8 @@
 
 package android.util;
 
+import android.annotation.SystemApi;
+
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
@@ -253,6 +255,19 @@
             throws IOException;
 
     /**
+     * Read events from the log, filtered by type, blocking until logs are about to be overwritten.
+     * @param tags to search for
+     * @param timestamp timestamp allow logs before this time to be overwritten.
+     * @param output container to add events into
+     * @throws IOException if something goes wrong reading events
+     * @hide
+     */
+    @SystemApi
+    public static native void readEventsOnWrapping(int[] tags, long timestamp,
+            Collection<Event> output)
+            throws IOException;
+
+    /**
      * Get the name associated with an event type tag code.
      * @param tag code to look up
      * @return the name of the tag, or null if no tag has that number
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index f5d515d..da0b609 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -16,6 +16,8 @@
 
 package android.util;
 
+import android.os.ParcelableException;
+
 import java.io.IOException;
 
 /**
@@ -24,19 +26,13 @@
  * @hide
  */
 public class ExceptionUtils {
-    // TODO: longer term these should be replaced with first-class
-    // Parcel.read/writeException() and AIDL support, but for now do this using
-    // a nasty hack.
-
-    private static final String PREFIX_IO = "\u2603";
-
     public static RuntimeException wrap(IOException e) {
-        throw new IllegalStateException(PREFIX_IO + e.getMessage());
+        throw new ParcelableException(e);
     }
 
     public static void maybeUnwrapIOException(RuntimeException e) throws IOException {
-        if ((e instanceof IllegalStateException) && e.getMessage().startsWith(PREFIX_IO)) {
-            throw new IOException(e.getMessage().substring(PREFIX_IO.length()));
+        if (e instanceof ParcelableException) {
+            ((ParcelableException) e).maybeRethrow(IOException.class);
         }
     }
 
diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java
index e4c025d..be531ff 100644
--- a/core/java/android/util/KeyValueListParser.java
+++ b/core/java/android/util/KeyValueListParser.java
@@ -129,4 +129,22 @@
         }
         return def;
     }
+
+    /**
+     * Get the value for key as a boolean.
+     * @param key The key to lookup.
+     * @param def The value to return if the key was not found.
+     * @return the string value associated with the key.
+     */
+    public boolean getBoolean(String key, boolean def) {
+        String value = mValues.get(key);
+        if (value != null) {
+            try {
+                return Boolean.parseBoolean(value);
+            } catch (NumberFormatException e) {
+                // fallthrough
+            }
+        }
+        return def;
+    }
 }
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index 6531aef..0fe56f6 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -30,7 +30,6 @@
  * @hide
  */
 public final class PackageUtils {
-    private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
 
     private PackageUtils() {
         /* hide constructor */
@@ -81,16 +80,6 @@
 
         messageDigest.update(data);
 
-        final byte[] digest = messageDigest.digest();
-        final int digestLength = digest.length;
-        final int charCount = 2 * digestLength;
-
-        final char[] chars = new char[charCount];
-        for (int i = 0; i < digestLength; i++) {
-            final int byteHex = digest[i] & 0xFF;
-            chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
-            chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
-        }
-        return new String(chars);
+        return ByteStringUtils.toHexString(messageDigest.digest());
     }
 }
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 37d6757..0a294ab 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -19,7 +19,6 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.os.SystemClock;
-import android.text.format.DateUtils;
 
 import com.android.internal.util.XmlUtils;
 
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index b37ea8e..105cc47 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -292,7 +292,7 @@
     public static final int STATE_VR = 5;
 
     /* The color mode constants defined below must be kept in sync with the ones in
-     * system/graphics.h */
+     * system/core/include/system/graphics-base.h */
 
     /**
      * Display color mode: The current color mode is unknown or invalid.
@@ -306,11 +306,24 @@
      */
     public static final int COLOR_MODE_DEFAULT = 0;
 
-    /**
-     * Display color mode: SRGB
-     * @hide
-     */
+    /** @hide */
+    public static final int COLOR_MODE_BT601_625 = 1;
+    /** @hide */
+    public static final int COLOR_MODE_BT601_625_UNADJUSTED = 2;
+    /** @hide */
+    public static final int COLOR_MODE_BT601_525 = 3;
+    /** @hide */
+    public static final int COLOR_MODE_BT601_525_UNADJUSTED = 4;
+    /** @hide */
+    public static final int COLOR_MODE_BT709 = 5;
+    /** @hide */
+    public static final int COLOR_MODE_DCI_P3 = 6;
+    /** @hide */
     public static final int COLOR_MODE_SRGB = 7;
+    /** @hide */
+    public static final int COLOR_MODE_ADOBE_RGB = 8;
+    /** @hide */
+    public static final int COLOR_MODE_DISPLAY_P3 = 9;
 
     /**
      * Internal method to create a display.
@@ -745,6 +758,8 @@
 
     /**
      * Returns the display's HDR capabilities.
+     *
+     * @see #isHdr()
      */
     public HdrCapabilities getHdrCapabilities() {
         synchronized (this) {
@@ -754,6 +769,35 @@
     }
 
     /**
+     * Returns whether this display supports any HDR type.
+     *
+     * @see #getHdrCapabilities()
+     * @see HdrCapabilities#getSupportedHdrTypes()
+     */
+    public boolean isHdr() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            int[] types = mDisplayInfo.hdrCapabilities.getSupportedHdrTypes();
+            return types != null && types.length > 0;
+        }
+    }
+
+    /**
+     * Returns whether this display can be used to display wide color gamut content.
+     */
+    public boolean isWideColorGamut() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            for (int colorMode : mDisplayInfo.supportedColorModes) {
+                if (colorMode == COLOR_MODE_DCI_P3 || colorMode > COLOR_MODE_SRGB) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
      * Gets the supported color modes of this device.
      * @hide
      */
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index a07a7ef..41a13cf 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -16,16 +16,12 @@
 
 package android.view;
 
-import static android.view.View.KEYBOARD_NAVIGATION_GROUP_CLUSTER;
-import static android.view.View.KEYBOARD_NAVIGATION_GROUP_SECTION;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
-import android.view.View.KeyboardNavigationGroupType;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -110,31 +106,28 @@
     }
 
     /**
-     * Find the root of the next keyboard navigation group after the current one. The group type can
-     * be either a cluster or a section.
-     * @param groupType Type of the keyboard navigation group
+     * Find the root of the next keyboard navigation cluster after the current one.
      * @param root The view tree to look inside. Cannot be null
-     * @param currentGroup The starting point of the search. Null means the default group
+     * @param currentCluster The starting point of the search. Null means the default cluster
      * @param direction Direction to look
-     * @return The next group, or null if none exists
+     * @return The next cluster, or null if none exists
      */
-    public View findNextKeyboardNavigationGroup(
-            @KeyboardNavigationGroupType int groupType,
+    public View findNextKeyboardNavigationCluster(
             @NonNull View root,
-            @Nullable View currentGroup,
+            @Nullable View currentCluster,
             int direction) {
         View next = null;
 
-        final ArrayList<View> groups = mTempList;
+        final ArrayList<View> clusters = mTempList;
         try {
-            groups.clear();
-            root.addKeyboardNavigationGroups(groupType, groups, direction);
-            if (!groups.isEmpty()) {
-                next = findNextKeyboardNavigationGroup(
-                        groupType, root, currentGroup, groups, direction);
+            clusters.clear();
+            root.addKeyboardNavigationClusters(clusters, direction);
+            if (!clusters.isEmpty()) {
+                next = findNextKeyboardNavigationCluster(
+                        root, currentCluster, clusters, direction);
             }
         } finally {
-            groups.clear();
+            clusters.clear();
         }
         return next;
     }
@@ -207,25 +200,22 @@
         }
     }
 
-    private View findNextKeyboardNavigationGroup(
-            @KeyboardNavigationGroupType int groupType,
+    private View findNextKeyboardNavigationCluster(
             View root,
-            View currentGroup,
-            List<View> groups,
+            View currentCluster,
+            List<View> clusters,
             int direction) {
-        final int count = groups.size();
+        final int count = clusters.size();
 
         switch (direction) {
             case View.FOCUS_FORWARD:
             case View.FOCUS_DOWN:
             case View.FOCUS_RIGHT:
-                return getNextKeyboardNavigationGroup(
-                        groupType, root, currentGroup, groups, count);
+                return getNextKeyboardNavigationCluster(root, currentCluster, clusters, count);
             case View.FOCUS_BACKWARD:
             case View.FOCUS_UP:
             case View.FOCUS_LEFT:
-                return getPreviousKeyboardNavigationGroup(
-                        groupType, root, currentGroup, groups, count);
+                return getPreviousKeyboardNavigationCluster(root, currentCluster, clusters, count);
             default:
                 throw new IllegalArgumentException("Unknown direction: " + direction);
         }
@@ -331,70 +321,50 @@
         return null;
     }
 
-    private static View getNextKeyboardNavigationGroup(
-            @KeyboardNavigationGroupType int groupType,
+    private static View getNextKeyboardNavigationCluster(
             View root,
-            View currentGroup,
-            List<View> groups,
+            View currentCluster,
+            List<View> clusters,
             int count) {
-        if (currentGroup == null) {
-            // The current group is the default one.
-            // The next group after the default one is the first one.
-            // Note that the caller guarantees that 'group' is not empty.
-            return groups.get(0);
+        if (currentCluster == null) {
+            // The current cluster is the default one.
+            // The next cluster after the default one is the first one.
+            // Note that the caller guarantees that 'clusters' is not empty.
+            return clusters.get(0);
         }
 
-        final int position = groups.lastIndexOf(currentGroup);
+        final int position = clusters.lastIndexOf(currentCluster);
         if (position >= 0 && position + 1 < count) {
-            // Return the next non-default group if we can find it.
-            return groups.get(position + 1);
+            // Return the next non-default cluster if we can find it.
+            return clusters.get(position + 1);
         }
 
-        switch (groupType) {
-            case KEYBOARD_NAVIGATION_GROUP_CLUSTER:
-                // The current cluster is the last one. The next one is the default one, i.e. the
-                // root.
-                return root;
-            case KEYBOARD_NAVIGATION_GROUP_SECTION:
-                // There is no "default section", hence returning the first one.
-                return groups.get(0);
-            default:
-                throw new IllegalArgumentException(
-                        "Unknown keyboard navigation group type: " + groupType);
-        }
+        // The current cluster is the last one. The next one is the default one, i.e. the
+        // root.
+        return root;
     }
 
-    private static View getPreviousKeyboardNavigationGroup(
-            @KeyboardNavigationGroupType int groupType,
+    private static View getPreviousKeyboardNavigationCluster(
             View root,
-            View currentGroup,
-            List<View> groups,
+            View currentCluster,
+            List<View> clusters,
             int count) {
-        if (currentGroup == null) {
-            // The current group is the default one.
-            // The previous group before the default one is the last one.
-            // Note that the caller guarantees that 'groups' is not empty.
-            return groups.get(count - 1);
+        if (currentCluster == null) {
+            // The current cluster is the default one.
+            // The previous cluster before the default one is the last one.
+            // Note that the caller guarantees that 'clusters' is not empty.
+            return clusters.get(count - 1);
         }
 
-        final int position = groups.indexOf(currentGroup);
+        final int position = clusters.indexOf(currentCluster);
         if (position > 0) {
-            // Return the previous non-default group if we can find it.
-            return groups.get(position - 1);
+            // Return the previous non-default cluster if we can find it.
+            return clusters.get(position - 1);
         }
 
-        switch (groupType) {
-            case KEYBOARD_NAVIGATION_GROUP_CLUSTER:
-                // The current cluster is the first one. The previous one is the default one, i.e.
-                // the root.
-                return root;
-            case KEYBOARD_NAVIGATION_GROUP_SECTION:
-                // There is no "default section", hence returning the last one.
-                return groups.get(count - 1);
-            default:
-                throw new IllegalArgumentException(
-                        "Unknown keyboard navigation group type: " + groupType);
-        }
+        // The current cluster is the first one. The previous one is the default one, i.e.
+        // the root.
+        return root;
     }
 
     /**
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 800a63f..92f0e8f 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -137,7 +137,7 @@
     /**
      * Identifiers for metrics available for each frame.
      *
-     * {@see {@link #getMetric(int)}}
+     * {@see #getMetric(int)}
      * @hide
      */
     @IntDef({
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 707300f..10b1e19 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -96,4 +96,9 @@
      * Called when Keyboard Shortcuts are requested for the window.
      */
     void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId);
+
+    /**
+     * Tell the window that it is either gaining or losing pointer capture.
+     */
+    void dispatchPointerCaptureChanged(boolean hasCapture);
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 19edb5c..c789f8c 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -135,9 +135,6 @@
     // ids that were affected by the update, ActivityManager should resize these stacks.
     int[] setNewDisplayOverrideConfiguration(in Configuration overrideConfig, int displayId);
 
-    // Retrieves the new bounds after the configuration update evaluated by window manager.
-    Rect getBoundsForNewConfiguration(int stackId);
-
     void startFreezingScreen(int exitAnim, int enterAnim);
     void stopFreezingScreen();
 
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 55f64d9..035d48f 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -237,6 +237,14 @@
     public static final int SOURCE_TRACKBALL = 0x00010000 | SOURCE_CLASS_TRACKBALL;
 
     /**
+     * The input source is a mouse device whose relative motions should be interpreted as
+     * navigation events.
+     *
+     * @see #SOURCE_CLASS_TRACKBALL
+     */
+    public static final int SOURCE_MOUSE_RELATIVE = 0x00020000 | SOURCE_CLASS_TRACKBALL;
+
+    /**
      * The input source is a touch pad or digitizer tablet that is not
      * associated with a display (unlike {@link #SOURCE_TOUCHSCREEN}).
      *
@@ -975,6 +983,7 @@
         appendSourceDescriptionIfApplicable(description, SOURCE_MOUSE, "mouse");
         appendSourceDescriptionIfApplicable(description, SOURCE_STYLUS, "stylus");
         appendSourceDescriptionIfApplicable(description, SOURCE_TRACKBALL, "trackball");
+        appendSourceDescriptionIfApplicable(description, SOURCE_MOUSE_RELATIVE, "mouse_relative");
         appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHPAD, "touchpad");
         appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK, "joystick");
         appendSourceDescriptionIfApplicable(description, SOURCE_GAMEPAD, "gamepad");
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 88f2d34..02202db 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -675,8 +675,8 @@
      *
      * @return The modifier behavior for this keyboard.
      *
-     * @see {@link #MODIFIER_BEHAVIOR_CHORDED}
-     * @see {@link #MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED}
+     * @see #MODIFIER_BEHAVIOR_CHORDED
+     * @see #MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED
      */
     public int getModifierBehavior() {
         switch (getKeyboardType()) {
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index f3f3d40..7a5c65f 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -335,7 +335,7 @@
         private ActionProvider itemActionProvider;
 
         private CharSequence itemContentDescription;
-        private CharSequence itemTooltip;
+        private CharSequence itemTooltipText;
 
         private static final int defaultGroupId = NO_ID;
         private static final int defaultItemId = NO_ID;
@@ -429,7 +429,7 @@
 
             itemContentDescription =
                     a.getText(com.android.internal.R.styleable.MenuItem_contentDescription);
-            itemTooltip = a.getText(com.android.internal.R.styleable.MenuItem_tooltip);
+            itemTooltipText = a.getText(com.android.internal.R.styleable.MenuItem_tooltipText);
 
             a.recycle();
 
@@ -495,7 +495,7 @@
             }
 
             item.setContentDescription(itemContentDescription);
-            item.setTooltip(itemTooltip);
+            item.setTooltipText(itemTooltipText);
         }
 
         public MenuItem addItem() {
diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java
index 5ced765..3f8d089 100644
--- a/core/java/android/view/MenuItem.java
+++ b/core/java/android/view/MenuItem.java
@@ -621,9 +621,9 @@
     /**
      * Change the tooltip text associated with this menu item.
      *
-     * @param tooltip The new tooltip text.
+     * @param tooltipText The new tooltip text.
      */
-    default MenuItem setTooltip(CharSequence tooltip) {
+    default MenuItem setTooltipText(CharSequence tooltipText) {
         return this;
     }
 
@@ -632,7 +632,7 @@
      *
      * @return The tooltip text.
      */
-    default CharSequence getTooltip() {
+    default CharSequence getTooltipText() {
         return null;
     }
 }
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index fc66697..ea6e63c 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -139,9 +139,6 @@
                 RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
     }
 
-    // Note: written by native when display lists are detached
-    private boolean mValid;
-
     // Do not access directly unless you are ThreadedRenderer
     final long mNativeRenderNode;
     private final View mOwningView;
@@ -233,7 +230,6 @@
         long displayList = canvas.finishRecording();
         nSetDisplayList(mNativeRenderNode, displayList);
         canvas.recycle();
-        mValid = true;
     }
 
     /**
@@ -242,10 +238,7 @@
      * obsolete resources after related resources are gone.
      */
     public void discardDisplayList() {
-        if (!mValid) return;
-
         nSetDisplayList(mNativeRenderNode, 0);
-        mValid = false;
     }
 
     /**
@@ -254,10 +247,12 @@
      *
      * @return boolean true if the display list is able to be replayed, false otherwise.
      */
-    public boolean isValid() { return mValid; }
+    public boolean isValid() {
+        return nIsValid(mNativeRenderNode);
+    }
 
     long getNativeDisplayList() {
-        if (!mValid) {
+        if (!isValid()) {
             throw new IllegalStateException("The display list is not valid.");
         }
         return mNativeRenderNode;
@@ -827,8 +822,7 @@
     // Regular JNI methods
     ///////////////////////////////////////////////////////////////////////////
 
-    // Intentionally not static because it acquires a reference to 'this'
-    private native long nCreate(String name);
+    private static native long nCreate(String name);
 
     private static native long nGetNativeFinalizer();
     private static native void nOutput(long renderNode);
@@ -853,6 +847,9 @@
     // @CriticalNative methods
     ///////////////////////////////////////////////////////////////////////////
 
+    @CriticalNative
+    private static native boolean nIsValid(long renderNode);
+
     // Matrix
 
     @CriticalNative
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index f3ebcb4..4ceb236 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -496,15 +496,6 @@
 
     private static void destroyResources(View view) {
         view.destroyHardwareResources();
-
-        if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup) view;
-
-            int count = group.getChildCount();
-            for (int i = 0; i < count; i++) {
-                destroyResources(group.getChildAt(i));
-            }
-        }
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9d1af50..ba9bb67 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -857,22 +857,39 @@
      */
     static boolean sCascadedDragDrop;
 
-    /**
-     * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
-     * calling setFlags.
-     */
-    private static final int NOT_FOCUSABLE = 0x00000000;
+    /** @hide */
+    @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Focusable {}
 
     /**
-     * This view wants keystrokes. Use with TAKES_FOCUS_MASK when calling
-     * setFlags.
+     * This view does not want keystrokes.
+     * <p>
+     * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code
+     * android:focusable}.
      */
-    private static final int FOCUSABLE = 0x00000001;
+    public static final int NOT_FOCUSABLE = 0x00000000;
+
+    /**
+     * This view wants keystrokes.
+     * <p>
+     * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code
+     * android:focusable}.
+     */
+    public static final int FOCUSABLE = 0x00000001;
+
+    /**
+     * This view determines focusability automatically. This is the default.
+     * <p>
+     * Use with {@link #setFocusable(int)} and <a href="#attr_android:focusable">{@code
+     * android:focusable}.
+     */
+    public static final int FOCUSABLE_AUTO = 0x00000010;
 
     /**
      * Mask for use with setFlags indicating bits used for focus.
      */
-    private static final int FOCUSABLE_MASK = 0x00000001;
+    private static final int FOCUSABLE_MASK = 0x00000011;
 
     /**
      * This view will adjust its padding to fit sytem windows (e.g. status bar)
@@ -1252,14 +1269,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface FocusRealDirection {} // Like @FocusDirection, but without forward/backward
 
-    /** @hide */
-    @IntDef({
-            KEYBOARD_NAVIGATION_GROUP_CLUSTER,
-            KEYBOARD_NAVIGATION_GROUP_SECTION
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface KeyboardNavigationGroupType {}
-
     /**
      * Use with {@link #focusSearch(int)}. Move focus to the previous selectable
      * item.
@@ -1293,18 +1302,6 @@
     public static final int FOCUS_DOWN = 0x00000082;
 
     /**
-     * Use with {@link #keyboardNavigationGroupSearch(int, View, int)}. Search for a keyboard
-     * navigation cluster.
-     */
-    public static final int KEYBOARD_NAVIGATION_GROUP_CLUSTER = 1;
-
-    /**
-     * Use with {@link #keyboardNavigationGroupSearch(int, View, int)}. Search for a keyboard
-     * navigation section.
-     */
-    public static final int KEYBOARD_NAVIGATION_GROUP_SECTION = 2;
-
-    /**
      * Bits of {@link #getMeasuredWidthAndState()} and
      * {@link #getMeasuredWidthAndState()} that provide the actual measured size.
      */
@@ -1768,6 +1765,12 @@
      */
     int mAccessibilityViewId = NO_ID;
 
+    /**
+     * The stable ID of this view for auto-fill purposes.
+     */
+    private int mAutoFillId = NO_ID;
+
+
     private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
 
     SendViewStateChangedAccessibilityEvent mSendViewStateChangedAccessibilityEvent;
@@ -2500,7 +2503,7 @@
      *                    1              PFLAG3_SCROLL_INDICATOR_END
      *                   1               PFLAG3_ASSIST_BLOCKED
      *                  1                PFLAG3_CLUSTER
-     *                 1                 PFLAG3_SECTION
+     *                 x                 * NO LONGER NEEDED, SHOULD BE REUSED *
      *                1                  PFLAG3_FINGER_DOWN
      *               1                   PFLAG3_FOCUSED_BY_DEFAULT
      *           xxxx                    * NO LONGER NEEDED, SHOULD BE REUSED *
@@ -2710,14 +2713,6 @@
     private static final int PFLAG3_CLUSTER = 0x8000;
 
     /**
-     * Flag indicating that the view is a root of a keyboard navigation section.
-     *
-     * @see #isKeyboardNavigationSection()
-     * @see #setKeyboardNavigationSection(boolean)
-     */
-    private static final int PFLAG3_SECTION = 0x10000;
-
-    /**
      * Indicates that the user is currently touching the screen.
      * Currently used for the tooltip positioning only.
      */
@@ -3709,6 +3704,8 @@
         private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
 
         OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
+
+        OnCapturedPointerListener mOnCapturedPointerListener;
     }
 
     ListenerInfo mListenerInfo;
@@ -3718,7 +3715,7 @@
          * Text to be displayed in a tooltip popup.
          */
         @Nullable
-        CharSequence mTooltip;
+        CharSequence mTooltipText;
 
         /**
          * View-relative position of the tooltip anchor point.
@@ -3807,11 +3804,6 @@
      */
     int mNextClusterForwardId = View.NO_ID;
 
-    /**
-     * User-specified next keyboard navigation section.
-     */
-    int mNextSectionForwardId = View.NO_ID;
-
     private CheckForLongPress mPendingCheckForLongPress;
     private CheckForTap mPendingCheckForTap = null;
     private PerformClick mPerformClick;
@@ -4169,7 +4161,7 @@
     public View(Context context) {
         mContext = context;
         mResources = context != null ? context.getResources() : null;
-        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
+        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | FOCUSABLE_AUTO;
         // Set some flags defaults
         mPrivateFlags2 =
                 (LAYOUT_DIRECTION_DEFAULT << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) |
@@ -4355,6 +4347,10 @@
 
         final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
 
+        // Set default values.
+        viewFlagValues |= FOCUSABLE_AUTO;
+        viewFlagMasks |= FOCUSABLE_AUTO;
+
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
@@ -4467,8 +4463,8 @@
                     }
                     break;
                 case com.android.internal.R.styleable.View_focusable:
-                    if (a.getBoolean(attr, false)) {
-                        viewFlagValues |= FOCUSABLE;
+                    viewFlagValues = (viewFlagValues & ~FOCUSABLE_MASK) | getFocusableAttribute(a);
+                    if ((viewFlagValues & FOCUSABLE_AUTO) == 0) {
                         viewFlagMasks |= FOCUSABLE_MASK;
                     }
                     break;
@@ -4622,9 +4618,6 @@
                 case R.styleable.View_nextClusterForward:
                     mNextClusterForwardId = a.getResourceId(attr, View.NO_ID);
                     break;
-                case R.styleable.View_nextSectionForward:
-                    mNextSectionForwardId = a.getResourceId(attr, View.NO_ID);
-                    break;
                 case R.styleable.View_minWidth:
                     mMinWidth = a.getDimensionPixelSize(attr, 0);
                     break;
@@ -4761,19 +4754,14 @@
                         forceHasOverlappingRendering(a.getBoolean(attr, true));
                     }
                     break;
-                case R.styleable.View_tooltip:
-                    setTooltip(a.getText(attr));
+                case R.styleable.View_tooltipText:
+                    setTooltipText(a.getText(attr));
                     break;
                 case R.styleable.View_keyboardNavigationCluster:
                     if (a.peekValue(attr) != null) {
                         setKeyboardNavigationCluster(a.getBoolean(attr, true));
                     }
                     break;
-                case R.styleable.View_keyboardNavigationSection:
-                    if (a.peekValue(attr) != null) {
-                        setKeyboardNavigationSection(a.getBoolean(attr, true));
-                    }
-                    break;
                 case R.styleable.View_focusedByDefault:
                     if (a.peekValue(attr) != null) {
                         setFocusedByDefault(a.getBoolean(attr, true));
@@ -5047,7 +5035,7 @@
             case GONE: out.append('G'); break;
             default: out.append('.'); break;
         }
-        out.append((mViewFlags&FOCUSABLE_MASK) == FOCUSABLE ? 'F' : '.');
+        out.append((mViewFlags & FOCUSABLE) == FOCUSABLE ? 'F' : '.');
         out.append((mViewFlags&ENABLED_MASK) == ENABLED ? 'E' : '.');
         out.append((mViewFlags&DRAW_MASK) == WILL_NOT_DRAW ? '.' : 'D');
         out.append((mViewFlags&SCROLLBARS_HORIZONTAL) != 0 ? 'H' : '.');
@@ -6934,8 +6922,7 @@
                 & (View.AUTO_FILL_FLAG_TYPE_FILL
                         | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0;
         final int id = mID;
-        if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0
-                && (id&0x0000ffff) != 0) {
+        if (id != NO_ID && !isViewIdGenerated(id)) {
             String pkg, type, entry;
             try {
                 final Resources res = getResources();
@@ -6953,8 +6940,8 @@
         if (forAutoFill) {
             // The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
             // reuse the accessibility id to save space.
-            structure.setAutoFillId(getAccessibilityViewId());
-
+            mAutoFillId = getAccessibilityViewId();
+            structure.setAutoFillId(mAutoFillId);
             structure.setAutoFillType(getAutoFillType());
         }
 
@@ -7084,7 +7071,7 @@
     }
 
     /**
-     * Describes the auto-fill type that should be used on callas to
+     * Describes the auto-fill type that should be used on calls to
      * {@link #autoFill(AutoFillValue)} and
      * {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}.
      *
@@ -7581,6 +7568,20 @@
     }
 
     /**
+     * Gets the unique identifier of this view for auto-fill purposes.
+     *
+     * <p>It's only set after {@link #onProvideAutoFillStructure(ViewStructure, int)} is called.
+     *
+     * @return The view autofill id or {@link #NO_ID} if
+     * {@link #onProvideAutoFillStructure(ViewStructure, int)}  was not called yet.
+     *
+     * @hide
+     */
+    public int getAutoFillViewId() {
+        return mAutoFillId;
+    }
+
+    /**
      * Gets the unique identifier of the window in which this View reseides.
      *
      * @return The window accessibility id.
@@ -8044,28 +8045,6 @@
     }
 
     /**
-     * Gets the id of the root of the next keyboard navigation section.
-     * @return The next keyboard navigation section ID, or {@link #NO_ID} if the framework should
-     * decide automatically.
-     *
-     * @attr ref android.R.styleable#View_nextSectionForward
-     */
-    public int getNextSectionForwardId() {
-        return mNextSectionForwardId;
-    }
-
-    /**
-     * Sets the id of the view to use as the root of the next keyboard navigation section.
-     * @param nextSectionForwardId The next section ID, or {@link #NO_ID} if the framework should
-     * decide automatically.
-     *
-     * @attr ref android.R.styleable#View_nextSectionForward
-     */
-    public void setNextSectionForwardId(int nextSectionForwardId) {
-        mNextSectionForwardId = nextSectionForwardId;
-    }
-
-    /**
      * Returns the visibility of this view and all of its ancestors
      *
      * @return True if this view and all of its ancestors are {@link #VISIBLE}
@@ -8517,20 +8496,39 @@
 
     /**
      * Set whether this view can receive the focus.
-     *
+     * <p>
      * Setting this to false will also ensure that this view is not focusable
      * in touch mode.
      *
      * @param focusable If true, this view can receive the focus.
      *
      * @see #setFocusableInTouchMode(boolean)
+     * @see #setFocusable(int)
      * @attr ref android.R.styleable#View_focusable
      */
     public void setFocusable(boolean focusable) {
-        if (!focusable) {
+        setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE);
+    }
+
+    /**
+     * Sets whether this view can receive focus.
+     * <p>
+     * Setting this to {@link #FOCUSABLE_AUTO} tells the framework to determine focusability
+     * automatically based on the view's interactivity. This is the default.
+     * <p>
+     * Setting this to NOT_FOCUSABLE will ensure that this view is also not focusable
+     * in touch mode.
+     *
+     * @param focusable One of {@link #NOT_FOCUSABLE}, {@link #FOCUSABLE},
+     *                  or {@link #FOCUSABLE_AUTO}.
+     * @see #setFocusableInTouchMode(boolean)
+     * @attr ref android.R.styleable#View_focusable
+     */
+    public void setFocusable(@Focusable int focusable) {
+        if ((focusable & (FOCUSABLE_AUTO | FOCUSABLE)) == 0) {
             setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
         }
-        setFlags(focusable ? FOCUSABLE : NOT_FOCUSABLE, FOCUSABLE_MASK);
+        setFlags(focusable, FOCUSABLE_MASK);
     }
 
     /**
@@ -9120,14 +9118,29 @@
 
 
     /**
-     * Returns whether this View is able to take focus.
+     * Returns whether this View is currently able to take focus.
      *
      * @return True if this view can take focus, or false otherwise.
-     * @attr ref android.R.styleable#View_focusable
      */
     @ViewDebug.ExportedProperty(category = "focus")
     public final boolean isFocusable() {
-        return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK);
+        return FOCUSABLE == (mViewFlags & FOCUSABLE);
+    }
+
+    /**
+     * Returns the focusable setting for this view.
+     *
+     * @return One of {@link #NOT_FOCUSABLE}, {@link #FOCUSABLE}, or {@link #FOCUSABLE_AUTO}.
+     * @attr ref android.R.styleable#View_focusable
+     */
+    @ViewDebug.ExportedProperty(mapping = {
+            @ViewDebug.IntToString(from = NOT_FOCUSABLE, to = "NOT_FOCUSABLE"),
+            @ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"),
+            @ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO")
+            })
+    @Focusable
+    public int getFocusable() {
+        return (mViewFlags & FOCUSABLE_AUTO) > 0 ? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE;
     }
 
     /**
@@ -9187,49 +9200,11 @@
     }
 
     /**
-     * Returns whether this View is a root of a keyboard navigation section.
-     *
-     * @return True if this view is a root of a section, or false otherwise.
-     * @attr ref android.R.styleable#View_keyboardNavigationSection
-     */
-    @ViewDebug.ExportedProperty(category = "keyboardNavigationSection")
-    public final boolean isKeyboardNavigationSection() {
-        return (mPrivateFlags3 & PFLAG3_SECTION) != 0;
-    }
-
-    /**
-     * Set whether this view is a root of a keyboard navigation section.
-     *
-     * @param isSection If true, this view is a root of a section.
-     *
-     * @attr ref android.R.styleable#View_keyboardNavigationSection
-     */
-    public void setKeyboardNavigationSection(boolean isSection) {
-        if (isSection) {
-            mPrivateFlags3 |= PFLAG3_SECTION;
-        } else {
-            mPrivateFlags3 &= ~PFLAG3_SECTION;
-        }
-    }
-
-    final boolean isKeyboardNavigationGroupOfType(@KeyboardNavigationGroupType int groupType) {
-        switch (groupType) {
-            case KEYBOARD_NAVIGATION_GROUP_CLUSTER:
-                return isKeyboardNavigationCluster();
-            case KEYBOARD_NAVIGATION_GROUP_SECTION:
-                return isKeyboardNavigationSection();
-            default:
-                throw new IllegalArgumentException(
-                        "Unknown keyboard navigation group type: " + groupType);
-        }
-    }
-
-    /**
      * Returns whether this View should receive focus when the focus is restored for the view
      * hierarchy containing this view.
      * <p>
      * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
-     * window or serves as a target of cluster or section navigation.
+     * window or serves as a target of cluster navigation.
      *
      * @see #restoreDefaultFocus(int)
      *
@@ -9246,7 +9221,7 @@
      * hierarchy containing this view.
      * <p>
      * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
-     * window or serves as a target of cluster or section navigation.
+     * window or serves as a target of cluster navigation.
      *
      * @param isFocusedByDefault {@code true} to set this view as the default-focus view,
      *                           {@code false} otherwise.
@@ -9285,35 +9260,28 @@
     }
 
     /**
-     * Find the nearest keyboard navigation group in the specified direction. The group type can be
-     * either a cluster or a section.
-     * This does not actually give focus to that group.
+     * Find the nearest keyboard navigation cluster in the specified direction.
+     * This does not actually give focus to that cluster.
      *
-     * @param groupType Type of the keyboard navigation group
-     * @param currentGroup The starting point of the search. Null means the current group is not
-     *                     found yet
+     * @param currentCluster The starting point of the search. Null means the current cluster is not
+     *                       found yet
      * @param direction Direction to look
      *
-     * @return The nearest keyboard navigation group in the specified direction, or null if none
+     * @return The nearest keyboard navigation cluster in the specified direction, or null if none
      *         can be found
      */
-    public View keyboardNavigationGroupSearch(
-            @KeyboardNavigationGroupType int groupType, View currentGroup, int direction) {
-        if (isKeyboardNavigationGroupOfType(groupType)) {
-            currentGroup = this;
+    public View keyboardNavigationClusterSearch(View currentCluster, int direction) {
+        if (isKeyboardNavigationCluster()) {
+            currentCluster = this;
         }
-        if (isRootNamespace()
-                || (groupType == KEYBOARD_NAVIGATION_GROUP_SECTION
-                && isKeyboardNavigationCluster())) {
+        if (isRootNamespace()) {
             // Root namespace means we should consider ourselves the top of the
             // tree for group searching; otherwise we could be group searching
             // into other tabs.  see LocalActivityManager and TabHost for more info.
-            // In addition, a cluster node works as a root for section searches.
-            return FocusFinder.getInstance().findNextKeyboardNavigationGroup(
-                    groupType, this, currentGroup, direction);
+            return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
+                    this, currentCluster, direction);
         } else if (mParent != null) {
-            return mParent.keyboardNavigationGroupSearch(
-                    groupType, currentGroup, direction);
+            return mParent.keyboardNavigationClusterSearch(currentCluster, direction);
         }
         return null;
     }
@@ -9441,19 +9409,16 @@
     }
 
     /**
-     * Adds any keyboard navigation group roots that are descendants of this view (possibly
-     * including this view if it is a group root itself) to views. The group type can be either a
-     * cluster or a section.
+     * Adds any keyboard navigation cluster roots that are descendants of this view (possibly
+     * including this view if it is a cluster root itself) to views.
      *
-     * @param groupType Type of the keyboard navigation group
-     * @param views Keyboard navigation group roots found so far
+     * @param views Keyboard navigation cluster roots found so far
      * @param direction Direction to look
      */
-    public void addKeyboardNavigationGroups(
-            @KeyboardNavigationGroupType int groupType,
+    public void addKeyboardNavigationClusters(
             @NonNull Collection<View> views,
             int direction) {
-        if (!(isKeyboardNavigationGroupOfType(groupType))) {
+        if (!(isKeyboardNavigationCluster())) {
             return;
         }
         views.add(this);
@@ -9727,8 +9692,8 @@
 
     private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
         // need to be focusable
-        if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||
-                (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
+        if ((mViewFlags & FOCUSABLE) != FOCUSABLE
+                || (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
             return false;
         }
 
@@ -10698,6 +10663,25 @@
     }
 
     /**
+     * Pass a captured pointer event down to the focused view.
+     *
+     * @param event The motion event to be dispatched.
+     * @return True if the event was handled by the view, false otherwise.
+     */
+    public boolean dispatchCapturedPointerEvent(MotionEvent event) {
+        if (!hasPointerCapture()) {
+            return false;
+        }
+        //noinspection SimplifiableIfStatement
+        ListenerInfo li = mListenerInfo;
+        if (li != null && li.mOnCapturedPointerListener != null
+                && li.mOnCapturedPointerListener.onCapturedPointer(this, event)) {
+            return true;
+        }
+        return onCapturedPointerEvent(event);
+    }
+
+    /**
      * Dispatch a generic motion event.
      * <p>
      * Generic motion events with source class {@link InputDevice#SOURCE_CLASS_POINTER}
@@ -12082,14 +12066,27 @@
         }
         int privateFlags = mPrivateFlags;
 
+        // If focusable is auto, update the FOCUSABLE bit.
+        if (((mViewFlags & FOCUSABLE_AUTO) != 0)
+                && (changed & (FOCUSABLE_MASK | CLICKABLE | FOCUSABLE_IN_TOUCH_MODE)) != 0) {
+            int newFocus = NOT_FOCUSABLE;
+            if ((mViewFlags & (CLICKABLE | FOCUSABLE_IN_TOUCH_MODE)) != 0) {
+                newFocus = FOCUSABLE;
+            } else {
+                mViewFlags = (mViewFlags & ~FOCUSABLE_IN_TOUCH_MODE);
+            }
+            mViewFlags = (mViewFlags & ~FOCUSABLE) | newFocus;
+            int focusChanged = (old & FOCUSABLE) ^ (newFocus & FOCUSABLE);
+            changed = (changed & ~FOCUSABLE) | focusChanged;
+        }
+
         /* Check if the FOCUSABLE bit has changed */
-        if (((changed & FOCUSABLE_MASK) != 0) &&
-                ((privateFlags & PFLAG_HAS_BOUNDS) !=0)) {
-            if (((old & FOCUSABLE_MASK) == FOCUSABLE)
+        if (((changed & FOCUSABLE) != 0) && ((privateFlags & PFLAG_HAS_BOUNDS) != 0)) {
+            if (((old & FOCUSABLE) == FOCUSABLE)
                     && ((privateFlags & PFLAG_FOCUSED) != 0)) {
                 /* Give up focus if we are no longer focusable */
                 clearFocus();
-            } else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)
+            } else if (((old & FOCUSABLE) == NOT_FOCUSABLE)
                     && ((privateFlags & PFLAG_FOCUSED) == 0)) {
                 /*
                  * Tell the view system that we are now available to take focus
@@ -12232,7 +12229,7 @@
         }
 
         if (accessibilityEnabled) {
-            if ((changed & FOCUSABLE_MASK) != 0 || (changed & VISIBILITY_MASK) != 0
+            if ((changed & FOCUSABLE) != 0 || (changed & VISIBILITY_MASK) != 0
                     || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
                     || (changed & CONTEXT_CLICKABLE) != 0) {
                 if (oldIncludeForAccessibility != includeForAccessibility()) {
@@ -13586,7 +13583,7 @@
      * Any previously attached StateListAnimator will be detached.
      *
      * @param stateListAnimator The StateListAnimator to update the view
-     * @see {@link android.animation.StateListAnimator}
+     * @see android.animation.StateListAnimator
      */
     public void setStateListAnimator(StateListAnimator stateListAnimator) {
         if (mStateListAnimator == stateListAnimator) {
@@ -16637,11 +16634,12 @@
      */
     @CallSuper
     protected void destroyHardwareResources() {
-        // Although the Layer will be destroyed by RenderNode, we want to release
-        // the staging display list, which is also a signal to RenderNode that it's
-        // safe to free its copy of the display list as it knows that we will
-        // push an updated DisplayList if we try to draw again
-        resetDisplayList();
+        if (mOverlay != null) {
+            mOverlay.getOverlayView().destroyHardwareResources();
+        }
+        if (mGhostView != null) {
+            mGhostView.destroyHardwareResources();
+        }
     }
 
     /**
@@ -16812,11 +16810,8 @@
     }
 
     private void resetDisplayList() {
-        if (mRenderNode.isValid()) {
-            mRenderNode.discardDisplayList();
-        }
-
-        if (mBackgroundRenderNode != null && mBackgroundRenderNode.isValid()) {
+        mRenderNode.discardDisplayList();
+        if (mBackgroundRenderNode != null) {
             mBackgroundRenderNode.discardDisplayList();
         }
     }
@@ -18157,7 +18152,7 @@
     private static String printFlags(int flags) {
         String output = "";
         int numFlags = 0;
-        if ((flags & FOCUSABLE_MASK) == FOCUSABLE) {
+        if ((flags & FOCUSABLE) == FOCUSABLE) {
             output += "TAKES_FOCUS";
             numFlags++;
         }
@@ -22640,6 +22635,10 @@
         }
     }
 
+    private static boolean isViewIdGenerated(int id) {
+        return (id & 0xFF000000) == 0 && (id & 0x00FFFFFF) != 0;
+    }
+
     /**
      * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions.
      * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and
@@ -22708,7 +22707,110 @@
         return mPointerIcon;
     }
 
-    //
+    /**
+     * Checks pointer capture status.
+     *
+     * @return true if the view has pointer capture.
+     * @see #requestPointerCapture()
+     * @see #hasPointerCapture()
+     */
+    public boolean hasPointerCapture() {
+        final ViewRootImpl viewRootImpl = getViewRootImpl();
+        if (viewRootImpl == null) {
+            return false;
+        }
+        return viewRootImpl.hasPointerCapture();
+    }
+
+    /**
+     * Requests pointer capture mode.
+     * <p>
+     * When the window has pointer capture, the mouse pointer icon will disappear and will not
+     * change its position. Further mouse will be dispatched with the source
+     * {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be available
+     * through {@link MotionEvent#getX} and {@link MotionEvent#getY}. Non-mouse events
+     * (touchscreens, or stylus) will not be affected.
+     * <p>
+     * If the window already has pointer capture, this call does nothing.
+     * <p>
+     * The capture may be released through {@link #releasePointerCapture()}, or will be lost
+     * automatically when the window loses focus.
+     *
+     * @see #releasePointerCapture()
+     * @see #hasPointerCapture()
+     */
+    public void requestPointerCapture() {
+        final ViewRootImpl viewRootImpl = getViewRootImpl();
+        if (viewRootImpl != null) {
+            viewRootImpl.requestPointerCapture(true);
+        }
+    }
+
+
+    /**
+     * Releases the pointer capture.
+     * <p>
+     * If the window does not have pointer capture, this call will do nothing.
+     * @see #requestPointerCapture()
+     * @see #hasPointerCapture()
+     */
+    public void releasePointerCapture() {
+        final ViewRootImpl viewRootImpl = getViewRootImpl();
+        if (viewRootImpl != null) {
+            viewRootImpl.requestPointerCapture(false);
+        }
+    }
+
+    /**
+     * Called when the window has just acquired or lost pointer capture.
+     *
+     * @param hasCapture True if the view now has pointerCapture, false otherwise.
+     */
+    @CallSuper
+    public void onPointerCaptureChange(boolean hasCapture) {
+    }
+
+    /**
+     * @see #onPointerCaptureChange
+     */
+    public void dispatchPointerCaptureChanged(boolean hasCapture) {
+        onPointerCaptureChange(hasCapture);
+    }
+
+    /**
+     * Implement this method to handle captured pointer events
+     *
+     * @param event The captured pointer event.
+     * @return True if the event was handled, false otherwise.
+     * @see #requestPointerCapture()
+     */
+    public boolean onCapturedPointerEvent(MotionEvent event) {
+        return false;
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when a captured pointer event
+     * is being dispatched this view. The callback will be invoked before the event is
+     * given to the view.
+     */
+    public interface OnCapturedPointerListener {
+        /**
+         * Called when a captured pointer event is dispatched to a view.
+         * @param view The view this event has been dispatched to.
+         * @param event The captured event.
+         * @return True if the listener has consumed the event, false otherwise.
+         */
+        boolean onCapturedPointer(View view, MotionEvent event);
+    }
+
+    /**
+     * Set a listener to receive callbacks when the pointer capture state of a view changes.
+     * @param l  The {@link OnCapturedPointerListener} to receive callbacks.
+     */
+    public void setOnCapturedPointerListener(OnCapturedPointerListener l) {
+        getListenerInfo().mOnCapturedPointerListener = l;
+    }
+
     // Properties
     //
     /**
@@ -24635,10 +24737,10 @@
      * menu). </li>
      * <li>On hover, after a brief delay since the pointer has stopped moving </li>
      *
-     * @param tooltip the tooltip text, or null if no tooltip is required
+     * @param tooltipText the tooltip text, or null if no tooltip is required
      */
-    public final void setTooltip(@Nullable CharSequence tooltip) {
-        if (TextUtils.isEmpty(tooltip)) {
+    public final void setTooltipText(@Nullable CharSequence tooltipText) {
+        if (TextUtils.isEmpty(tooltipText)) {
             setFlags(0, TOOLTIP);
             hideTooltip();
             mTooltipInfo = null;
@@ -24649,9 +24751,9 @@
                 mTooltipInfo.mShowTooltipRunnable = this::showHoverTooltip;
                 mTooltipInfo.mHideTooltipRunnable = this::hideTooltip;
             }
-            mTooltipInfo.mTooltip = tooltip;
+            mTooltipInfo.mTooltipText = tooltipText;
             if (mTooltipInfo.mTooltipPopup != null && mTooltipInfo.mTooltipPopup.isShowing()) {
-                mTooltipInfo.mTooltipPopup.updateContent(mTooltipInfo.mTooltip);
+                mTooltipInfo.mTooltipPopup.updateContent(mTooltipInfo.mTooltipText);
             }
         }
     }
@@ -24662,8 +24764,8 @@
      * @return the tooltip text
      */
     @Nullable
-    public final CharSequence getTooltip() {
-        return mTooltipInfo != null ? mTooltipInfo.mTooltip : null;
+    public final CharSequence getTooltipText() {
+        return mTooltipInfo != null ? mTooltipInfo.mTooltipText : null;
     }
 
     private boolean showTooltip(int x, int y, boolean fromLongClick) {
@@ -24673,7 +24775,7 @@
         if ((mViewFlags & ENABLED_MASK) != ENABLED) {
             return false;
         }
-        final CharSequence tooltipText = getTooltip();
+        final CharSequence tooltipText = getTooltipText();
         if (TextUtils.isEmpty(tooltipText)) {
             return false;
         }
@@ -24778,6 +24880,19 @@
                 ViewConfiguration.getLongPressTooltipHideTimeout());
     }
 
+    private int getFocusableAttribute(TypedArray attributes) {
+        TypedValue val = new TypedValue();
+        if (attributes.getValue(com.android.internal.R.styleable.View_focusable, val)) {
+            if (val.type == TypedValue.TYPE_INT_BOOLEAN) {
+                return (val.data == 0 ? NOT_FOCUSABLE : FOCUSABLE);
+            } else {
+                return val.data;
+            }
+        } else {
+            return FOCUSABLE_AUTO;
+        }
+    }
+
     /**
      * @return The content view of the tooltip popup currently being shown, or null if the tooltip
      * is not showing.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 6d2f850..0e753f3 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -83,6 +83,13 @@
     private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500;
 
     /**
+     * Defines the duration in milliseconds a user needs to hold down the
+     * appropriate button to bring up the accessibility shortcut (first time) or enable it
+     * (once shortcut is configured).
+     */
+    private static final int A11Y_SHORTCUT_KEY_TIMEOUT = 3000;
+
+    /**
      * Defines the duration in milliseconds we will wait to see if a touch event
      * is a tap or a scroll. If the user does not move within this interval, it is
      * considered to be a tap.
@@ -785,6 +792,18 @@
     }
 
     /**
+     * The amount of time a user needs to press the relevant keys to activate the accessibility
+     * shortcut.
+     *
+     * @return how long a user needs to press the relevant keys to activate the accessibility
+     *   shortcut.
+     * @hide
+     */
+    public long getAccessibilityShortcutKeyTimeout() {
+        return A11Y_SHORTCUT_KEY_TIMEOUT;
+    }
+
+    /**
      * The amount of friction applied to scrolls and flings.
      *
      * @return A scalar dimensionless value representing the coefficient of
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1b42a90..af39eb3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -915,13 +915,10 @@
      */
     @Override
     public View focusSearch(View focused, int direction) {
-        if (isRootNamespace()
-                || isKeyboardNavigationCluster()
-                && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD)) {
+        if (isRootNamespace()) {
             // root namespace means we should consider ourselves the top of the
             // tree for focus searching; otherwise we could be focus searching
             // into other tabs.  see LocalActivityManager and TabHost for more info.
-            // Cluster's root works same way for the forward and backward navigation.
             return FocusFinder.getInstance().findNextFocus(this, focused, direction);
         } else if (mParent != null) {
             return mParent.focusSearch(focused, direction);
@@ -1136,12 +1133,6 @@
 
     @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if (isKeyboardNavigationCluster()
-                && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD) && !hasFocus()) {
-            // A cluster cannot be focus-entered from outside using forward/backward navigation.
-            return;
-        }
-
         final int focusableCount = views.size();
 
         final int descendantFocusability = getDescendantFocusability();
@@ -1175,11 +1166,10 @@
     }
 
     @Override
-    public void addKeyboardNavigationGroups(
-            @KeyboardNavigationGroupType int groupType, Collection<View> views, int direction) {
+    public void addKeyboardNavigationClusters(Collection<View> views, int direction) {
         final int focusableCount = views.size();
 
-        super.addKeyboardNavigationGroups(groupType, views, direction);
+        super.addKeyboardNavigationClusters(views, direction);
 
         if (focusableCount != views.size()) {
             // No need to look for groups inside a group.
@@ -1195,14 +1185,8 @@
 
         for (int i = 0; i < count; i++) {
             final View child = children[i];
-            if (groupType == KEYBOARD_NAVIGATION_GROUP_SECTION
-                    && child.isKeyboardNavigationCluster()) {
-                // When the current cluster is the default cluster, and we are searching for
-                // sections, ignore sections inside non-default clusters.
-                continue;
-            }
             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
-                child.addKeyboardNavigationGroups(groupType, views, direction);
+                child.addKeyboardNavigationClusters(views, direction);
             }
         }
     }
@@ -1772,6 +1756,34 @@
     }
 
     @Override
+    public boolean dispatchCapturedPointerEvent(MotionEvent event) {
+        if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
+                == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
+            if (super.dispatchCapturedPointerEvent(event)) {
+                return true;
+            }
+        } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
+                == PFLAG_HAS_BOUNDS) {
+            if (mFocused.dispatchCapturedPointerEvent(event)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void dispatchPointerCaptureChanged(boolean hasCapture) {
+        exitHoverTargets();
+
+        super.dispatchPointerCaptureChanged(hasCapture);
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchPointerCaptureChanged(hasCapture);
+        }
+    }
+
+    @Override
     public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
         final float x = event.getX(pointerIndex);
         final float y = event.getY(pointerIndex);
@@ -1788,15 +1800,15 @@
             for (int i = childrenCount - 1; i >= 0; i--) {
                 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
                 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
-                final PointF point = getLocalPoint();
-                if (isTransformedTouchPointInView(x, y, child, point)) {
-                    final PointerIcon pointerIcon =
-                            dispatchResolvePointerIcon(event, pointerIndex, child);
-                    if (pointerIcon != null) {
-                        if (preorderedList != null) preorderedList.clear();
-                        return pointerIcon;
-                    }
-                    break;
+                if (!canViewReceivePointerEvents(child)
+                        || !isTransformedTouchPointInView(x, y, child, null)) {
+                    continue;
+                }
+                final PointerIcon pointerIcon =
+                        dispatchResolvePointerIcon(event, pointerIndex, child);
+                if (pointerIcon != null) {
+                    if (preorderedList != null) preorderedList.clear();
+                    return pointerIcon;
                 }
             }
             if (preorderedList != null) preorderedList.clear();
@@ -2091,11 +2103,12 @@
                                 getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
                         final View child =
                                 getAndVerifyPreorderedView(preorderedList, children, childIndex);
-                        final PointF point = getLocalPoint();
-                        if (isTransformedTouchPointInView(x, y, child, point)) {
-                            if (dispatchTooltipHoverEvent(event, child)) {
-                                newTarget = child;
-                            }
+                        if (!canViewReceivePointerEvents(child)
+                                || !isTransformedTouchPointInView(x, y, child, null)) {
+                            continue;
+                        }
+                        if (dispatchTooltipHoverEvent(event, child)) {
+                            newTarget = child;
                             break;
                         }
                     }
@@ -3071,8 +3084,7 @@
         final View[] children = mChildren;
         for (int i = index; i != end; i += increment) {
             View child = children[i];
-            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
-                    && !child.isKeyboardNavigationCluster()) {
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                 if (child.requestFocus(direction, previouslyFocusedRect)) {
                     return true;
                 }
@@ -4613,6 +4625,16 @@
         clearCachedLayoutMode();
     }
 
+    /** @hide */
+    @Override
+    protected void destroyHardwareResources() {
+        super.destroyHardwareResources();
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).destroyHardwareResources();
+        }
+    }
+
     /**
      * Adds a view during layout. This is useful if in your onLayout() method,
      * you need to add more views (as does the list view for example).
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index c9277ca..79b05cd 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.view.View.KeyboardNavigationGroupType;
 import android.view.accessibility.AccessibilityEvent;
 
 /**
@@ -148,20 +147,17 @@
     public View focusSearch(View v, int direction);
 
     /**
-     * Find the nearest keyboard navigation group in the specified direction. The group type can be
-     * either a cluster or a section.
-     * This does not actually give focus to that group.
+     * Find the nearest keyboard navigation cluster in the specified direction.
+     * This does not actually give focus to that cluster.
      *
-     * @param groupType Type of the keyboard navigation group
-     * @param currentGroup The starting point of the search. Null means the current group is not
-     *                     found yet
+     * @param currentCluster The starting point of the search. Null means the current cluster is not
+     *                       found yet
      * @param direction Direction to look
      *
-     * @return The nearest keyboard navigation group in the specified direction, or null if none
+     * @return The nearest keyboard navigation cluster in the specified direction, or null if none
      *         can be found
      */
-    View keyboardNavigationGroupSearch(
-            @KeyboardNavigationGroupType int groupType, View currentGroup, int direction);
+    View keyboardNavigationClusterSearch(View currentCluster, int direction);
 
     /**
      * Change the z order of the child so it's on top of all other children.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c0f2c37..c9b9d5f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,8 +16,6 @@
 
 package android.view;
 
-import static android.view.View.KEYBOARD_NAVIGATION_GROUP_CLUSTER;
-import static android.view.View.KEYBOARD_NAVIGATION_GROUP_SECTION;
 import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
 import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -73,7 +71,6 @@
 import android.util.TypedValue;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
-import android.view.View.KeyboardNavigationGroupType;
 import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -195,8 +192,8 @@
     View mAccessibilityFocusedHost;
     AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
 
-    // The view which captures mouse input, or null when no one is capturing.
-    View mCapturingView;
+    // True if the window currently has pointer capture enabled.
+    boolean mPointerCapture;
 
     int mViewVisibility;
     boolean mAppVisible = true;
@@ -3203,6 +3200,27 @@
         }
     }
 
+    boolean hasPointerCapture() {
+        return mPointerCapture;
+    }
+
+    void requestPointerCapture(boolean enabled) {
+        if (mPointerCapture == enabled) {
+            return;
+        }
+        InputManager.getInstance().requestPointerCapture(mAttachInfo.mWindowToken, enabled);
+    }
+
+    private void handlePointerCaptureChanged(boolean hasCapture) {
+        if (mPointerCapture == hasCapture) {
+            return;
+        }
+        mPointerCapture = hasCapture;
+        if (mView != null) {
+            mView.dispatchPointerCaptureChanged(hasCapture);
+        }
+    }
+
     @Override
     public void requestChildFocus(View child, View focused) {
         if (DEBUG_INPUT_RESIZE) {
@@ -3396,6 +3414,7 @@
     private final static int MSG_DISPATCH_WINDOW_SHOWN = 25;
     private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
     private final static int MSG_UPDATE_POINTER_ICON = 27;
+    private final static int MSG_POINTER_CAPTURE_CHANGED = 28;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -3445,6 +3464,8 @@
                     return "MSG_DISPATCH_WINDOW_SHOWN";
                 case MSG_UPDATE_POINTER_ICON:
                     return "MSG_UPDATE_POINTER_ICON";
+                case MSG_POINTER_CAPTURE_CHANGED:
+                    return "MSG_POINTER_CAPTURE_CHANGED";
             }
             return super.getMessageName(message);
         }
@@ -3616,6 +3637,10 @@
                                 .softInputMode &=
                                     ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
                         mHasHadWindowFocus = true;
+                    } else {
+                        if (mPointerCapture) {
+                            handlePointerCaptureChanged(false);
+                        }
                     }
                 }
             } break;
@@ -3694,6 +3719,10 @@
                 MotionEvent event = (MotionEvent) msg.obj;
                 resetPointerIcon(event);
             } break;
+            case MSG_POINTER_CAPTURE_CHANGED: {
+                final boolean hasCapture = msg.arg1 != 0;
+                handlePointerCaptureChanged(hasCapture);
+            } break;
             }
         }
     }
@@ -4397,14 +4426,13 @@
             return false;
         }
 
-        private boolean performKeyboardGroupNavigation(
-                @KeyboardNavigationGroupType int groupType, int direction) {
+        private boolean performKeyboardGroupNavigation(int direction) {
             final View focused = mView.findFocus();
-            final View group = focused != null
-                    ? focused.keyboardNavigationGroupSearch(groupType, null, direction)
-                    : keyboardNavigationGroupSearch(groupType, null, direction);
+            final View cluster = focused != null
+                    ? focused.keyboardNavigationClusterSearch(null, direction)
+                    : keyboardNavigationClusterSearch(null, direction);
 
-            if (group != null && group.restoreDefaultFocus(View.FOCUS_DOWN)) {
+            if (cluster != null && cluster.restoreDefaultFocus(View.FOCUS_DOWN)) {
                 return true;
             }
 
@@ -4424,32 +4452,13 @@
             }
 
             int groupNavigationDirection = 0;
-            @KeyboardNavigationGroupType int groupType = 0;
 
-            if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed()) {
-                final int character =
-                        event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK);
-                if (character == '+') {
-                    groupType = KEYBOARD_NAVIGATION_GROUP_CLUSTER;
+            if (event.getAction() == KeyEvent.ACTION_DOWN
+                    && event.getKeyCode() == KeyEvent.KEYCODE_TAB) {
+                if (KeyEvent.metaStateHasModifiers(event.getMetaState(), KeyEvent.META_META_ON)) {
                     groupNavigationDirection = View.FOCUS_FORWARD;
-                }
-
-                if (character == '_') {
-                    groupType = KEYBOARD_NAVIGATION_GROUP_CLUSTER;
-                    groupNavigationDirection = View.FOCUS_BACKWARD;
-                }
-            }
-
-            if (event.getAction() == KeyEvent.ACTION_DOWN && event.isAltPressed()) {
-                final int character =
-                        event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_ALT_MASK);
-                if (character == '+') {
-                    groupType = KEYBOARD_NAVIGATION_GROUP_SECTION;
-                    groupNavigationDirection = View.FOCUS_FORWARD;
-                }
-
-                if (character == '_') {
-                    groupType = KEYBOARD_NAVIGATION_GROUP_SECTION;
+                } else if (KeyEvent.metaStateHasModifiers(event.getMetaState(),
+                        KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON)) {
                     groupNavigationDirection = View.FOCUS_BACKWARD;
                 }
             }
@@ -4479,7 +4488,7 @@
             // Handle automatic focus changes.
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 if (groupNavigationDirection != 0) {
-                    if (performKeyboardGroupNavigation(groupType, groupNavigationDirection)) {
+                    if (performKeyboardGroupNavigation(groupNavigationDirection)) {
                         return FINISH_HANDLED;
                     }
                 } else {
@@ -4495,11 +4504,8 @@
             final MotionEvent event = (MotionEvent)q.mEvent;
 
             mAttachInfo.mUnbufferedDispatchRequested = false;
-            final View eventTarget =
-                    (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
-                            mCapturingView : mView;
             mAttachInfo.mHandlingPointerEvent = true;
-            boolean handled = eventTarget.dispatchPointerEvent(event);
+            boolean handled = mView.dispatchPointerEvent(event);
             maybeUpdatePointerIcon(event);
             maybeUpdateTooltip(event);
             mAttachInfo.mHandlingPointerEvent = false;
@@ -4533,6 +4539,12 @@
         private int processTrackballEvent(QueuedInputEvent q) {
             final MotionEvent event = (MotionEvent)q.mEvent;
 
+            if (event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
+                if (!hasPointerCapture() || mView.dispatchCapturedPointerEvent(event)) {
+                    return FINISH_HANDLED;
+                }
+            }
+
             if (mView.dispatchTrackballEvent(event)) {
                 return FINISH_HANDLED;
             }
@@ -5910,11 +5922,10 @@
      * {@inheritDoc}
      */
     @Override
-    public View keyboardNavigationGroupSearch(
-            @KeyboardNavigationGroupType int groupType, View currentGroup, int direction) {
+    public View keyboardNavigationClusterSearch(View currentCluster, int direction) {
         checkThread();
-        return FocusFinder.getInstance().findNextKeyboardNavigationGroup(groupType,
-                mView, currentGroup, direction);
+        return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
+                mView, currentCluster, direction);
     }
 
     public void debug() {
@@ -6724,6 +6735,14 @@
                 MSG_REQUEST_KEYBOARD_SHORTCUTS, deviceId, 0, receiver).sendToTarget();
     }
 
+    public void dispatchPointerCaptureChanged(boolean on) {
+        final int what = MSG_POINTER_CAPTURE_CHANGED;
+        mHandler.removeMessages(what);
+        Message msg = mHandler.obtainMessage(what);
+        msg.arg1 = on ? 1 : 0;
+        mHandler.sendMessage(msg);
+    }
+
     /**
      * Post a callback to send a
      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
@@ -7304,6 +7323,15 @@
                 viewAncestor.dispatchRequestKeyboardShortcuts(receiver, deviceId);
             }
         }
+
+        @Override
+        public void dispatchPointerCaptureChanged(boolean hasCapture) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchPointerCaptureChanged(hasCapture);
+            }
+        }
+
     }
 
     public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 8bc988d..c424086 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -27,6 +27,7 @@
 import android.annotation.StyleRes;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -573,6 +574,13 @@
          */
         default public void onProvideKeyboardShortcuts(
                 List<KeyboardShortcutGroup> data, @Nullable Menu menu, int deviceId) { };
+
+        /**
+         * Called when pointer capture is enabled or disabled for the current window.
+         *
+         * @param hasCapture True if the window has pointer capture.
+         */
+        default public void onPointerCaptureChanged(boolean hasCapture) { };
     }
 
     /** @hide */
@@ -1128,6 +1136,28 @@
     }
 
     /**
+     * <p>Set the color mode of the window. Setting the color mode might
+     * override the window's pixel {@link WindowManager.LayoutParams#format format}.</p>
+     *
+     * <p>The color mode must be one of {@link ActivityInfo#COLOR_MODE_DEFAULT},
+     * {@link ActivityInfo#COLOR_MODE_WIDE_COLOR_GAMUT} or {@link ActivityInfo#COLOR_MODE_HDR}.</p>
+     */
+    public void setColorMode(@ActivityInfo.ColorMode int colorMode) {
+        final WindowManager.LayoutParams attrs = getAttributes();
+        attrs.setColorMode(colorMode);
+        dispatchWindowAttributesChanged(attrs);
+    }
+
+    /**
+     * Returns the color mode of the window, one of {@link ActivityInfo#COLOR_MODE_DEFAULT},
+     * {@link ActivityInfo#COLOR_MODE_WIDE_COLOR_GAMUT} or {@link ActivityInfo#COLOR_MODE_HDR}.
+     */
+    @ActivityInfo.ColorMode
+    public int getColorMode() {
+        return getAttributes().getColorMode();
+    }
+
+    /**
      * Set the amount of dim behind the window when using
      * {@link WindowManager.LayoutParams#FLAG_DIM_BEHIND}.  This overrides
      * the default dim amount of that is selected by the Window based on
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index 8f2d2e1..02c8945 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -158,5 +158,10 @@
             List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
         mWrapped.onProvideKeyboardShortcuts(data, menu, deviceId);
     }
+
+    @Override
+    public void onPointerCaptureChanged(boolean hasCapture) {
+        mWrapped.onPointerCaptureChanged(hasCapture);
+    }
 }
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e5a6ebd..bf840e5 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -22,7 +22,6 @@
 import android.app.Presentation;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
-import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -1577,7 +1576,8 @@
 
         /**
          * The desired bitmap format.  May be one of the constants in
-         * {@link android.graphics.PixelFormat}.  Default is OPAQUE.
+         * {@link android.graphics.PixelFormat}. The choice of format
+         * might be overridden by {@link #setColorMode(int)}. Default is OPAQUE.
          */
         public int format;
 
@@ -1833,6 +1833,17 @@
          */
         public long hideTimeoutMilliseconds = -1;
 
+        /**
+         * The color mode requested by this window. The target display may
+         * not be able to honor the request. When the color mode is not set
+         * to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
+         * pixel format specified in {@link #format}.
+         *
+         * @hide
+         */
+        @ActivityInfo.ColorMode
+        private int mColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+
         public LayoutParams() {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             type = TYPE_APPLICATION;
@@ -1912,6 +1923,31 @@
             preservePreviousSurfaceInsets = preservePrevious;
         }
 
+        /**
+         * <p>Set the color mode of the window. Setting the color mode might
+         * override the window's pixel {@link WindowManager.LayoutParams#format format}.</p>
+         *
+         * <p>The color mode must be one of {@link ActivityInfo#COLOR_MODE_DEFAULT},
+         * {@link ActivityInfo#COLOR_MODE_WIDE_COLOR_GAMUT} or
+         * {@link ActivityInfo#COLOR_MODE_HDR}.</p>
+         *
+         * @see #getColorMode()
+         */
+        public void setColorMode(@ActivityInfo.ColorMode int colorMode) {
+            mColorMode = colorMode;
+        }
+
+        /**
+         * Returns the color mode of the window, one of {@link ActivityInfo#COLOR_MODE_DEFAULT},
+         * {@link ActivityInfo#COLOR_MODE_WIDE_COLOR_GAMUT} or {@link ActivityInfo#COLOR_MODE_HDR}.
+         *
+         * @see #setColorMode(int)
+         */
+        @ActivityInfo.ColorMode
+        public int getColorMode() {
+            return mColorMode;
+        }
+
         /** @hide */
         @SystemApi
         public final void setUserActivityTimeout(long timeout) {
@@ -2268,9 +2304,11 @@
             sb.append(',');
             sb.append(y);
             sb.append(")(");
-            sb.append((width== MATCH_PARENT ?"fill":(width==WRAP_CONTENT?"wrap":width)));
+            sb.append((width == MATCH_PARENT ? "fill" : (width == WRAP_CONTENT
+                    ? "wrap" : String.valueOf(width))));
             sb.append('x');
-            sb.append((height== MATCH_PARENT ?"fill":(height==WRAP_CONTENT?"wrap":height)));
+            sb.append((height == MATCH_PARENT ? "fill" : (height == WRAP_CONTENT
+                    ? "wrap" : String.valueOf(height))));
             sb.append(")");
             if (horizontalMargin != 0) {
                 sb.append(" hm=");
@@ -2367,6 +2405,7 @@
                 sb.append(" needsMenuKey=");
                 sb.append(needsMenuKey);
             }
+            sb.append(" colorMode=").append(mColorMode);
             sb.append('}');
             return sb.toString();
         }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 3748134..bc4ae6d 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -750,13 +750,14 @@
      * @param windowFlags Window layout flags.
      * @param overrideConfig override configuration to consider when generating
      *        context to for resources.
+     * @param displayId Id of the display to show the splash screen at.
      *
      * @return The starting surface.
      *
      */
     public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
             CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
-            int logo, int windowFlags, Configuration overrideConfig);
+            int logo, int windowFlags, Configuration overrideConfig, int displayId);
 
     /**
      * Prepare for a window being added to the window manager.  You can throw an
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 7f940f1..bfb8d83 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -21,6 +21,7 @@
 import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.NonNull;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
@@ -241,6 +242,8 @@
      * @hide
      */
     public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
+        // Constructor can't be chained because we can't create an instance of an inner class
+        // before calling another constructor.
         mHandler = new MyHandler(context.getMainLooper());
         mUserId = userId;
         synchronized (mLock) {
@@ -249,6 +252,23 @@
     }
 
     /**
+     * Create an instance.
+     *
+     * @param handler The handler to use
+     * @param service An interface to the backing service.
+     * @param userId User id under which to run.
+     *
+     * @hide
+     */
+    public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
+        mHandler = handler;
+        mUserId = userId;
+        synchronized (mLock) {
+            tryConnectToServiceLocked(service);
+        }
+    }
+
+    /**
      * @hide
      */
     public IAccessibilityManagerClient getClient() {
@@ -647,6 +667,30 @@
     }
 
     /**
+     * Find an installed service with the specified {@link ComponentName}.
+     *
+     * @param componentName The name to match to the service.
+     *
+     * @return The info corresponding to the installed service, or {@code null} if no such service
+     * is installed.
+     * @hide
+     */
+    public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName(
+            ComponentName componentName) {
+        final List<AccessibilityServiceInfo> installedServiceInfos =
+                getInstalledAccessibilityServiceList();
+        if ((installedServiceInfos == null) || (componentName == null)) {
+            return null;
+        }
+        for (int i = 0; i < installedServiceInfos.size(); i++) {
+            if (componentName.equals(installedServiceInfos.get(i).getComponentName())) {
+                return installedServiceInfos.get(i);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Adds an accessibility interaction connection interface for a given window.
      * @param windowToken The window token to which a connection is added.
      * @param connection The connection.
@@ -693,6 +737,26 @@
         }
     }
 
+    /**
+     * Perform the accessibility shortcut if the caller has permission.
+     *
+     * @hide
+     */
+    public void performAccessibilityShortcut() {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.performAccessibilityShortcut();
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
+        }
+    }
+
     private IAccessibilityManager getServiceLocked() {
         if (mService == null) {
             tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index f0bf7e5..56d45b0 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3998,8 +3998,10 @@
          * Obtains a pooled instance.
          *
          * @param type The type of the range.
-         * @param min The min value.
-         * @param max The max value.
+         * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
+         *            minimum.
+         * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no
+         *            maximum.
          * @param current The current value.
          */
         public static RangeInfo obtain(int type, float min, float max, float current) {
@@ -4019,8 +4021,10 @@
          * Creates a new range.
          *
          * @param type The type of the range.
-         * @param min The min value.
-         * @param max The max value.
+         * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
+         *            minimum.
+         * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no
+         *            maximum.
          * @param current The current value.
          */
         private RangeInfo(int type, float min, float max, float current) {
@@ -4044,18 +4048,18 @@
         }
 
         /**
-         * Gets the min value.
+         * Gets the minimum value.
          *
-         * @return The min value.
+         * @return The minimum value, or {@code Float.NEGATIVE_INFINITY} if no minimum exists.
          */
         public float getMin() {
             return mMin;
         }
 
         /**
-         * Gets the max value.
+         * Gets the maximum value.
          *
-         * @return The max value.
+         * @return The maximum value, or {@code Float.POSITIVE_INFINITY} if no maximum exists.
          */
         public float getMax() {
             return mMax;
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 2829744..ed77f68 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -60,7 +60,6 @@
 
     IBinder getWindowToken(int windowId, int userId);
 
-    void enableAccessibilityService(in ComponentName service, int userId);
-
-    void disableAccessibilityService(in ComponentName service, int userId);
+    // Requires WRITE_SECURE_SETTINGS
+    void performAccessibilityShortcut();
 }
diff --git a/core/java/android/view/animation/LayoutAnimationController.java b/core/java/android/view/animation/LayoutAnimationController.java
index df2f18c..7fa49c1 100644
--- a/core/java/android/view/animation/LayoutAnimationController.java
+++ b/core/java/android/view/animation/LayoutAnimationController.java
@@ -150,7 +150,7 @@
      * Returns the order used to compute the delay of each child's animation.
      *
      * @return one of {@link #ORDER_NORMAL}, {@link #ORDER_REVERSE} or
-     *         {@link #ORDER_RANDOM)
+     *         {@link #ORDER_RANDOM}
      *
      * @attr ref android.R.styleable#LayoutAnimation_animationOrder
      */
diff --git a/core/java/android/view/autofill/AutoFillId.java b/core/java/android/view/autofill/AutoFillId.java
index b7b694d..e9c1c3b 100644
--- a/core/java/android/view/autofill/AutoFillId.java
+++ b/core/java/android/view/autofill/AutoFillId.java
@@ -43,6 +43,13 @@
     }
 
     /** @hide */
+    public AutoFillId(int parentId, int virtualChildId) {
+        mVirtual = true;
+        mViewId = parentId;
+        mVirtualId = virtualChildId;
+    }
+
+    /** @hide */
     public int getViewId() {
         return mViewId;
     }
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
new file mode 100644
index 0000000..cd9842f
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.service.autofill.IAutoFillManagerService;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * App entry point to the AutoFill Framework.
+ */
+// TODO(b/33197203): improve this javadoc
+public final class AutoFillManager {
+
+    private static final String TAG = "AutoFillManager";
+    private static final boolean DEBUG = true; // TODO(b/33197203): change to false once stable
+
+    /**
+     * Flag used to show the auto-fill UI affordance for a view.
+     */
+    public static final int FLAG_UPDATE_UI_SHOW = 1 << 0;
+
+    /**
+     * Flag used to hide the auto-fill UI affordance for a view.
+     */
+    public static final int FLAG_UPDATE_UI_HIDE = 1 << 1;
+
+    private final IAutoFillManagerService mService;
+
+    /**
+     * @hide
+     */
+    public AutoFillManager(@SuppressWarnings("unused") Context context,
+            IAutoFillManagerService service) {
+        mService = service;
+    }
+
+    /**
+     * Updates the auto-fill bar for a given {@link View}.
+     *
+     * <b>Typically called twice, with different flags ({@link #FLAG_UPDATE_UI_SHOW} and
+     * {@link #FLAG_UPDATE_UI_HIDE} respectively), as the user "entered" and "exited" a view.
+     *
+     * @param view view to be updated.
+     * @param flags either {@link #FLAG_UPDATE_UI_SHOW} or
+     * {@link #FLAG_UPDATE_UI_HIDE}.
+     */
+    public void updateAutoFillInput(View view, int flags) {
+        if (DEBUG) {
+            Log.v(TAG, "updateAutoFillInput(" + view.getAutoFillViewId() + "): flags=" + flags);
+        }
+
+        updateAutoFillInput(view, false, View.NO_ID, null, flags);
+    }
+
+    /**
+     * Updates the auto-fill bar for a virtual child of a given {@link View}.
+     *
+     * <b>Typically called twice, with different flags ({@link #FLAG_UPDATE_UI_SHOW} and
+     * {@link #FLAG_UPDATE_UI_HIDE} respectively), as the user "entered" and "exited" a view.
+     *
+     * @param parent parent view.
+     * @param childId id identifying the virtual child inside the parent view.
+     * @param boundaries boundaries of the child (inside the parent; could be {@code null} when
+     * flag is {@link #FLAG_UPDATE_UI_HIDE}.
+     * @param flags either {@link #FLAG_UPDATE_UI_SHOW} or
+     * {@link #FLAG_UPDATE_UI_HIDE}.
+     */
+    public void updateAutoFillInput(View parent, int childId, @Nullable Rect boundaries,
+            int flags) {
+        if (DEBUG) {
+            Log.v(TAG, "updateAutoFillInput(" + parent.getAutoFillViewId() + ", " + childId
+                    + "): boundaries=" + boundaries + ", flags=" + flags);
+        }
+        updateAutoFillInput(parent, true, childId, boundaries, flags);
+    }
+
+    private void updateAutoFillInput(View view, boolean virtual, int childId, Rect boundaries,
+            int flags) {
+        if ((flags & FLAG_UPDATE_UI_SHOW) != 0) {
+            final int viewId = view.getAutoFillViewId();
+            final AutoFillId id = virtual
+                    ? new AutoFillId(viewId, childId)
+                    : new AutoFillId(viewId);
+            showAutoFillInput(id, boundaries);
+            return;
+        }
+        // TODO(b/33197203): handle FLAG_UPDATE_UI_HIDE
+    }
+
+    private void showAutoFillInput(AutoFillId id, Rect boundaries) {
+        final int autoFillViewId = id.getViewId();
+        /*
+         * TODO(b/33197203): currently SHOW_AUTO_FILL_BAR is only set once per activity (i.e, when
+         * the view does not have an auto-fill id), but it should be called again for views that
+         * were not part of the initial auto-fill dataset returned by the service. For example:
+         *
+         * 1.Activity has 4 fields, `first_name`, `last_name`, and `address`.
+         * 2.User taps `first_name`.
+         * 3.Service returns a dataset with ids for `first_name` and `last_name`.
+         * 4.When user taps `first_name` (again) or `last_name`, flag should not have
+         *   SHOW_AUTO_FILL_BAR set, but when user taps `address`, it should (since that field was
+         *   not part of the initial dataset).
+         *
+         * Similarly, once the activity is auto-filled, the flag logic should be reset (so if the
+         * user taps the view again, a new auto-fill request is made)
+         */
+        if (autoFillViewId != View.NO_ID) {
+            return;
+        }
+
+        try {
+            mService.showAutoFillInput(id, boundaries);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/view/autofill/Dataset.java
index a73eb774..b11eecc 100644
--- a/core/java/android/view/autofill/Dataset.java
+++ b/core/java/android/view/autofill/Dataset.java
@@ -19,12 +19,13 @@
 import static android.view.autofill.Helper.DEBUG;
 import static android.view.autofill.Helper.append;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.assist.AssistStructure.ViewNode;
+import android.hardware.fingerprint.FingerprintManager.CryptoObject;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.service.autofill.AutoFillService;
 
 import com.android.internal.util.Preconditions;
 
@@ -50,12 +51,20 @@
     private final CharSequence mName;
     private final ArrayList<DatasetField> mFields;
     private final Bundle mExtras;
+    private final int mFlags;
+    private final boolean mRequiresAuth;
+    private final boolean mHasCryptoObject;
+    private final long mCryptoOpId;
 
     private Dataset(Dataset.Builder builder) {
         mName = builder.mName;
         // TODO(b/33197203): make an immutable copy of mFields?
         mFields = builder.mFields;
         mExtras = builder.mExtras;
+        mFlags = builder.mFlags;
+        mRequiresAuth = builder.mRequiresAuth;
+        mHasCryptoObject = builder.mHasCryptoObject;
+        mCryptoOpId = builder.mCryptoOpId;
     }
 
     /** @hide */
@@ -73,13 +82,41 @@
         return mExtras;
     }
 
+    /** @hide */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /** @hide */
+    public boolean isAuthRequired() {
+        return mRequiresAuth;
+    }
+
+    /** @hide */
+    public boolean isEmpty() {
+        return mFields.isEmpty();
+    }
+
+    /** @hide */
+    public boolean hasCryptoObject() {
+        return mHasCryptoObject;
+    }
+
+    /** @hide */
+    public long getCryptoObjectOpId() {
+        return mCryptoOpId;
+    }
+
     @Override
     public String toString() {
         if (!DEBUG) return super.toString();
 
         final StringBuilder builder = new StringBuilder("Dataset [name=").append(mName)
                 .append(", fields=").append(mFields).append(", extras=");
-        append(builder, mExtras);
+        append(builder, mExtras)
+            .append(", flags=").append(mFlags)
+            .append(", requiresAuth: ").append(mRequiresAuth)
+            .append(", hasCrypto: ").append(mHasCryptoObject);
         return builder.append(']').toString();
     }
 
@@ -90,6 +127,10 @@
         private CharSequence mName;
         private final ArrayList<DatasetField> mFields = new ArrayList<>();
         private Bundle mExtras;
+        private int mFlags;
+        private boolean mRequiresAuth;
+        private boolean mHasCryptoObject;
+        private long mCryptoOpId;
 
         /**
          * Creates a new builder.
@@ -103,6 +144,125 @@
         }
 
         /**
+         * Requires dataset authentication through the {@link
+         * android.service.autofill.AutoFillService} before auto-filling the activity with this
+         * dataset.
+         *
+         * <p>This method is typically called when the device (or the service) does not support
+         * fingerprint authentication (and hence it cannot use {@link
+         * #requiresFingerprintAuthentication(CryptoObject, Bundle, int)}) or when the service needs
+         * to use a custom authentication UI for the dataset. For example, when a dataset contains
+         * credit card information (such as number, expiration date, and verification code), the
+         * service displays an authentication dialog asking for the verification code to unlock the
+         * rest of the data).
+         *
+         * <p>Since the dataset is "locked" until the user authenticates it, typically this dataset
+         * name is masked (for example, "VISA....1234").
+         *
+         * <p>When the user selects this dataset, the Android System calls {@link
+         * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
+         * passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_REQUESTED} in
+         * the flags and the same {@code extras} passed to this method. The service can then
+         * displays its custom authentication UI, and then call the proper method on {@link
+         * android.service.autofill.FillCallback} depending on the authentication result and whether
+         * this dataset already contains the fields needed to auto-fill the activity:
+         *
+         * <ul>
+         *   <li>If authentication failed, call
+         *   {@link android.service.autofill.FillCallback#onDatasetAuthentication(Dataset,
+         *       int)} passing {@link
+         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the flags.
+         *   <li>If authentication succeeded and this datast is empty (no fields), call {@link
+         *       android.service.autofill.FillCallback#onSuccess(FillResponse)} with a new dataset
+         *       (with the proper fields).
+         *   <li>If authentication succeeded and this response is not empty, call {@link
+         *       android.service.autofill.FillCallback#onDatasetAuthentication(Dataset, int)}
+         *       passing
+         *       {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS} in the
+         *       {@code flags} and {@code null} as the {@code dataset}.
+         * </ul>
+         *
+         * @param extras when set, will be passed back in the {@link
+         *     android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle,
+         *     int)}, call so it could be used by the service to handle state.
+         * @param flags optional parameters, currently ignored.
+         */
+        public Builder requiresCustomAuthentication(@Nullable Bundle extras, int flags) {
+            return requiresAuthentication(null, extras, flags);
+        }
+
+        /**
+         * Requires dataset authentication through the Fingerprint sensor before auto-filling the
+         * activity with this dataset.
+         *
+         * <p>This method is typically called when the dataset contains sensitive information (for
+         * example, credit card information) and the provider requires the user to re-authenticate
+         * before using it.
+         *
+         * <p>Since the dataset is "locked" until the user authenticates it, typically this dataset
+         * name is masked (for example, "VISA....1234").
+         *
+         * <p>When the user selects this dataset, the Android System displays an UI affordance
+         * asking the user to use the fingerprint sensor unlock the dataset, and what happens after
+         * a successful fingerprint authentication depends on whether the dataset is empty (no
+         * fields, only the masked name) or not:
+         *
+         * <ul>
+         *   <li>If it's empty, the Android System will call {@link
+         *       android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle,
+         *       int)} passing {@link
+         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}} in the
+         *       flags.
+         *   <li>If it's not empty, the activity will be auto-filled with its data.
+         * </ul>
+         *
+         * <p>If the fingerprint authentication fails, the Android System will call {@link
+         * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
+         * passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the
+         * flags.
+         *
+         * <p><strong>NOTE: </note> the {@link android.service.autofill.AutoFillService} should use
+         * the {@link android.hardware.fingerprint.FingerprintManager} to check if fingerpint
+         * authentication is available before using this method, and use other alternatives (such as
+         * {@link #requiresCustomAuthentication(Bundle, int)}) if it is not: if this method is
+         * called when fingerprint is not available, Android System will call {@link
+         * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
+         * passing {@link
+         * android.service.autofill.AutoFillService#FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}
+         * in the flags, but it would be wasting system resources (and worsening the user
+         * experience) in the process.
+         *
+         * @param crypto object that will be authenticated.
+         * @param extras when set, will be passed back in the {@link
+         *     android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
+         *     call so it could be used by the service to handle state.
+         * @param flags optional parameters, currently ignored.
+         */
+        public Builder requiresFingerprintAuthentication(CryptoObject crypto,
+                @Nullable Bundle extras, int flags) {
+            // TODO(b/33197203): should we allow crypto to be null?
+            Preconditions.checkArgument(crypto != null, "must pass a CryptoObject");
+            return requiresAuthentication(crypto, extras, flags);
+        }
+
+        private Builder requiresAuthentication(CryptoObject cryptoObject, Bundle extras,
+                int flags) {
+            // There can be only one!
+            Preconditions.checkState(!mRequiresAuth,
+                    "requires-authentication methods already called");
+            // TODO(b/33197203): make sure that either this method or setExtras() is called, but
+            // not both
+            mExtras = extras;
+            mFlags = flags;
+            mRequiresAuth = true;
+            if (cryptoObject != null) {
+                mHasCryptoObject = true;
+                mCryptoOpId = cryptoObject.getOpId();
+            }
+            return this;
+        }
+
+        /**
          * Sets the value of a field.
          *
          * @param id id returned by {@link ViewNode#getAutoFillId()}.
@@ -121,15 +281,17 @@
         }
 
         /**
-         * Sets a {@link Bundle} that will be passed to subsequent calls to {@link AutoFillService}
-         * methods such as
-         * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
-         * android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using
-         * {@link AutoFillService#EXTRA_DATASET_EXTRAS} as the key.
+         * Sets a {@link Bundle} that will be passed to subsequent calls to
+         * {@link android.service.autofill.AutoFillService} methods such as
+ * {@link android.service.autofill.AutoFillService#onSaveRequest(android.app.assist.AssistStructure,
+         * Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using
+         * {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} as the key.
          *
          * <p>It can be used to keep service state in between calls.
          */
         public Builder setExtras(Bundle extras) {
+            // TODO(b/33197203): make sure that either this method or the requires-Authentication
+            // ones are called, but not both
             mExtras = Objects.requireNonNull(extras, "extras cannot be null");
             return this;
         }
@@ -158,6 +320,12 @@
         parcel.writeCharSequence(mName);
         parcel.writeList(mFields);
         parcel.writeBundle(mExtras);
+        parcel.writeInt(mFlags);
+        parcel.writeInt(mRequiresAuth ? 1 : 0);
+        parcel.writeInt(mHasCryptoObject ? 1 : 0);
+        if (mHasCryptoObject) {
+            parcel.writeLong(mCryptoOpId);
+        }
     }
 
     @SuppressWarnings("unchecked")
@@ -165,6 +333,10 @@
         mName = parcel.readCharSequence();
         mFields = parcel.readArrayList(null);
         mExtras = parcel.readBundle();
+        mFlags = parcel.readInt();
+        mRequiresAuth = parcel.readInt() == 1;
+        mHasCryptoObject = parcel.readInt() == 1;
+        mCryptoOpId = mHasCryptoObject ? parcel.readLong() : 0;
     }
 
     public static final Parcelable.Creator<Dataset> CREATOR = new Parcelable.Creator<Dataset>() {
diff --git a/core/java/android/view/autofill/FillResponse.java b/core/java/android/view/autofill/FillResponse.java
index 3a14767..67eb85a 100644
--- a/core/java/android/view/autofill/FillResponse.java
+++ b/core/java/android/view/autofill/FillResponse.java
@@ -18,11 +18,13 @@
 import static android.view.autofill.Helper.DEBUG;
 import static android.view.autofill.Helper.append;
 
+import android.annotation.Nullable;
 import android.app.Activity;
+import android.hardware.fingerprint.FingerprintManager.CryptoObject;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.service.autofill.AutoFillService;
+import android.service.autofill.FillCallback;
 
 import com.android.internal.util.Preconditions;
 
@@ -34,14 +36,16 @@
 import java.util.Set;
 
 /**
- * Response for a
- * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
- * android.os.CancellationSignal, android.service.autofill.FillCallback)}
- * request.
+ * Response for a {@link
+ * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
+ * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} request.
  *
  * <p>The response typically contains one or more {@link Dataset}s, each representing a set of
- * fields that can be auto-filled together. For example, for a login page with username/password
- * where the user only have one account in the service, the response could be:
+ * fields that can be auto-filled together, and the Android System displays a dataset picker UI
+ * affordance that the user must use before the {@link Activity} is filled with the dataset.
+ *
+ * <p>For example, for a login page with username/password where the user only has one account in
+ * the service, the response could be:
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
@@ -67,9 +71,9 @@
  *      .build();
  * </pre>
  *
- * <p>If the user does not have any data associated with this {@link Activity} but the service
- * wants to offer the user the option to save the data that was entered, then the service could
- * populate the response with {@code savableIds} instead of {@link Dataset}s:
+ * <p>If the user does not have any data associated with this {@link Activity} but the service wants
+ * to offer the user the option to save the data that was entered, then the service could populate
+ * the response with {@code savableIds} instead of {@link Dataset}s:
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
@@ -79,8 +83,8 @@
  *
  * <p>Similarly, there might be cases where the user data on the service is enough to populate some
  * fields but not all, and the service would still be interested on saving the other fields. In this
- * scenario, the service could populate the response with both {@link Dataset}s and
- * {@code savableIds}:
+ * scenario, the service could populate the response with both {@link Dataset}s and {@code
+ * savableIds}:
  *
  * <pre class="prettyprint">
  *   new FillResponse.Builder()
@@ -101,9 +105,9 @@
  * <p>If the service has multiple {@link Dataset}s with multiple options for some fields on each
  * dataset (for example, multiple accounts with both a home and work address), then it should
  * "partition" the {@link Activity} in sections and populate the response with just a subset of the
- * data that would fulfill the first section; then once the user fills the first section and taps
- * a field from the next section, the Android system would issue another request for that section,
- * and so on. For example, the first response could be:
+ * data that would fulfill the first section; then once the user fills the first section and taps a
+ * field from the next section, the Android system would issue another request for that section, and
+ * so on. For example, the first response could be:
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
@@ -134,19 +138,31 @@
  *      .build();
  * </pre>
  *
- * <p>Finally, the service can use the {@link FillResponse.Builder#setExtras(Bundle)} and/or
- * {@link Dataset.Builder#setExtras(Bundle)} methods to pass
- * a {@link Bundle} with service-specific data use to identify this response on future calls (like
- * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
- * android.os.CancellationSignal, android.service.autofill.SaveCallback)}) - such bundle will be
- * available as the {@link AutoFillService#EXTRA_RESPONSE_EXTRAS} extra in
- * that method's {@code extras} argument.
+ * <p>The service could require user authentication, either at the {@link FillResponse} or {@link
+ * Dataset} levels, prior to auto-filling the activity - see {@link
+ * FillResponse.Builder#requiresFingerprintAuthentication(CryptoObject, Bundle, int)}, {@link
+ * FillResponse.Builder#requiresCustomAuthentication(Bundle, int)}, {@link
+ * Dataset.Builder#requiresFingerprintAuthentication(CryptoObject, Bundle, int)}, and {@link
+ * Dataset.Builder#requiresCustomAuthentication(Bundle, int)} for details.
+ *
+ * <p>Finally, the service can use the {@link FillResponse.Builder#setExtras(Bundle)} and/or {@link
+ * Dataset.Builder#setExtras(Bundle)} methods to pass {@link Bundle}s with service-specific data use
+ * to identify this response on future calls (like {@link
+ * android.service.autofill.AutoFillService#onSaveRequest(android.app.assist.AssistStructure,
+ * Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback)}) - such bundles
+ * will be available as the {@link android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS}
+ * and {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} extras in that method's
+ * {@code extras} argument.
  */
 public final class FillResponse implements Parcelable {
 
     private final List<Dataset> mDatasets;
     private final AutoFillId[] mSavableIds;
     private final Bundle mExtras;
+    private final int mFlags;
+    private final boolean mRequiresAuth;
+    private final boolean mHasCryptoObject;
+    private final long mCryptoOpId;
 
     private FillResponse(Builder builder) {
         // TODO(b/33197203): make it immutable?
@@ -158,6 +174,10 @@
             mSavableIds[i++] = id;
         }
         mExtras = builder.mExtras;
+        mFlags = builder.mFlags;
+        mRequiresAuth = builder.mRequiresAuth;
+        mHasCryptoObject = builder.mHasCryptoObject;
+        mCryptoOpId = builder.mCryptoOpId;
     }
 
     /** @hide */
@@ -175,13 +195,161 @@
         return mExtras;
     }
 
+    /** @hide */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /** @hide */
+    public boolean isAuthRequired() {
+        return mRequiresAuth;
+    }
+
+    /** @hide */
+    public boolean hasCryptoObject() {
+        return mHasCryptoObject;
+    }
+
+    /** @hide */
+    public long getCryptoObjectOpId() {
+        return mCryptoOpId;
+    }
+
     /**
      * Builder for {@link FillResponse} objects.
      */
     public static final class Builder {
         private final List<Dataset> mDatasets = new ArrayList<>();
         private final Set<AutoFillId> mSavableIds = new HashSet<>();
-        private  Bundle mExtras;
+        private Bundle mExtras;
+        private int mFlags;
+        private boolean mRequiresAuth;
+        private boolean mHasCryptoObject;
+        private long mCryptoOpId;
+
+        /**
+         * Requires user authentication through the {@link android.service.autofill.AutoFillService}
+         * before handling an auto-fill request.
+         *
+         * <p>This method is typically called when the device (or the service) does not support
+         * fingerprint authentication (and hence it cannot use {@link
+         * #requiresFingerprintAuthentication(CryptoObject, Bundle, int)}) or when the service needs
+         * to use a custom authentication UI and is used in 2 scenarios:
+         *
+         * <ol>
+         *   <li>When the user data is encrypted and the service must authenticate an object that
+         *       will be used to decrypt it.
+         *   <li>When the service already acquired the user data but wants to confirm the user's
+         *       identity before the activity is filled with it.
+         * </ol>
+         *
+         * <p>When this method is called, the Android System displays an UI affordance asking the
+         * user to tap it to auto-fill the activity; if the user taps it, the Android System calls
+         * {@link
+         * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
+         * int)} passing {@link
+         * android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_REQUESTED} in the flags and
+         * the same {@code extras} passed to this method. The service can then displays its custom
+         * authentication UI, and then call the proper method on {@link FillCallback} depending on
+         * the authentication result and whether this response already contains the {@link Dataset}s
+         * need to auto-fill the activity:
+         *
+         * <ul>
+         *   <li>If authentication failed, call {@link
+         *       FillCallback#onFillResponseAuthentication(int)} passing {@link
+         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the flags.
+         *   <li>If authentication succeeded and this response is empty (no datasets), call {@link
+         *       FillCallback#onSuccess(FillResponse)} with a new dataset (that does not require
+         *       authentication).
+         *   <li>If authentication succeeded and this response is not empty, call {@link
+         *       FillCallback#onFillResponseAuthentication(int)} passing {@link
+         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS} in the flags.
+         * </ul>
+         *
+         * @param extras when set, will be passed back in the {@link
+         *     android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
+         *     int)} call so it could be used by the service to handle state.
+         * @param flags optional parameters, currently ignored.
+         */
+        public Builder requiresCustomAuthentication(@Nullable Bundle extras, int flags) {
+            return requiresAuthentication(null, extras, flags);
+        }
+
+        /**
+         * Requires user authentication through the Fingerprint sensor before handling an auto-fill
+         * request.
+         *
+         * <p>The {@link android.service.autofill.AutoFillService} typically uses this method in 2
+         * situations:
+         *
+         * <ol>
+         *   <li>When the user data is encrypted and the service must authenticate an object that
+         *       will be used to decrypt it.
+         *   <li>When the service already acquired the user data but wants to confirm the user's
+         *       identity before the activity is filled with it.
+         * </ol>
+         *
+         * <p>When this method is called, the Android System displays an UI affordance asking the
+         * user to use the fingerprint sensor to auto-fill the activity, and what happens after a
+         * successful fingerprint authentication depends on the number of {@link Dataset}s included
+         * in this response:
+         *
+         * <ul>
+         *   <li>If it's empty (scenario #1 above), the Android System will call {@link
+         *     android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
+         *       int)} passing {@link
+         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}} in the
+         *       flags.
+         *   <li>If it contains one dataset, the activity will be auto-filled right away.
+         *   <li>If it contains many datasets, the Android System will show dataset picker UI, and
+         *       then auto-fill the activity once the user select the proper datased.
+         * </ul>
+         *
+         * <p>If the fingerprint authentication fails, the Android System will call {@link
+         * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
+         * int)} passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR}
+         * in the flags.
+         *
+         * <p><strong>NOTE: </note> the {@link android.service.autofill.AutoFillService} should use
+         * the {@link android.hardware.fingerprint.FingerprintManager} to check if fingerpint
+         * authentication is available before using this method, and use other alternatives (such as
+         * {@link #requiresCustomAuthentication(Bundle, int)}) if it is not: if this method is
+         * called when fingerprint is not available, Android System will call {@link
+         * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
+         * int)} passing {@link
+         * android.service.autofill.AutoFillService#FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}
+         * in the flags, but it would be wasting system resources (and worsening the user
+         * experience) in the process.
+         *
+         * @param crypto object that will be authenticated.
+         * @param extras when set, will be passed back in the {@link
+         *     android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
+         *     int)} call so it could be used by the service to handle state.
+         * @param flags optional parameters, currently ignored.
+         */
+        public Builder requiresFingerprintAuthentication(CryptoObject crypto,
+                @Nullable Bundle extras, int flags) {
+            // TODO(b/33197203): should we allow crypto to be null?
+            Preconditions.checkArgument(crypto != null, "must pass a CryptoObject");
+            return requiresAuthentication(crypto, extras, flags);
+        }
+
+        private Builder requiresAuthentication(CryptoObject cryptoObject, Bundle extras,
+                int flags) {
+            // There can be only one!
+            Preconditions.checkState(!mRequiresAuth,
+                    "requires-authentication methods already called");
+            // TODO(b/33197203): make sure that either this method or setExtras() is called, but
+            // not both
+            mExtras = extras;
+            mFlags = flags;
+            mRequiresAuth = true;
+            if (cryptoObject != null) {
+                mHasCryptoObject = true;
+                mCryptoOpId = cryptoObject.getOpId();
+            }
+            return this;
+        }
 
         /**
          * Adds a new {@link Dataset} to this response.
@@ -191,7 +359,6 @@
         public Builder addDataset(Dataset dataset) {
             Preconditions.checkNotNull(dataset, "dataset cannot be null");
             // TODO(b/33197203): check if name already exists
-            // TODO(b/33197203): check if authId already exists (and update javadoc)
             mDatasets.add(dataset);
             for (DatasetField field : dataset.getFields()) {
                 mSavableIds.add(field.getId());
@@ -201,13 +368,14 @@
 
         /**
          * Adds ids of additional fields that the service would be interested to save (through
-         * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
-         * android.os.CancellationSignal, android.service.autofill.SaveCallback)}) but were not
-         * indirectly set through {@link #addDataset(Dataset)}.
+         * {@link android.service.autofill.AutoFillService#onSaveRequest(
+         * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
+         * android.service.autofill.SaveCallback)}) but were not indirectly set through {@link
+         * #addDataset(Dataset)}.
          *
          * <p>See {@link FillResponse} for examples.
          */
-        public Builder addSavableFields(AutoFillId...ids) {
+        public Builder addSavableFields(AutoFillId... ids) {
             for (AutoFillId id : ids) {
                 mSavableIds.add(id);
             }
@@ -215,15 +383,18 @@
         }
 
         /**
-         * Sets a {@link Bundle} that will be passed to subsequent calls to {@link AutoFillService}
-         * methods such as
-         * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
-         * android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using
-         * {@link AutoFillService#EXTRA_RESPONSE_EXTRAS} as the key.
+         * Sets a {@link Bundle} that will be passed to subsequent calls to {@link
+         * android.service.autofill.AutoFillService} methods such as {@link
+         * android.service.autofill.AutoFillService#onSaveRequest(
+         * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
+         * android.service.autofill.SaveCallback)}, using {@link
+         * android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS} as the key.
          *
          * <p>It can be used when to keep service state in between calls.
          */
         public Builder setExtras(Bundle extras) {
+            // TODO(b/33197203): make sure that either this method or the requires-Authentication
+            // ones are called, but not both
             mExtras = Objects.requireNonNull(extras, "extras cannot be null");
             return this;
         }
@@ -246,7 +417,10 @@
         final StringBuilder builder = new StringBuilder("FillResponse: [datasets=")
                 .append(mDatasets).append(", savableIds=").append(Arrays.toString(mSavableIds))
                 .append(", extras=");
-        append(builder, mExtras);
+        append(builder, mExtras)
+            .append(", flags=").append(mFlags)
+            .append(", requiresAuth: ").append(mRequiresAuth)
+            .append(", hasCrypto: ").append(mHasCryptoObject);
         return builder.append(']').toString();
     }
 
@@ -264,6 +438,12 @@
         parcel.writeList(mDatasets);
         parcel.writeParcelableArray(mSavableIds, 0);
         parcel.writeBundle(mExtras);
+        parcel.writeInt(mFlags);
+        parcel.writeInt(mRequiresAuth ? 1 : 0);
+        parcel.writeInt(mHasCryptoObject ? 1 : 0);
+        if (mHasCryptoObject) {
+            parcel.writeLong(mCryptoOpId);
+        }
     }
 
     private FillResponse(Parcel parcel) {
@@ -271,6 +451,10 @@
         parcel.readList(mDatasets, null);
         mSavableIds = parcel.readParcelableArray(null, AutoFillId.class);
         mExtras = parcel.readBundle();
+        mFlags = parcel.readInt();
+        mRequiresAuth = parcel.readInt() == 1;
+        mHasCryptoObject = parcel.readInt() == 1;
+        mCryptoOpId = mHasCryptoObject ? parcel.readLong() : 0;
     }
 
     public static final Parcelable.Creator<FillResponse> CREATOR =
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
index 772710e..14cf9e8 100644
--- a/core/java/android/view/autofill/Helper.java
+++ b/core/java/android/view/autofill/Helper.java
@@ -18,28 +18,33 @@
 
 import android.os.Bundle;
 
+import java.util.Arrays;
+import java.util.Objects;
 import java.util.Set;
 
 /** @hide */
 public final class Helper {
 
-    // TODO(b/33197203): set to false when stable
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = true; // TODO(b/33197203): set to false when stable
     static final String REDACTED = "[REDACTED]";
 
-    static void append(StringBuilder builder, Bundle bundle) {
+    static StringBuilder append(StringBuilder builder, Bundle bundle) {
         if (bundle == null) {
             builder.append("N/A");
         } else if (!DEBUG) {
             builder.append(REDACTED);
         } else {
             final Set<String> keySet = bundle.keySet();
-            builder.append("[bundle with ").append(keySet.size()).append(" extras:");
+            builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
             for (String key : keySet) {
-                builder.append(' ').append(key).append('=').append(bundle.get(key)).append(',');
+                final Object value = bundle.get(key);
+                builder.append(' ').append(key).append('=');
+                builder.append((value instanceof Object[])
+                        ? Arrays.toString((Objects[]) value) : value);
             }
             builder.append(']');
         }
+        return builder;
     }
 
     private Helper() {
diff --git a/core/java/android/view/autofill/VirtualViewDelegate.java b/core/java/android/view/autofill/VirtualViewDelegate.java
index a19b4e5..278bf4f 100644
--- a/core/java/android/view/autofill/VirtualViewDelegate.java
+++ b/core/java/android/view/autofill/VirtualViewDelegate.java
@@ -15,6 +15,8 @@
  */
 package android.view.autofill;
 
+import android.annotation.Nullable;
+import android.graphics.Rect;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewStructure;
@@ -71,20 +73,18 @@
     public abstract static class Callback {
 
         /**
-         * Sent when the focus inside the hierarchy changed.
+         * Sent when the auto-fill bar for a child must be updated.
          *
-         * <p>Typically callled twice - for the nodes that lost and gained focus.
-         *
-         * <p>This method should only be called when the change was not caused by the AutoFill
-         * Framework itselft (i.e, through {@link VirtualViewDelegate#autoFill(int, AutoFillValue)},
-         * but by external causes (for example, when the user changed the value through the view's
-         * UI).
-         *
-         * @param virtualId id of the node whose focus changed.
-         * @param hasFocus {@code true} when focus was gained, {@code false} when it was lost.
+         * See {@link AutoFillManager#updateAutoFillInput(View, int, android.graphics.Rect, int)}
+         * for more details.
          */
-        public void onFocusChanged(int virtualId, boolean hasFocus) {
-            if (DEBUG) Log.d(TAG, "onFocusChanged() for " + virtualId + ": " + hasFocus);
+        // TODO(b/33197203): do we really need it, or should the parent view just call
+        // AutoFillManager.updateAutoFillInput() directly?
+        public void onAutoFillInputUpdated(int virtualId, @Nullable Rect boundaries, int flags) {
+            if (DEBUG) {
+                Log.v(TAG, "onAutoFillInputUpdated(): virtualId=" + virtualId + ", boundaries="
+                        + boundaries + ", flags=" + flags);
+            }
         }
 
         /**
diff --git a/core/java/android/view/textclassifier/EntityConfidence.java b/core/java/android/view/textclassifier/EntityConfidence.java
new file mode 100644
index 0000000..7aab71f
--- /dev/null
+++ b/core/java/android/view/textclassifier/EntityConfidence.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper object for setting and getting entity scores for classified text.
+ *
+ * @param <T> the entity type.
+ * @hide
+ */
+final class EntityConfidence<T> {
+
+    private final Map<T, Float> mEntityConfidence = new HashMap<>();
+
+    private final Comparator<T> mEntityComparator = (e1, e2) -> {
+        float score1 = mEntityConfidence.get(e1);
+        float score2 = mEntityConfidence.get(e2);
+        if (score1 > score2) {
+            return 1;
+        }
+        if (score1 < score2) {
+            return -1;
+        }
+        return 0;
+    };
+
+    EntityConfidence() {}
+
+    EntityConfidence(@NonNull EntityConfidence<T> source) {
+        Preconditions.checkNotNull(source);
+        mEntityConfidence.putAll(source.mEntityConfidence);
+    }
+
+    /**
+     * Sets an entity type for the classified text and assigns a confidence score.
+     *
+     * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
+     *      0 implies the entity does not exist for the classified text.
+     *      Values greater than 1 are clamped to 1.
+     */
+    public void setEntityType(
+            @NonNull T type, @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
+        Preconditions.checkNotNull(type);
+        if (confidenceScore > 0) {
+            mEntityConfidence.put(type, Math.min(1, confidenceScore));
+        } else {
+            mEntityConfidence.remove(type);
+        }
+    }
+
+    /**
+     * Returns an immutable list of entities found in the classified text ordered from
+     * high confidence to low confidence.
+     */
+    @NonNull
+    public List<T> getEntities() {
+        List<T> entities = new ArrayList<>(mEntityConfidence.size());
+        entities.addAll(mEntityConfidence.keySet());
+        entities.sort(mEntityComparator);
+        return Collections.unmodifiableList(entities);
+    }
+
+    /**
+     * Returns the confidence score for the specified entity. The value ranges from
+     * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the
+     * classified text.
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public float getConfidenceScore(T entity) {
+        if (mEntityConfidence.containsKey(entity)) {
+            return mEntityConfidence.get(entity);
+        }
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return mEntityConfidence.toString();
+    }
+}
diff --git a/core/java/android/view/textclassifier/LinksInfo.java b/core/java/android/view/textclassifier/LinksInfo.java
new file mode 100644
index 0000000..3acbdc0
--- /dev/null
+++ b/core/java/android/view/textclassifier/LinksInfo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+
+/**
+ * Link information that can be applied to text. See: {@link #apply(CharSequence)}.
+ * Typical implementations of this interface will annotate spannable text with e.g
+ * {@link android.text.style.ClickableSpan}s or other annotations.
+ */
+public interface LinksInfo {
+
+    /**
+     * @hide
+     */
+    LinksInfo NO_OP = text -> false;
+
+    /**
+     * Applies link annotations to the specified text.
+     * These annotations are not guaranteed to be applied. For example, the annotations may not be
+     * applied if the text has changed from what it was when the link spec was generated for it.
+     *
+     * @return Whether or not the link annotations were successfully applied.
+     */
+    boolean apply(@NonNull CharSequence text);
+}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
new file mode 100644
index 0000000..4673c50
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.text.LangId;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Interface to the text classification service.
+ *
+ * <p>You do not instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService}.
+ */
+public final class TextClassificationManager {
+
+    private static final String LOG_TAG = "TextClassificationManager";
+
+    private final Context mContext;
+    // TODO: Implement a way to close the file descriptor.
+    private ParcelFileDescriptor mFd;
+    private TextClassifier mDefault;
+    private LangId mLangId;
+
+    /** @hide */
+    public TextClassificationManager(Context context) {
+        mContext = Preconditions.checkNotNull(context);
+    }
+
+    /**
+     * Returns the default text classifier.
+     */
+    public TextClassifier getDefaultTextClassifier() {
+        if (mDefault == null) {
+            try {
+                mFd = ParcelFileDescriptor.open(
+                        new File("/etc/assistant/smart-selection.model"),
+                        ParcelFileDescriptor.MODE_READ_ONLY);
+                mDefault = new TextClassifierImpl(mContext, mFd);
+            } catch (FileNotFoundException e) {
+                Log.e(LOG_TAG, "Error accessing 'text classifier selection' model file.", e);
+                mDefault = TextClassifier.NO_OP;
+            }
+        }
+        return mDefault;
+    }
+
+    /**
+     * Returns information containing languages that were detected in the provided text.
+     * This is a blocking operation you should avoid calling it on the UI thread.
+     *
+     * @throws IllegalArgumentException if text is null
+     */
+    public List<TextLanguage> detectLanguages(@NonNull CharSequence text) {
+        Preconditions.checkArgument(text != null);
+        try {
+            if (text.length() > 0) {
+                final String language = getLanguageDetector().findLanguage(text.toString());
+                final Locale locale = new Locale.Builder().setLanguageTag(language).build();
+                return Collections.unmodifiableList(Arrays.asList(
+                        new TextLanguage.Builder(0, text.length())
+                                .setLanguage(locale, 1.0f /* confidence */)
+                                .build()));
+            }
+        } catch (Throwable t) {
+            // Avoid throwing from this method. Log the error.
+            Log.e(LOG_TAG, "Error detecting languages for text. Returning empty result.", t);
+        }
+        // Getting here means something went wrong. Return an empty result.
+        return Collections.emptyList();
+    }
+
+    private LangId getLanguageDetector() {
+        if (mLangId == null) {
+            // TODO: Use a file descriptor as soon as we start to depend on a model file
+            // for language detection.
+            mLangId = new LangId(0);
+        }
+        return mLangId;
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassificationResult.java b/core/java/android/view/textclassifier/TextClassificationResult.java
new file mode 100644
index 0000000..6af0efb
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassificationResult.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.View.OnClickListener;
+import android.view.textclassifier.TextClassifier.EntityType;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Information for generating a widget to handle classified text.
+ */
+public final class TextClassificationResult {
+
+    /**
+     * @hide
+     */
+    static final TextClassificationResult EMPTY = new TextClassificationResult.Builder().build();
+
+    @NonNull private final String mText;
+    @Nullable private final Drawable mIcon;
+    @Nullable private final String mLabel;
+    @Nullable private final Intent mIntent;
+    @Nullable private final OnClickListener mOnClickListener;
+    @NonNull private final EntityConfidence<String> mEntityConfidence;
+    @NonNull private final List<String> mEntities;
+
+    private TextClassificationResult(
+            @NonNull String text,
+            Drawable icon,
+            String label,
+            Intent intent,
+            OnClickListener onClickListener,
+            @NonNull EntityConfidence<String> entityConfidence) {
+        mText = text;
+        mIcon = icon;
+        mLabel = label;
+        mIntent = intent;
+        mOnClickListener = onClickListener;
+        mEntityConfidence = new EntityConfidence<>(entityConfidence);
+        mEntities = mEntityConfidence.getEntities();
+    }
+
+    /**
+     * Gets the classified text.
+     */
+    @NonNull
+    public String getText() {
+        return mText;
+    }
+
+    /**
+     * Returns the number of entities found in the classified text.
+     */
+    @IntRange(from = 0)
+    public int getEntityCount() {
+        return mEntities.size();
+    }
+
+    /**
+     * Returns the entity at the specified index. Entities are ordered from high confidence
+     * to low confidence.
+     *
+     * @throws IndexOutOfBoundsException if the specified index is out of range.
+     * @see #getEntityCount() for the number of entities available.
+     */
+    @NonNull
+    public @EntityType String getEntity(int index) {
+        return mEntities.get(index);
+    }
+
+    /**
+     * Returns the confidence score for the specified entity. The value ranges from
+     * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the
+     * classified text.
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public float getConfidenceScore(@EntityType String entity) {
+        return mEntityConfidence.getConfidenceScore(entity);
+    }
+
+    /**
+     * Returns an icon that may be rendered on a widget used to act on the classified text.
+     */
+    @Nullable
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Returns a label that may be rendered on a widget used to act on the classified text.
+     */
+    @Nullable
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Returns an intent that may be fired to act on the classified text.
+     */
+    @Nullable
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    /**
+     * Returns an OnClickListener that may be triggered to act on the classified text.
+     */
+    @Nullable
+    public OnClickListener getOnClickListener() {
+        return mOnClickListener;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("TextClassificationResult {"
+                        + "text=%s, entities=%s, label=%s, intent=%s}",
+                mText, mEntityConfidence, mLabel, mIntent);
+    }
+
+    /**
+     * Creates an OnClickListener that starts an activity with the specified intent.
+     *
+     * @throws IllegalArgumentException if context or intent is null
+     * @hide
+     */
+    @NonNull
+    public static OnClickListener createStartActivityOnClick(
+            @NonNull final Context context, @NonNull final Intent intent) {
+        Preconditions.checkArgument(context != null);
+        Preconditions.checkArgument(intent != null);
+        return v -> context.startActivity(intent);
+    }
+
+    /**
+     * Builder for building {@link TextClassificationResult}s.
+     */
+    public static final class Builder {
+
+        @NonNull private String mText;
+        @Nullable private Drawable mIcon;
+        @Nullable private String mLabel;
+        @Nullable private Intent mIntent;
+        @Nullable private OnClickListener mOnClickListener;
+        @NonNull private final EntityConfidence<String> mEntityConfidence =
+                new EntityConfidence<>();
+
+        /**
+         * Sets the classified text.
+         */
+        public Builder setText(@NonNull String text) {
+            mText = Preconditions.checkNotNull(text);
+            return this;
+        }
+
+        /**
+         * Sets an entity type for the classification result and assigns a confidence score.
+         *
+         * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
+         *      0 implies the entity does not exist for the classified text.
+         *      Values greater than 1 are clamped to 1.
+         */
+        public Builder setEntityType(
+                @NonNull @EntityType String type,
+                @FloatRange(from = 0.0, to = 1.0)float confidenceScore) {
+            mEntityConfidence.setEntityType(type, confidenceScore);
+            return this;
+        }
+
+        /**
+         * Sets an icon that may be rendered on a widget used to act on the classified text.
+         */
+        public Builder setIcon(@Nullable Drawable icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets a label that may be rendered on a widget used to act on the classified text.
+         */
+        public Builder setLabel(@Nullable String label) {
+            mLabel = label;
+            return this;
+        }
+
+        /**
+         * Sets an intent that may be fired to act on the classified text.
+         */
+        public Builder setIntent(@Nullable Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /**
+         * Sets an OnClickListener that may be triggered to act on the classified text.
+         */
+        public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
+            mOnClickListener = onClickListener;
+            return this;
+        }
+
+        /**
+         * Builds an returns a {@link TextClassificationResult}.
+         */
+        public TextClassificationResult build() {
+            return new TextClassificationResult(
+                    mText, mIcon, mLabel, mIntent, mOnClickListener, mEntityConfidence);
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
new file mode 100644
index 0000000..b84e2ae
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for providing text classification related features.
+ *
+ * <p>Unless otherwise stated, methods of this interface are blocking operations and you should
+ * avoid calling them on the UI thread.
+ */
+public interface TextClassifier {
+
+    String TYPE_OTHER = "other";
+    String TYPE_EMAIL = "email";
+    String TYPE_PHONE = "phone";
+    String TYPE_ADDRESS = "address";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({
+            TYPE_OTHER, TYPE_EMAIL, TYPE_PHONE, TYPE_ADDRESS
+    })
+    @interface EntityType {}
+
+    /**
+     * No-op TextClassifier.
+     * This may be used to turn off TextClassifier features.
+     */
+    TextClassifier NO_OP = new TextClassifier() {
+
+        @Override
+        public TextSelection suggestSelection(
+                CharSequence text, int selectionStartIndex, int selectionEndIndex) {
+            return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
+        }
+
+        @Override
+        public TextClassificationResult getTextClassificationResult(
+                CharSequence text, int startIndex, int endIndex) {
+            return TextClassificationResult.EMPTY;
+        }
+
+        @Override
+        public LinksInfo getLinks(CharSequence text, int linkMask) {
+            return LinksInfo.NO_OP;
+        }
+    };
+
+    /**
+     * Returns suggested text selection indices, recognized types and their associated confidence
+     * scores. The selections are ordered from highest to lowest scoring.
+     *
+     * @param text text providing context for the selected text (which is specified
+     *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
+     * @param selectionStartIndex start index of the selected part of text
+     * @param selectionEndIndex end index of the selected part of text
+     *
+     * @throws IllegalArgumentException if text is null; selectionStartIndex is negative;
+     *      selectionEndIndex is greater than text.length() or less than selectionStartIndex
+     */
+    @NonNull
+    TextSelection suggestSelection(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int selectionStartIndex,
+            @IntRange(from = 0) int selectionEndIndex);
+
+    /**
+     * Returns a {@link TextClassificationResult} object that can be used to generate a widget for
+     * handling the classified text.
+     *
+     * @param text text providing context for the text to classify (which is specified
+     *      by the sub sequence starting at startIndex and ending at endIndex)
+     * @param startIndex start index of the text to classify
+     * @param endIndex end index of the text to classify
+     *
+     * @throws IllegalArgumentException if text is null; startIndex is negative;
+     *      endIndex is greater than text.length() or less than startIndex
+     */
+    @NonNull
+    TextClassificationResult getTextClassificationResult(
+            @NonNull CharSequence text, int startIndex, int endIndex);
+
+    /**
+     * Returns a {@link LinksInfo} that may be applied to the text to annotate it with links
+     * information.
+     *
+     * @param text the text to generate annotations for
+     * @param linkMask See {@link android.text.util.Linkify} for a list of linkMasks that may be
+     *      specified. Subclasses of this interface may specify additional linkMasks
+     *
+     * @throws IllegalArgumentException if text is null
+     */
+    LinksInfo getLinks(@NonNull CharSequence text, int linkMask);
+}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
new file mode 100644
index 0000000..0657067
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.icu.text.BreakIterator;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.text.SmartSelection;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.method.WordIterator;
+import android.text.style.ClickableSpan;
+import android.text.util.Linkify;
+import android.util.Log;
+import android.view.View;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Default implementation of the {@link TextClassifier} interface.
+ *
+ * <p>This class uses machine learning to recognize entities in text.
+ * Unless otherwise stated, methods of this class are blocking operations and should most
+ * likely not be called on the UI thread.
+ *
+ * @hide
+ */
+final class TextClassifierImpl implements TextClassifier {
+
+    private static final String LOG_TAG = "TextClassifierImpl";
+
+    private final Context mContext;
+    private final ParcelFileDescriptor mFd;
+    private SmartSelection mSmartSelection;
+
+    TextClassifierImpl(Context context, ParcelFileDescriptor fd) {
+        mContext = Preconditions.checkNotNull(context);
+        mFd = Preconditions.checkNotNull(fd);
+    }
+
+    @Override
+    public TextSelection suggestSelection(
+            @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex) {
+        validateInput(text, selectionStartIndex, selectionEndIndex);
+        try {
+            if (text.length() > 0) {
+                final String string = text.toString();
+                final int[] startEnd = getSmartSelection()
+                        .suggest(string, selectionStartIndex, selectionEndIndex);
+                final int start = startEnd[0];
+                final int end = startEnd[1];
+                if (start >= 0 && end <= string.length() && start <= end) {
+                    final String type = getSmartSelection().classifyText(string, start, end);
+                    return new TextSelection.Builder(start, end)
+                            .setEntityType(type, 1.0f)
+                            .build();
+                } else {
+                    // We can not trust the result. Log the issue and ignore the result.
+                    Log.d(LOG_TAG, "Got bad indices for input text. Ignoring result.");
+                }
+            }
+        } catch (Throwable t) {
+            // Avoid throwing from this method. Log the error.
+            Log.e(LOG_TAG,
+                    "Error suggesting selection for text. No changes to selection suggested.",
+                    t);
+        }
+        // Getting here means something went wrong, return a NO_OP result.
+        return TextClassifier.NO_OP.suggestSelection(
+                text, selectionStartIndex, selectionEndIndex);
+    }
+
+    @Override
+    public TextClassificationResult getTextClassificationResult(
+            @NonNull CharSequence text, int startIndex, int endIndex) {
+        validateInput(text, startIndex, endIndex);
+        try {
+            if (text.length() > 0) {
+                final CharSequence classified = text.subSequence(startIndex, endIndex);
+                String type = getSmartSelection()
+                        .classifyText(text.toString(), startIndex, endIndex);
+                if (!TextUtils.isEmpty(type)) {
+                    type = type.toLowerCase().trim();
+                    // TODO: Added this log for debug only. Remove before release.
+                    Log.d(LOG_TAG, String.format("Classification type: %s", type));
+                    return createClassificationResult(type, classified);
+                }
+            }
+        } catch (Throwable t) {
+            // Avoid throwing from this method. Log the error.
+            Log.e(LOG_TAG, "Error getting assist info.", t);
+        }
+        // Getting here means something went wrong, return a NO_OP result.
+        return TextClassifier.NO_OP.getTextClassificationResult(text, startIndex, endIndex);
+    }
+
+
+    @Override
+    public LinksInfo getLinks(CharSequence text, int linkMask) {
+        Preconditions.checkArgument(text != null);
+        try {
+            return LinksInfoFactory.create(
+                    mContext, getSmartSelection(), text.toString(), linkMask);
+        } catch (Throwable t) {
+            // Avoid throwing from this method. Log the error.
+            Log.e(LOG_TAG, "Error getting links info.", t);
+        }
+        // Getting here means something went wrong, return a NO_OP result.
+        return TextClassifier.NO_OP.getLinks(text, linkMask);
+    }
+
+    private synchronized SmartSelection getSmartSelection() throws FileNotFoundException {
+        if (mSmartSelection == null) {
+            mSmartSelection = new SmartSelection(mFd.getFd());
+        }
+        return mSmartSelection;
+    }
+
+    private TextClassificationResult createClassificationResult(String type, CharSequence text) {
+        final Intent intent = IntentFactory.create(type, text.toString());
+        if (intent == null) {
+            return TextClassificationResult.EMPTY;
+        }
+
+        final TextClassificationResult.Builder builder = new TextClassificationResult.Builder()
+                .setText(text.toString())
+                .setEntityType(type, 1.0f /* confidence */)
+                .setIntent(intent)
+                .setOnClickListener(TextClassificationResult.createStartActivityOnClick(
+                        mContext, intent))
+                .setLabel(IntentFactory.getLabel(mContext, type));
+        final PackageManager pm = mContext.getPackageManager();
+        final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+        // TODO: If the resolveInfo is the "chooser", do not set the package name and use a default
+        // icon for this classification type.
+        intent.setPackage(resolveInfo.activityInfo.packageName);
+        Drawable icon = resolveInfo.activityInfo.loadIcon(pm);
+        if (icon == null) {
+            icon = resolveInfo.loadIcon(pm);
+        }
+        builder.setIcon(icon);
+        return builder.build();
+    }
+
+    /**
+     * @throws IllegalArgumentException if text is null; startIndex is negative;
+     *      endIndex is greater than text.length() or less than startIndex
+     */
+    private static void validateInput(@NonNull CharSequence text, int startIndex, int endIndex) {
+        Preconditions.checkArgument(text != null);
+        Preconditions.checkArgument(startIndex >= 0);
+        Preconditions.checkArgument(endIndex <= text.length());
+        Preconditions.checkArgument(endIndex >= startIndex);
+    }
+
+    /**
+     * Detects and creates links for specified text.
+     */
+    private static final class LinksInfoFactory {
+
+        private LinksInfoFactory() {}
+
+        public static LinksInfo create(
+                Context context, SmartSelection smartSelection, String text, int linkMask) {
+            final WordIterator wordIterator = new WordIterator();
+            wordIterator.setCharSequence(text, 0, text.length());
+            final List<SpanSpec> spans = new ArrayList<>();
+            int start = 0;
+            int end;
+            while ((end = wordIterator.nextBoundary(start)) != BreakIterator.DONE) {
+                final String token = text.substring(start, end);
+                if (TextUtils.isEmpty(token)) {
+                    continue;
+                }
+
+                final int[] selection = smartSelection.suggest(text, start, end);
+                final int selectionStart = selection[0];
+                final int selectionEnd = selection[1];
+                if (selectionStart >= 0 && selectionEnd <= text.length()
+                        && selectionStart <= selectionEnd) {
+                    final String type =
+                            smartSelection.classifyText(text, selectionStart, selectionEnd);
+                    if (matches(type, linkMask)) {
+                        final ClickableSpan span = createSpan(
+                                context, type, text.substring(selectionStart, selectionEnd));
+                        spans.add(new SpanSpec(selectionStart, selectionEnd, span));
+                    }
+                }
+                start = end;
+            }
+            return new LinksInfoImpl(text, avoidOverlaps(spans, text));
+        }
+
+        /**
+         * Returns true if the classification type matches the specified linkMask.
+         */
+        private static boolean matches(String type, int linkMask) {
+            if ((linkMask & Linkify.PHONE_NUMBERS) != 0
+                    && TextClassifier.TYPE_PHONE.equals(type)) {
+                return true;
+            }
+            if ((linkMask & Linkify.EMAIL_ADDRESSES) != 0
+                    && TextClassifier.TYPE_EMAIL.equals(type)) {
+                return true;
+            }
+            if ((linkMask & Linkify.MAP_ADDRESSES) != 0
+                    && TextClassifier.TYPE_ADDRESS.equals(type)) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Trim the number of spans so that no two spans overlap.
+         *
+         * This algorithm first ensures that there is only one span per start index, then it
+         * makes sure that no two spans overlap.
+         */
+        private static List<SpanSpec> avoidOverlaps(List<SpanSpec> spans, String text) {
+            Collections.sort(spans, Comparator.comparingInt(span -> span.mStart));
+            // Group spans by start index. Take the longest span.
+            final Map<Integer, SpanSpec> reps = new LinkedHashMap<>();  // order matters.
+            final int size = spans.size();
+            for (int i = 0; i < size; i++) {
+                final SpanSpec span = spans.get(i);
+                final LinksInfoFactory.SpanSpec rep = reps.get(span.mStart);
+                if (rep == null || rep.mEnd < span.mEnd) {
+                    reps.put(span.mStart, span);
+                }
+            }
+            // Avoid span intersections. Take the longer span.
+            final LinkedList<SpanSpec> result = new LinkedList<>();
+            for (SpanSpec rep : reps.values()) {
+                if (result.isEmpty()) {
+                    result.add(rep);
+                    continue;
+                }
+
+                final SpanSpec last = result.getLast();
+                if (rep.mStart < last.mEnd) {
+                    // Spans intersect. Use the one with characters.
+                    if ((rep.mEnd - rep.mStart) > (last.mEnd - last.mStart)) {
+                        result.set(result.size() - 1, rep);
+                    }
+                } else {
+                    result.add(rep);
+                }
+            }
+            return result;
+        }
+
+        private static ClickableSpan createSpan(
+                final Context context, final String type, final String text) {
+            return new ClickableSpan() {
+                // TODO: Style this span.
+                @Override
+                public void onClick(View widget) {
+                    context.startActivity(IntentFactory.create(type, text));
+                }
+            };
+        }
+
+        /**
+         * Implementation of LinksInfo that adds ClickableSpans to the specified text.
+         */
+        private static final class LinksInfoImpl implements LinksInfo {
+
+            private final CharSequence mOriginalText;
+            private final List<SpanSpec> mSpans;
+
+            LinksInfoImpl(CharSequence originalText, List<SpanSpec> spans) {
+                mOriginalText = originalText;
+                mSpans = spans;
+            }
+
+            @Override
+            public boolean apply(@NonNull CharSequence text) {
+                Preconditions.checkArgument(text != null);
+                if (text instanceof Spannable && mOriginalText.toString().equals(text.toString())) {
+                    Spannable spannable = (Spannable) text;
+                    final int size = mSpans.size();
+                    for (int i = 0; i < size; i++) {
+                        final SpanSpec span = mSpans.get(i);
+                        spannable.setSpan(span.mSpan, span.mStart, span.mEnd, 0);
+                    }
+                    return true;
+                }
+                return false;
+            }
+        }
+
+        /**
+         * Span plus its start and end index.
+         */
+        private static final class SpanSpec {
+
+            private final int mStart;
+            private final int mEnd;
+            private final ClickableSpan mSpan;
+
+            SpanSpec(int start, int end, ClickableSpan span) {
+                mStart = start;
+                mEnd = end;
+                mSpan = span;
+            }
+        }
+    }
+
+    /**
+     * Creates intents based on the classification type.
+     */
+    private static final class IntentFactory {
+
+        private IntentFactory() {}
+
+        @Nullable
+        public static Intent create(String type, String text) {
+            switch (type) {
+                case TextClassifier.TYPE_EMAIL:
+                    return new Intent(Intent.ACTION_SENDTO)
+                            .setData(Uri.parse(String.format("mailto:%s", text)));
+                case TextClassifier.TYPE_PHONE:
+                    return new Intent(Intent.ACTION_DIAL)
+                            .setData(Uri.parse(String.format("tel:%s", text)));
+                case TextClassifier.TYPE_ADDRESS:
+                    return new Intent(Intent.ACTION_VIEW)
+                            .setData(Uri.parse(String.format("geo:0,0?q=%s", text)));
+                default:
+                    return null;
+                // TODO: Add other classification types.
+            }
+        }
+
+        @Nullable
+        public static String getLabel(Context context, String type) {
+            switch (type) {
+                case TextClassifier.TYPE_EMAIL:
+                    return context.getString(com.android.internal.R.string.email);
+                case TextClassifier.TYPE_PHONE:
+                    return context.getString(com.android.internal.R.string.dial);
+                case TextClassifier.TYPE_ADDRESS:
+                    return context.getString(com.android.internal.R.string.map);
+                default:
+                    return null;
+                // TODO: Add other classification types.
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
new file mode 100644
index 0000000..d94d163
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Specifies detected languages for a section of text indicated by a start and end index.
+ */
+public final class TextLanguage {
+
+    private final int mStartIndex;
+    private final int mEndIndex;
+    @NonNull private final EntityConfidence<Locale> mLanguageConfidence;
+    @NonNull private final List<Locale> mLanguages;
+
+    private TextLanguage(
+            int startIndex, int endIndex, @NonNull EntityConfidence<Locale> languageConfidence) {
+        mStartIndex = startIndex;
+        mEndIndex = endIndex;
+        mLanguageConfidence = new EntityConfidence<>(languageConfidence);
+        mLanguages = mLanguageConfidence.getEntities();
+    }
+
+    /**
+     * Returns the start index of the detected languages in the text provided to generate this
+     * object.
+     */
+    public int getStartIndex() {
+        return mStartIndex;
+    }
+
+    /**
+     * Returns the end index of the detected languages in the text provided to generate this object.
+     */
+    public int getEndIndex() {
+        return mEndIndex;
+    }
+
+    /**
+     * Returns the number of languages found in the classified text.
+     */
+    @IntRange(from = 0)
+    public int getLanguageCount() {
+        return mLanguages.size();
+    }
+
+    /**
+     * Returns the language locale at the specified index.
+     * Language locales are ordered from high confidence to low confidence.
+     *
+     * @throws IndexOutOfBoundsException if the specified index is out of range.
+     * @see #getLanguageCount() for the number of language locales available.
+     */
+    @NonNull
+    public Locale getLanguage(int index) {
+        return mLanguages.get(index);
+    }
+
+    /**
+     * Returns the confidence score for the specified language. The value ranges from
+     * 0 (low confidence) to 1 (high confidence). 0 indicates that the language was
+     * not found for the classified text.
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public float getConfidenceScore(@Nullable Locale language) {
+        return mLanguageConfidence.getConfidenceScore(language);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("TextLanguage {%d, %d, %s}",
+                mStartIndex, mEndIndex, mLanguageConfidence);
+    }
+
+    /**
+     * Builder to build {@link TextLanguage} objects.
+     */
+    public static final class Builder {
+
+        private final int mStartIndex;
+        private final int mEndIndex;
+        @NonNull private final EntityConfidence<Locale> mLanguageConfidence =
+                new EntityConfidence<>();
+
+        /**
+         * Creates a builder to build {@link TextLanguage} objects.
+         *
+         * @param startIndex the start index of the detected languages in the text provided
+         *      to generate the result
+         * @param endIndex the end index of the detected languages in the text provided
+         *      to generate the result. Must be greater than startIndex
+         */
+        public Builder(@IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex) {
+            Preconditions.checkArgument(startIndex >= 0);
+            Preconditions.checkArgument(endIndex > startIndex);
+            mStartIndex = startIndex;
+            mEndIndex = endIndex;
+        }
+
+        /**
+         * Sets a language locale with the associated confidence score.
+         */
+        public Builder setLanguage(
+                @NonNull Locale locale, @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
+            mLanguageConfidence.setEntityType(locale, confidenceScore);
+            return this;
+        }
+
+        /**
+         * Builds and returns a {@link TextLanguage}.
+         */
+        public TextLanguage build() {
+            return new TextLanguage(mStartIndex, mEndIndex, mLanguageConfidence);
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
new file mode 100644
index 0000000..3172c13
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.view.textclassifier.TextClassifier.EntityType;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Information about where text selection should be.
+ */
+public final class TextSelection {
+
+    private final int mStartIndex;
+    private final int mEndIndex;
+    @NonNull private final EntityConfidence<String> mEntityConfidence;
+    @NonNull private final List<String> mEntities;
+
+    private TextSelection(
+            int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence) {
+        mStartIndex = startIndex;
+        mEndIndex = endIndex;
+        mEntityConfidence = new EntityConfidence<>(entityConfidence);
+        mEntities = mEntityConfidence.getEntities();
+    }
+
+    /**
+     * Returns the start index of the text selection.
+     */
+    public int getSelectionStartIndex() {
+        return mStartIndex;
+    }
+
+    /**
+     * Returns the end index of the text selection.
+     */
+    public int getSelectionEndIndex() {
+        return mEndIndex;
+    }
+
+    /**
+     * Returns the number of entities found in the classified text.
+     */
+    @IntRange(from = 0)
+    public int getEntityCount() {
+        return mEntities.size();
+    }
+
+    /**
+     * Returns the entity at the specified index. Entities are ordered from high confidence
+     * to low confidence.
+     *
+     * @throws IndexOutOfBoundsException if the specified index is out of range.
+     * @see #getEntityCount() for the number of entities available.
+     */
+    @NonNull
+    public @EntityType String getEntity(int index) {
+        return mEntities.get(index);
+    }
+
+    /**
+     * Returns the confidence score for the specified entity. The value ranges from
+     * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the
+     * classified text.
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public float getConfidenceScore(@EntityType String entity) {
+        return mEntityConfidence.getConfidenceScore(entity);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("TextSelection {%d, %d, %s}",
+                mStartIndex, mEndIndex, mEntityConfidence);
+    }
+
+    /**
+     * Builder used to build {@link TextSelection} objects.
+     */
+    public static final class Builder {
+
+        private final int mStartIndex;
+        private final int mEndIndex;
+        @NonNull private final EntityConfidence<String> mEntityConfidence =
+                new EntityConfidence<>();
+
+        /**
+         * Creates a builder used to build {@link TextSelection} objects.
+         *
+         * @param startIndex the start index of the text selection.
+         * @param endIndex the end index of the text selection. Must be greater than startIndex
+         */
+        public Builder(@IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex) {
+            Preconditions.checkArgument(startIndex >= 0);
+            Preconditions.checkArgument(endIndex > startIndex);
+            mStartIndex = startIndex;
+            mEndIndex = endIndex;
+        }
+
+        /**
+         * Sets an entity type for the classified text and assigns a confidence score.
+         *
+         * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
+         *      0 implies the entity does not exist for the classified text.
+         *      Values greater than 1 are clamped to 1.
+         */
+        public Builder setEntityType(
+                @NonNull @EntityType String type,
+                @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
+            mEntityConfidence.setEntityType(type, confidenceScore);
+            return this;
+        }
+
+        /**
+         * Builds and returns {@link TextSelection} object.
+         */
+        public TextSelection build() {
+            return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence);
+        }
+    }
+}
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index 894f080..dbca7ff 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -78,4 +78,14 @@
      * Enable or disable the WebView package fallback mechanism.
      */
     void enableFallbackLogic(boolean enable);
+
+    /**
+     * Used by Settings to determine whether multiprocess is enabled.
+     */
+    boolean isMultiProcessEnabled();
+
+    /**
+     * Used by Settings to enable/disable multiprocess.
+     */
+    void enableMultiProcess(boolean enable);
 }
diff --git a/core/java/android/webkit/RenderProcessGoneDetail.java b/core/java/android/webkit/RenderProcessGoneDetail.java
index 77d8596..1c79399 100644
--- a/core/java/android/webkit/RenderProcessGoneDetail.java
+++ b/core/java/android/webkit/RenderProcessGoneDetail.java
@@ -32,4 +32,15 @@
      *         system.
      **/
     public abstract boolean didCrash();
+
+    /**
+     * Returns the renderer priority that was set at the time that the
+     * renderer exited.  This may be greater than the priority that
+     * any individual {@link WebView} requested using
+     * {@link WebView#setRendererPriorityPolicy}.
+     *
+     * @return the priority of the renderer at exit.
+     **/
+    @WebView.RendererPriority
+    public abstract int rendererPriorityAtExit();
 }
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
new file mode 100644
index 0000000..404bcf4
--- /dev/null
+++ b/core/java/android/webkit/UserPackage.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for storing a (user,PackageInfo) mapping.
+ * @hide
+ */
+public class UserPackage {
+    private final UserInfo mUserInfo;
+    private final PackageInfo mPackageInfo;
+
+    public UserPackage(UserInfo user, PackageInfo packageInfo) {
+        this.mUserInfo = user;
+        this.mPackageInfo = packageInfo;
+    }
+
+    /**
+     * Returns a list of (User,PackageInfo) pairs corresponding to the PackageInfos for all
+     * device users for the package named {@param packageName}.
+     */
+    public static List<UserPackage> getPackageInfosAllUsers(Context context,
+            String packageName, int packageFlags) {
+        List<UserInfo> users = getAllUsers(context);
+        List<UserPackage> userPackages = new ArrayList<UserPackage>(users.size());
+        for (UserInfo user : users) {
+            PackageInfo packageInfo = null;
+            try {
+                packageInfo = context.getPackageManager().getPackageInfoAsUser(
+                        packageName, packageFlags, user.id);
+            } catch (NameNotFoundException e) {
+            }
+            userPackages.add(new UserPackage(user, packageInfo));
+        }
+        return userPackages;
+    }
+
+    /**
+     * Returns whether the given package is enabled.
+     * This state can be changed by the user from Settings->Apps
+     */
+    public boolean isEnabledPackage() {
+        if (mPackageInfo == null) return false;
+        return mPackageInfo.applicationInfo.enabled;
+    }
+
+    /**
+     * Return true if the package is installed and not hidden
+     */
+    public boolean isInstalledPackage() {
+        if (mPackageInfo == null) return false;
+        return (((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0)
+            && ((mPackageInfo.applicationInfo.privateFlags
+                        & ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
+    }
+
+    public UserInfo getUserInfo() {
+        return mUserInfo;
+    }
+
+    public PackageInfo getPackageInfo() {
+        return mPackageInfo;
+    }
+
+
+    private static List<UserInfo> getAllUsers(Context context) {
+        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        return userManager.getUsers(false);
+    }
+
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f98c099..1e7cddf 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.Widget;
@@ -60,6 +61,8 @@
 
 import java.io.BufferedWriter;
 import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Map;
 
 /**
@@ -2151,6 +2154,94 @@
         return mProvider.findHierarchyView(className, hashCode);
     }
 
+    /** @hide */
+    @IntDef({
+        RENDERER_PRIORITY_WAIVED,
+        RENDERER_PRIORITY_BOUND,
+        RENDERER_PRIORITY_IMPORTANT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RendererPriority {}
+
+    /**
+     * The renderer associated with this WebView is bound with
+     * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level
+     * {@link WebView} renderers will be strong targets for out of memory
+     * killing.
+     *
+     * Use with {@link #setRendererPriorityPolicy}.
+     */
+    public static final int RENDERER_PRIORITY_WAIVED = 0;
+    /**
+     * The renderer associated with this WebView is bound with
+     * the default priority for services.
+     *
+     * Use with {@link #setRendererPriorityPolicy}.
+     */
+    public static final int RENDERER_PRIORITY_BOUND = 1;
+    /**
+     * The renderer associated with this WebView is bound with
+     * {@link Context#BIND_IMPORTANT}.
+     *
+     * Use with {@link #setRendererPriorityPolicy}.
+     */
+    public static final int RENDERER_PRIORITY_IMPORTANT = 2;
+
+    /**
+     * Set the renderer priority policy for this {@link WebView}. The
+     * priority policy will be used to determine whether an out of
+     * process renderer should be considered to be a target for OOM
+     * killing.
+     *
+     * Because a renderer can be associated with more than one
+     * WebView, the final priority it is computed as the maximum of
+     * any attached WebViews. When a WebView is destroyed it will
+     * cease to be considerered when calculating the renderer
+     * priority. Once no WebViews remain associated with the renderer,
+     * the priority of the renderer will be reduced to
+     * {@link #RENDERER_PRIORITY_WAIVED}.
+     *
+     * The default policy is to set the priority to
+     * {@link #RENDERER_PRIORITY_IMPORTANT} regardless of visibility,
+     * and this should not be changed unless the caller also handles
+     * renderer crashes with
+     * {@link WebViewClient#onRenderProcessGone}. Any other setting
+     * will result in WebView renderers being killed by the system
+     * more aggressively than the application.
+     *
+     * @param rendererRequestedPriority the minimum priority at which
+     *        this WebView desires the renderer process to be bound.
+     * @param waivedWhenNotVisible if true, this flag specifies that
+     *        when this WebView is not visible, it will be treated as
+     *        if it had requested a priority of
+     *        {@link #RENDERER_PRIORITY_WAIVED}.
+     */
+    public void setRendererPriorityPolicy(
+            @RendererPriority int rendererRequestedPriority,
+            boolean waivedWhenNotVisible) {
+        mProvider.setRendererPriorityPolicy(rendererRequestedPriority, waivedWhenNotVisible);
+    }
+
+    /**
+     * Get the requested renderer priority for this WebView.
+     *
+     * @return the requested renderer priority policy.
+     */
+    @RendererPriority
+    public int getRendererRequestedPriority() {
+        return mProvider.getRendererRequestedPriority();
+    }
+
+    /**
+     * Return whether this WebView requests a priority of
+     * {@link #RENDERER_PRIORITY_WAIVED} when not visible.
+     *
+     * @return whether this WebView requests a priority of
+     * {@link #RENDERER_PRIORITY_WAIVED} when not visible.
+     */
+    public boolean getRendererPriorityWaivedWhenNotVisible() {
+        return mProvider.getRendererPriorityWaivedWhenNotVisible();
+    }
     //-------------------------------------------------------------------------
     // Interface for WebView providers
     //-------------------------------------------------------------------------
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 2cdff79..92d0d71 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -26,6 +26,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.SparseArray;
@@ -206,4 +207,15 @@
                     appInfo.getBaseResourcePath(), newAssetPath);
         }
     }
+
+    /**
+     * Returns whether WebView should run in multiprocess mode.
+     */
+    public boolean isMultiProcessEnabled() {
+        try {
+            return WebViewFactory.getUpdateService().isMultiProcessEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index dd1b0d2..ffc18b1 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -269,6 +269,12 @@
 
     public View findHierarchyView(String className, int hashCode);
 
+    public void setRendererPriorityPolicy(int rendererRequestedPriority, boolean waivedWhenNotVisible);
+
+    public int getRendererRequestedPriority();
+
+    public boolean getRendererPriorityWaivedWhenNotVisible();
+
     //-------------------------------------------------------------------------
     // Provider internal methods
     //-------------------------------------------------------------------------
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index af5c842..6706790 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -42,6 +42,8 @@
  * <p>See the <a href="{@docRoot}guide/topics/ui/controls/text.html">Text Fields</a>
  * guide.</p>
  * <p>
+ * This widget does not support auto-sizing text.
+ * <p>
  * <b>XML attributes</b>
  * <p>
  * See {@link android.R.styleable#EditText EditText Attributes},
@@ -167,8 +169,6 @@
             Log.w(VIEW_LOG_TAG, "EditText.autoFill(): no text on AutoFillValue");
             return;
         }
-        // TODO(b/33197203): once auto-fill is triggered by the IME, we'll need a new setText()
-        // or setAutoFillText() method on TextView to avoid re-triggering it.
         setText(text);
     }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2703d6e..f7f9a81 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -60,8 +60,6 @@
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.StaticLayout;
-import android.text.TextClassification;
-import android.text.TextSelection;
 import android.text.TextUtils;
 import android.text.method.KeyListener;
 import android.text.method.MetaKeyKeyListener;
@@ -108,6 +106,8 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.view.textclassifier.TextClassificationResult;
+import android.view.textclassifier.TextSelection;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.TextView.Drawables;
 import android.widget.TextView.OnEditorActionListener;
@@ -149,7 +149,7 @@
     private static final String UNDO_OWNER_TAG = "Editor";
 
     // Ordering constants used to place the Action Mode or context menu items in their menu.
-    // Reserve 1 for the app that the ASSIST logic suggests as the best app to handle the selection.
+    private static final int MENU_ITEM_ORDER_ASSIST = 1;
     private static final int MENU_ITEM_ORDER_UNDO = 2;
     private static final int MENU_ITEM_ORDER_REDO = 3;
     private static final int MENU_ITEM_ORDER_SHARE = 4;
@@ -238,6 +238,8 @@
     private boolean mPreserveSelection;
     private boolean mRestartActionModeOnNextRefresh;
 
+    private TextClassificationResult mTextClassificationResult;
+
     boolean mIsBeingLongClicked;
 
     private SuggestionsPopupWindow mSuggestionsPopupWindow;
@@ -1889,7 +1891,7 @@
             mInsertionPointCursorController.invalidateHandle();
         }
         if (mTextActionMode != null) {
-            mTextActionMode.invalidate();
+            invalidateActionMode(getTextClassifierInfo(false));
         }
     }
 
@@ -1982,12 +1984,12 @@
                 if (mRestartActionModeOnNextRefresh) {
                     // To avoid distraction, newly start action mode only when selection action
                     // mode is being restarted.
-                    startSelectionActionMode();
+                    startSelectionActionMode(getTextClassifierInfo(true));
                 }
             } else if (selectionController == null || !selectionController.isActive()) {
                 // Insertion action mode is active. Avoid dismissing the selection.
                 stopTextActionModeWithPreservingSelection();
-                startSelectionActionMode();
+                startSelectionActionMode(getTextClassifierInfo(true));
             } else {
                 mTextActionMode.invalidateContentRect();
             }
@@ -2031,7 +2033,8 @@
      *
      * @return true if the selection mode was actually started.
      */
-    boolean startSelectionActionMode() {
+    boolean startSelectionActionMode(@Nullable TextClassificationResult textClassificationResult) {
+        mTextClassificationResult = textClassificationResult;
         boolean selectionStarted = startSelectionActionModeInternal();
         if (selectionStarted) {
             getSelectionController().show();
@@ -2040,6 +2043,40 @@
         return selectionStarted;
     }
 
+    private boolean startSelectionActionModeWithTextAssistant() {
+        return startSelectionActionMode(getTextClassifierInfo(true));
+    }
+
+    private void invalidateActionMode(TextClassificationResult textClassificationResult) {
+        mTextClassificationResult = textClassificationResult;
+        mTextActionMode.invalidate();
+    }
+
+    // TODO: Make this a non-blocking call.
+    private TextClassificationResult getTextClassifierInfo(boolean updateSelection) {
+        // TODO: Trim the text so that only text necessary to provide context of the selected
+        // text is sent to the assistant.
+        final int trimStartIndex = 0;
+        final int trimEndIndex = mTextView.getText().length();
+        CharSequence trimmedText =
+                mTextView.getText().subSequence(trimStartIndex, trimEndIndex);
+        int startIndex = mTextView.getSelectionStart() - trimStartIndex;
+        int endIndex = mTextView.getSelectionEnd() - trimStartIndex;
+
+        if (updateSelection) {
+            TextSelection textSelection = mTextView.getTextClassifier()
+                    .suggestSelection(trimmedText, startIndex, endIndex);
+            startIndex = Math.max(0, textSelection.getSelectionStartIndex() + trimStartIndex);
+            endIndex = Math.min(mTextView.getText().length(),
+                    textSelection.getSelectionEndIndex() + trimStartIndex);
+            Selection.setSelection((Spannable) mTextView.getText(), startIndex, endIndex);
+            return getTextClassifierInfo(false);
+        }
+
+        return mTextView.getTextClassifier()
+                .getTextClassificationResult(trimmedText, startIndex, endIndex);
+    }
+
     /**
      * If the TextView allows text selection, selects the current word when no existing selection
      * was available and starts a drag.
@@ -2086,7 +2123,7 @@
         }
         if (mTextActionMode != null) {
             // Text action mode is already started
-            mTextActionMode.invalidate();
+            invalidateActionMode(getTextClassifierInfo(false));
             return false;
         }
 
@@ -2480,7 +2517,7 @@
     }
 
     void onDrop(DragEvent event) {
-        StringBuilder content = new StringBuilder("");
+        SpannableStringBuilder content = new SpannableStringBuilder();
 
         final DragAndDropPermissions permissions = DragAndDropPermissions.obtain(event);
         if (permissions != null) {
@@ -3744,8 +3781,7 @@
         private final Path mSelectionPath = new Path();
         private final RectF mSelectionBounds = new RectF();
         private final boolean mHasSelection;
-
-        private int mHandleHeight;
+        private final int mHandleHeight;
 
         public TextActionModeCallback(boolean hasSelection) {
             mHasSelection = hasSelection;
@@ -3765,18 +3801,19 @@
                 if (insertionController != null) {
                     insertionController.getHandle();
                     mHandleHeight = mSelectHandleCenter.getMinimumHeight();
+                } else {
+                    mHandleHeight = 0;
                 }
             }
         }
 
         @Override
         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-            TextClassification textClassification = updateSelectionWithTextAssistant();
-
             mode.setTitle(null);
             mode.setSubtitle(null);
             mode.setTitleOptionalHint(true);
-            populateMenuWithItems(menu, textClassification);
+            populateMenuWithItems(menu);
+            updateAssistMenuItem(menu, mTextClassificationResult);
 
             Callback customCallback = getCustomCallback();
             if (customCallback != null) {
@@ -3802,30 +3839,13 @@
             }
         }
 
-        private TextClassification updateSelectionWithTextAssistant() {
-            // Trim the text so that only text necessary to provide context of the selected text is
-            // sent to the assistant.
-            CharSequence trimmedText = mTextView.getText();
-            int textLength = mTextView.getText().length();
-            int trimStartIndex = 0;
-            int startIndex = mTextView.getSelectionStart() - trimStartIndex;
-            int endIndex = mTextView.getSelectionEnd() - trimStartIndex;
-            TextSelection textSelection = mTextView.getTextAssistant()
-                    .suggestSelection(trimmedText, startIndex, endIndex);
-            Selection.setSelection(
-                    (Spannable) mTextView.getText(),
-                    Math.max(0, textSelection.getSelectionStartIndex() + trimStartIndex),
-                    Math.min(textLength, textSelection.getSelectionEndIndex() + trimStartIndex));
-            return textSelection.getTextClassification();
-        }
-
         private Callback getCustomCallback() {
             return mHasSelection
                     ? mCustomSelectionActionModeCallback
                     : mCustomInsertionActionModeCallback;
         }
 
-        private void populateMenuWithItems(Menu menu, TextClassification textClassification) {
+        private void populateMenuWithItems(Menu menu) {
             if (mTextView.canCut()) {
                 menu.add(Menu.NONE, TextView.ID_CUT, MENU_ITEM_ORDER_CUT,
                         com.android.internal.R.string.cut)
@@ -3855,13 +3875,13 @@
 
             updateSelectAllItem(menu);
             updateReplaceItem(menu);
-            updateAssistMenuItem(menu, textClassification);
         }
 
         @Override
         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
             updateSelectAllItem(menu);
             updateReplaceItem(menu);
+            updateAssistMenuItem(menu, mTextClassificationResult);
 
             Callback customCallback = getCustomCallback();
             if (customCallback != null) {
@@ -3894,10 +3914,16 @@
             }
         }
 
-        private void updateAssistMenuItem(Menu menu, TextClassification textClassification) {
-            // TODO: Find the best app available to handle the selected text based on information in
-            // the TextClassification object.
-            // Add app icon + intent to trigger app to the menu.
+        private void updateAssistMenuItem(
+                Menu menu, TextClassificationResult textClassificationResult) {
+            menu.removeItem(TextView.ID_ASSIST);
+            if (textClassificationResult != null
+                    && textClassificationResult.getIcon() != null
+                    && textClassificationResult.getOnClickListener() != null) {
+                menu.add(Menu.NONE, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, null)
+                        .setIcon(textClassificationResult.getIcon())
+                        .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+            }
         }
 
         @Override
@@ -3909,6 +3935,10 @@
             if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
                 return true;
             }
+            if (TextView.ID_ASSIST == item.getItemId() && mTextClassificationResult != null) {
+                mTextClassificationResult.getOnClickListener().onClick(mTextView);
+                stopTextActionMode();
+            }
             return mTextView.onTextContextMenuItem(item.getItemId());
         }
 
@@ -3916,6 +3946,7 @@
         public void onDestroyActionMode(ActionMode mode) {
             // Clear mTextActionMode not to recursively destroy action mode by clearing selection.
             mTextActionMode = null;
+            mTextClassificationResult = null;
             Callback customCallback = getCustomCallback();
             if (customCallback != null) {
                 customCallback.onDestroyActionMode(mode);
@@ -4733,7 +4764,7 @@
             }
             positionAtCursorOffset(offset, false);
             if (mTextActionMode != null) {
-                mTextActionMode.invalidate();
+                invalidateActionMode(getTextClassifierInfo(false));
             }
         }
 
@@ -4817,7 +4848,7 @@
             }
             updateDrawable();
             if (mTextActionMode != null) {
-                mTextActionMode.invalidate();
+                invalidateActionMode(getTextClassifierInfo(false));
             }
         }
 
@@ -5465,7 +5496,8 @@
                     resetDragAcceleratorState();
 
                     if (mTextView.hasSelection()) {
-                        startSelectionActionMode();
+                        // TODO: Do not invoke the text assistant if this was a drag selection.
+                        startSelectionActionMode(getTextClassifierInfo(true));
                     }
                     break;
             }
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index eb27533..59bbc3b 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.annotation.MenuRes;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.view.Gravity;
 import android.view.Menu;
@@ -281,4 +282,19 @@
          */
         void onDismiss(PopupMenu menu);
     }
+
+    /**
+     * Returns the {@link ListView} representing the list of menu items in the currently showing
+     * menu.
+     *
+     * @return The view representing the list of menu items.
+     * @hide
+     */
+    @TestApi
+    public ListView getMenuListView() {
+        if (!mPopup.isShowing()) {
+            return null;
+        }
+        return mPopup.getPopup().getListView();
+    }
 }
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 3b7fe86..70b70bc 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -150,7 +150,11 @@
      */
     public void setIsIndicator(boolean isIndicator) {
         mIsUserSeekable = !isIndicator;
-        setFocusable(!isIndicator);
+        if (isIndicator) {
+            setFocusable(FOCUSABLE_AUTO);
+        } else {
+            setFocusable(FOCUSABLE);
+        }
     }
 
     /**
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index eb46b9f..359d04e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3302,7 +3302,7 @@
      * Applies the views asynchronously, moving as much of the task on the background
      * thread as possible.
      *
-     * @see {@link #apply(Context, ViewGroup)}
+     * @see #apply(Context, ViewGroup)
      * @param context Default context to use
      * @param parent Parent that the resulting view hierarchy will be attached to. This method
      * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate.
@@ -3457,7 +3457,7 @@
      * Applies all the actions to the provided view, moving as much of the task on the background
      * thread as possible.
      *
-     * @see {@link #reapply(Context, View)}
+     * @see #reapply(Context, View)
      * @param context Default context to use
      * @param v The view to apply the actions to.  This should be the result of
      * the {@link #apply(Context,ViewGroup)} call.
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 9139361..3822138 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -357,9 +357,9 @@
             setInputType(inputType);
         }
 
-        boolean focusable = true;
-        focusable = a.getBoolean(R.styleable.SearchView_focusable, focusable);
-        setFocusable(focusable);
+        if (getFocusable() == FOCUSABLE_AUTO) {
+            setFocusable(FOCUSABLE);
+        }
 
         a.recycle();
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 345896a..072fe4a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -22,6 +22,7 @@
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
 import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
@@ -76,8 +77,6 @@
 import android.text.Spanned;
 import android.text.SpannedString;
 import android.text.StaticLayout;
-import android.text.TextAssistant;
-import android.text.TextClassificationManager;
 import android.text.TextDirectionHeuristic;
 import android.text.TextDirectionHeuristics;
 import android.text.TextPaint;
@@ -137,6 +136,7 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AnimationUtils;
+import android.view.autofill.AutoFillManager;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
@@ -145,6 +145,8 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
 import android.view.textservice.SpellCheckerSubtype;
 import android.view.textservice.TextServicesManager;
 import android.widget.RemoteViews.RemoteView;
@@ -158,6 +160,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Locale;
@@ -252,11 +256,16 @@
  * @attr ref android.R.styleable#TextView_fontFeatureSettings
  * @attr ref android.R.styleable#TextView_breakStrategy
  * @attr ref android.R.styleable#TextView_hyphenationFrequency
+ * @attr ref android.R.styleable#TextView_autoSizeText
+ * @attr ref android.R.styleable#TextView_autoSizeMinTextSize
+ * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
+ * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
  */
 @RemoteView
 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
     static final String LOG_TAG = "TextView";
     static final boolean DEBUG_EXTRACT = false;
+    static final boolean DEBUG_AUTOFILL = false;
 
     // Enum for the "typeface" XML parameter.
     // TODO: How can we get this from the XML instead of hardcoding it here?
@@ -345,6 +354,8 @@
     private boolean mPreDrawRegistered;
     private boolean mPreDrawListenerDetached;
 
+    private TextClassifier mTextClassifier;
+
     // A flag to prevent repeated movements from escaping the enclosing text view. The idea here is
     // that if a user is holding down a movement key to traverse text, we shouldn't also traverse
     // the view hierarchy. On the other hand, if the user is using the movement key to traverse
@@ -670,17 +681,31 @@
      */
     private int mDeviceProvisionedState = DEVICE_PROVISIONED_UNKNOWN;
 
-    // The TextView does not auto-size text.
-    public static final int AUTO_SIZE_TYPE_NONE = 0;
+    // The TextView does not auto-size text (default).
+    public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0;
     // The TextView performs uniform horizontal and vertical text size scaling to fit within the
     // container.
-    public static final int AUTO_SIZE_TYPE_XY = 1;
-    // Auto-size type.
-    private int mAutoSizeType = AUTO_SIZE_TYPE_NONE;
-    // Specify if auto-size is needed.
-    private boolean mNeedsAutoSize = false;
+    public static final int AUTO_SIZE_TEXT_TYPE_XY = 1;
+    /** @hide */
+    @IntDef({AUTO_SIZE_TEXT_TYPE_NONE, AUTO_SIZE_TEXT_TYPE_XY})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AutoSizeTextType {}
+    // Default minimum size for auto-sizing text in scaled pixels. {@see #setAutoSizeMinTextSize}.
+    private static final int DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP = 12;
+    // Default maximum size for auto-sizing text in scaled pixels. {@see #setAutoSizeMaxTextSize}.
+    private static final int DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP = 112;
     // Default value for the step size in pixels.
     private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
+    // Auto-size text type.
+    private int mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
+    // Specify if auto-size text is needed.
+    private boolean mNeedsAutoSizeText = false;
+    // Step size for auto-sizing in pixels.
+    private int mAutoSizeStepGranularityInPx = 0;
+    // Minimum text size for auto-sizing in pixels.
+    private int mAutoSizeMinTextSizeInPx = 0;
+    // Maximum text size for auto-sizing in pixels.
+    private int mAutoSizeMaxTextSizeInPx = 0;
     // Contains the sorted set of desired text sizes in pixels to pick from when auto-sizing text.
     private int[] mAutoSizeTextSizesInPx;
 
@@ -887,13 +912,6 @@
         CharSequence hint = null;
         boolean password = false;
         int inputType = EditorInfo.TYPE_NULL;
-        int autoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX;
-        int autoSizeMinTextSize = (int) TypedValue.applyDimension(
-                TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics());
-        int autoSizeMaxTextSize = (int) TypedValue.applyDimension(
-                TypedValue.COMPLEX_UNIT_SP, 112, getResources().getDisplayMetrics());
-
-
         a = theme.obtainStyledAttributes(
                     attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
 
@@ -1249,20 +1267,19 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizeText:
-                    mAutoSizeType = a.getInt(attr, AUTO_SIZE_TYPE_NONE);
+                    mAutoSizeTextType = a.getInt(attr, AUTO_SIZE_TEXT_TYPE_NONE);
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizeStepGranularity:
-                    autoSizeStepGranularityInPx = a.getDimensionPixelSize(
-                            attr, DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX);
+                    mAutoSizeStepGranularityInPx = a.getDimensionPixelSize(attr, 0);
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizeMinTextSize:
-                    autoSizeMinTextSize = a.getDimensionPixelSize(attr, autoSizeMinTextSize);
+                    mAutoSizeMinTextSizeInPx = a.getDimensionPixelSize(attr, 0);
                     break;
 
                 case com.android.internal.R.styleable.TextView_autoSizeMaxTextSize:
-                    autoSizeMaxTextSize = a.getDimensionPixelSize(attr, autoSizeMaxTextSize);
+                    mAutoSizeMaxTextSizeInPx = a.getDimensionPixelSize(attr, 0);
                     break;
             }
         }
@@ -1500,26 +1517,22 @@
         if (hint != null) setHint(hint);
 
         /*
-         * Views are not normally focusable unless specified to be.
+         * Views are not normally clickable unless specified to be.
          * However, TextViews that have input or movement methods *are*
-         * focusable by default.
+         * clickable by default. By setting clickable here, we implicitly set focusable as well
+         * if not overridden by the developer.
          */
         a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
-
-        boolean focusable = mMovement != null || getKeyListener() != null;
-        boolean clickable = focusable || isClickable();
-        boolean longClickable = focusable || isLongClickable();
+        boolean canInputOrMove = (mMovement != null || getKeyListener() != null);
+        boolean clickable = canInputOrMove || isClickable();
+        boolean longClickable = canInputOrMove || isLongClickable();
 
         n = a.getIndexCount();
         for (int i = 0; i < n; i++) {
             int attr = a.getIndex(i);
 
             switch (attr) {
-                case com.android.internal.R.styleable.View_focusable:
-                    focusable = a.getBoolean(attr, focusable);
-                    break;
-
                 case com.android.internal.R.styleable.View_clickable:
                     clickable = a.getBoolean(attr, clickable);
                     break;
@@ -1531,7 +1544,6 @@
         }
         a.recycle();
 
-        setFocusable(focusable);
         setClickable(clickable);
         setLongClickable(longClickable);
 
@@ -1542,39 +1554,204 @@
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
         }
 
-        // Setup auto-size.
+        setupAutoSizeTextXY();
+    }
+
+    /**
+     * Specify whether this widget should automatically scale the text to try to perfectly fit
+     * within the layout bounds by taking into account the auto-size configuration.
+     *
+     * @param autoSizeTextType the type of auto-size. Must be one of
+     *        {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or
+     *        {@link TextView#AUTO_SIZE_TEXT_TYPE_XY}
+     *
+     * @attr ref android.R.styleable#TextView_autoSizeText
+     *
+     * @see #getAutoSizeTextType()
+     */
+    public void setAutoSizeTextType(@AutoSizeTextType int autoSizeTextType) {
         if (supportsAutoSizeText()) {
-            switch (mAutoSizeType) {
-                case AUTO_SIZE_TYPE_NONE:
-                    // Nothing to do.
+            switch (autoSizeTextType) {
+                case AUTO_SIZE_TEXT_TYPE_NONE:
+                    if (mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_NONE) {
+                        // Clear all auto-size configuration
+                        mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
+                        mAutoSizeMinTextSizeInPx = 0;
+                        mAutoSizeMaxTextSizeInPx = 0;
+                        mAutoSizeStepGranularityInPx = 0;
+                        mAutoSizeTextSizesInPx = null;
+                        mNeedsAutoSizeText = false;
+                    }
                     break;
-                case AUTO_SIZE_TYPE_XY:
-                    if (autoSizeMaxTextSize <= autoSizeMinTextSize) {
-                        throw new IllegalStateException("Maximum text size is less then minimum "
-                                + "text size");
+                case AUTO_SIZE_TEXT_TYPE_XY:
+                    if (mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_XY) {
+                        mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_XY;
+                        setupAutoSizeTextXY();
                     }
-
-                    if (autoSizeStepGranularityInPx <= 0) {
-                        throw new IllegalStateException("Unexpected zero or negative value for auto"
-                                + " size step granularity in pixels");
-                    }
-
-                    final int autoSizeValuesLength = (autoSizeMaxTextSize - autoSizeMinTextSize)
-                            / autoSizeStepGranularityInPx;
-                    mAutoSizeTextSizesInPx = new int[autoSizeValuesLength];
-                    int sizeToAdd = autoSizeMinTextSize;
-                    for (int i = 0; i < autoSizeValuesLength; i++) {
-                        mAutoSizeTextSizesInPx[i] = sizeToAdd;
-                        sizeToAdd += autoSizeStepGranularityInPx;
-                    }
-
-                    mNeedsAutoSize = true;
                     break;
                 default:
                     throw new IllegalArgumentException(
-                            "Unknown autoSizeText type: " + mAutoSizeType);
+                            "Unknown auto-size text type: " + autoSizeTextType);
+            }
+        }
+    }
+
+    /**
+     * Returns the type of auto-size set for this widget.
+     *
+     * @return an {@code int} corresponding to one of the auto-size types:
+     *         {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or
+     *         {@link TextView#AUTO_SIZE_TEXT_TYPE_XY}
+     *
+     * @attr ref android.R.styleable#TextView_autoSizeText
+     *
+     * @see #setAutoSizeTextType(int)
+     */
+    @AutoSizeTextType
+    public int getAutoSizeTextType() {
+        return mAutoSizeTextType;
+    }
+
+    /**
+     * Sets the auto-size step granularity. It is used in conjunction with auto-size minimum
+     * and maximum text size in order to build the set of text sizes the system uses to choose
+     * from when auto-sizing.
+     *
+     * @param unit the desired dimension unit. See {@link TypedValue} for the possible
+     *             dimension units
+     * @param size the desired size in the given units
+     *
+     * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
+     *
+     * @see #getAutoSizeStepGranularity()
+     * @see #setAutoSizeMinTextSize(int, float)
+     * @see #setAutoSizeMaxTextSize(int, float)
+     */
+    public void setAutoSizeStepGranularity(int unit, float size) {
+        if (supportsAutoSizeText()) {
+            mAutoSizeStepGranularityInPx = (int) TypedValue.applyDimension(
+                    unit, size, getResources().getDisplayMetrics());
+            setupAutoSizeTextXY();
+        }
+    }
+
+    /**
+     * @return the current auto-size step granularity in pixels.
+     *
+     * @see #setAutoSizeStepGranularity(int, float)
+     */
+    public int getAutoSizeStepGranularity() {
+        return mAutoSizeStepGranularityInPx;
+    }
+
+    /**
+     * Sets the minimum text size to be used in conjunction with auto-size maximum text size and
+     * auto-size step granularity in order to build the set of text sizes the system uses to choose
+     * from when auto-sizing.
+     *
+     * @param unit the desired dimension unit. See {@link TypedValue} for the possible
+     *             dimension units
+     * @param size the desired size in the given units
+     *
+     * @attr ref android.R.styleable#TextView_autoSizeMinTextSize
+     *
+     * @see #getAutoSizeMinTextSize()
+     * @see #setAutoSizeMaxTextSize(int, float)
+     * @see #setAutoSizeStepGranularity(int, float)
+     */
+    public void setAutoSizeMinTextSize(int unit, float size) {
+        if (supportsAutoSizeText()) {
+            mAutoSizeMinTextSizeInPx = (int) TypedValue.applyDimension(
+                    unit, size, getResources().getDisplayMetrics());
+            setupAutoSizeTextXY();
+        }
+    }
+
+    /**
+     * @return the current auto-size minimum text size in pixels (the default is 12sp). Note that
+     *         if auto-size has not been configured this function returns {@code 0}.
+     *
+     * @see #setAutoSizeMinTextSize(int, float)
+     */
+    public int getAutoSizeMinTextSize() {
+        return mAutoSizeMinTextSizeInPx;
+    }
+
+    /**
+     * Sets the maximum text size to be used in conjunction with auto-size minimum text size and
+     * auto-size step granularity in order to build the set of text sizes the system uses to choose
+     * from when auto-sizing.
+     *
+     * @param unit the desired dimension unit. See {@link TypedValue} for the possible
+     *             dimension units
+     * @param size the desired size in the given units
+     *
+     * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
+     *
+     * @see #getAutoSizeMaxTextSize()
+     * @see #setAutoSizeMinTextSize(int, float)
+     * @see #setAutoSizeStepGranularity(int, float)
+     */
+    public void setAutoSizeMaxTextSize(int unit, float size) {
+        if (supportsAutoSizeText()) {
+            mAutoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension(
+                    unit, size, getResources().getDisplayMetrics());
+            setupAutoSizeTextXY();
+        }
+    }
+
+    /**
+     * @return the current auto-size maximum text size in pixels (the default is 112sp). Note that
+     *         if auto-size has not been configured this function returns {@code 0}.
+     *
+     * @see #setAutoSizeMaxTextSize(int, float)
+     */
+    public int getAutoSizeMaxTextSize() {
+        return mAutoSizeMaxTextSizeInPx;
+    }
+
+    private void setupAutoSizeTextXY() {
+        if (supportsAutoSizeText() && mAutoSizeTextType == AUTO_SIZE_TEXT_TYPE_XY) {
+            // Set valid defaults.
+            if (mAutoSizeMinTextSizeInPx <= 0) {
+                mAutoSizeMinTextSizeInPx = (int) TypedValue.applyDimension(
+                        TypedValue.COMPLEX_UNIT_SP,
+                        DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
+                        getResources().getDisplayMetrics());
             }
 
+            if (mAutoSizeMaxTextSizeInPx <= 0) {
+                mAutoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension(
+                        TypedValue.COMPLEX_UNIT_SP,
+                        DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
+                        getResources().getDisplayMetrics());
+            }
+
+            if (mAutoSizeStepGranularityInPx <= 0) {
+                mAutoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX;
+            }
+
+            // Validate.
+            if (mAutoSizeMaxTextSizeInPx <= mAutoSizeMinTextSizeInPx) {
+                throw new IllegalStateException("Maximum auto-size text size ("
+                        + mAutoSizeMaxTextSizeInPx + "px) is less or equal to minimum auto-size "
+                        + "text size (" + mAutoSizeMinTextSizeInPx + "px)");
+            }
+
+            // Calculate sizes to choose from based on the current auto-size configuration.
+            final int autoSizeValuesLength = (int) Math.ceil(
+                    (mAutoSizeMaxTextSizeInPx - mAutoSizeMinTextSizeInPx)
+                            / mAutoSizeStepGranularityInPx);
+
+            mAutoSizeTextSizesInPx = new int[autoSizeValuesLength];
+            int sizeToAdd = mAutoSizeMinTextSizeInPx;
+            for (int i = 0; i < autoSizeValuesLength; i++) {
+                mAutoSizeTextSizesInPx[i] = sizeToAdd;
+                sizeToAdd += mAutoSizeStepGranularityInPx;
+            }
+
+            mNeedsAutoSizeText = true;
+            autoSizeText();
         }
     }
 
@@ -1975,11 +2152,11 @@
 
     private void fixFocusableAndClickableSettings() {
         if (mMovement != null || (mEditor != null && mEditor.mKeyListener != null)) {
-            setFocusable(true);
+            setFocusable(FOCUSABLE);
             setClickable(true);
             setLongClickable(true);
         } else {
-            setFocusable(false);
+            setFocusable(FOCUSABLE_AUTO);
             setClickable(false);
             setLongClickable(false);
         }
@@ -3030,9 +3207,6 @@
 
     /**
      * @return the size (in pixels) of the default text size in this TextView.
-     *
-     * <p>Note: if this TextView has mAutoSizeType set to {@link TextView#AUTO_SIZE_TYPE_XY} than
-     * this function returns the maximum text size for auto-sizing.
      */
     @ViewDebug.ExportedProperty(category = "text")
     public float getTextSize() {
@@ -3077,7 +3251,7 @@
     }
 
     /**
-     * Set the default text size to a given unit and value.  See {@link
+     * Set the default text size to a given unit and value. See {@link
      * TypedValue} for the possible dimension units.
      *
      * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
@@ -3113,7 +3287,7 @@
 
             if (mLayout != null) {
                 // Do not auto-size right after setting the text size.
-                mNeedsAutoSize = false;
+                mNeedsAutoSizeText = false;
                 nullLayouts();
                 requestLayout();
                 invalidate();
@@ -3257,20 +3431,6 @@
     }
 
     /**
-     * Returns the font variation settings.
-     *
-     * @return the currently set font variation settings.  Returns null if no variation is
-     * specified.
-     *
-     * @see #setFontVariationSettings(String)
-     * @see Paint#setFontVariationSettings(String) Paint.setFontVariationSettings(String)
-     */
-    @Nullable
-    public String getFontVariationSettings() {
-        return mTextPaint.getFontVariationSettings();
-    }
-
-    /**
      * Sets the break strategy for breaking paragraphs into lines. The default value for
      * TextView is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}, and the default value for
      * EditText is {@link Layout#BREAK_STRATEGY_SIMPLE}, the latter to avoid the
@@ -3377,41 +3537,6 @@
 
 
     /**
-     * Sets TrueType or OpenType font variation settings. The settings string is constructed from
-     * multiple pairs of axis tag and style values. The axis tag must contain four ASCII characters
-     * and must be wrapped with single quotes (U+0027) or double quotes (U+0022). Axis strings that
-     * are longer or shorter than four characters, or contain characters outside of U+0020..U+007E
-     * are invalid. If a specified axis name is not defined in the font, the settings will be
-     * ignored.
-     *
-     * <pre>
-     *   textView.setFontVariationSettings("'wdth' 1.0");
-     *   textView.setFontVariationSettings("'AX  ' 1.8, 'FB  ' 2.0");
-     * </pre>
-     *
-     * @param fontVariationSettings font variation settings. You can pass null or empty string as
-     *                              no variation settings.
-     *
-     * @see #getFontVariationSettings()
-     * @see Paint#getFontVariationSettings() Paint.getFontVariationSettings()
-     */
-    public void setFontVariationSettings(@Nullable String fontVariationSettings) {
-        final String existingSettings = mTextPaint.getFontVariationSettings();
-        if (fontVariationSettings == existingSettings
-                || (fontVariationSettings != null
-                        && fontVariationSettings.equals(existingSettings))) {
-            return;
-        }
-        mTextPaint.setFontVariationSettings(fontVariationSettings);
-
-        if (mLayout != null) {
-            nullLayouts();
-            requestLayout();
-            invalidate();
-        }
-    }
-
-    /**
      * Sets the text color for all the states (normal, selected,
      * focused) to be this color.
      *
@@ -5998,7 +6123,7 @@
 
         mEditor.mTextIsSelectable = selectable;
         setFocusableInTouchMode(selectable);
-        setFocusable(selectable);
+        setFocusable(FOCUSABLE_AUTO);
         setClickable(selectable);
         setLongClickable(selectable);
 
@@ -7612,13 +7737,13 @@
         }
 
         if (isAutoSizeEnabled()) {
-            if (mNeedsAutoSize) {
+            if (mNeedsAutoSizeText) {
                 // Call auto-size after the width and height have been calculated.
                 autoSizeText();
             }
             // Always try to auto-size if enabled. Functions that do not want to trigger auto-sizing
             // after the next measuring round should set this to false.
-            mNeedsAutoSize = true;
+            mNeedsAutoSizeText = true;
         }
 
         setMeasuredDimension(width, height);
@@ -7740,14 +7865,25 @@
             desired = Math.max(desired, dr.mDrawableHeightRight);
         }
 
-        desired += getCompoundPaddingTop() + getCompoundPaddingBottom();
+        int linecount = layout.getLineCount();
+        final int padding = getCompoundPaddingTop() + getCompoundPaddingBottom();
+        desired += padding;
 
         if (mMaxMode != LINES) {
             desired = Math.min(desired, mMaximum);
+        } else if (cap && linecount > mMaximum && layout instanceof DynamicLayout) {
+            desired = layout.getLineTop(mMaximum);
+
+            if (dr != null) {
+                desired = Math.max(desired, dr.mDrawableHeightLeft);
+                desired = Math.max(desired, dr.mDrawableHeightRight);
+            }
+
+            desired += padding;
+            linecount = mMaximum;
         }
 
         if (mMinMode == LINES) {
-            int linecount = layout.getLineCount();
             if (linecount < mMinimum) {
                 desired += getLineHeight() * (mMinimum - linecount);
             }
@@ -8890,6 +9026,15 @@
                 Spannable sp = (Spannable) mText;
                 MetaKeyKeyListener.resetMetaState(sp);
             }
+        } else {
+            final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class);
+            if (afm != null) {
+                if (DEBUG_AUTOFILL) {
+                    Log.v(LOG_TAG, "onFocusChanged(): id=" + getAutoFillViewId() + ", focused= "
+                            + focused);
+                }
+                afm.updateAutoFillInput(this, AutoFillManager.FLAG_UPDATE_UI_HIDE);
+            }
         }
 
         startStopMarquee(focused);
@@ -9366,7 +9511,7 @@
      * auto-size.
      */
     private boolean isAutoSizeEnabled() {
-        return supportsAutoSizeText() && mAutoSizeType != AUTO_SIZE_TYPE_NONE;
+        return supportsAutoSizeText() && mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_NONE;
     }
 
     /**
@@ -9753,7 +9898,7 @@
                         Selection.setSelection((Spannable) text, start, end);
                         // Make sure selection mode is engaged.
                         if (mEditor != null) {
-                            mEditor.startSelectionActionMode();
+                            mEditor.startSelectionActionMode(null);
                         }
                         return true;
                     }
@@ -9897,6 +10042,7 @@
     static final int ID_SHARE = android.R.id.shareText;
     static final int ID_PASTE_AS_PLAIN_TEXT = android.R.id.pasteAsPlainText;
     static final int ID_REPLACE = android.R.id.replaceText;
+    static final int ID_ASSIST = android.R.id.textAssist;
 
     /**
      * Called when a context menu option for the text view is selected.  Currently
@@ -10121,33 +10267,30 @@
         return mEditor == null ? null : mEditor.mCustomInsertionActionModeCallback;
     }
 
-    private TextAssistant mTextAssistant;
-
     /**
-     * Sets the {@link TextAssistant} for this TextView.
-     * If null, this TextView uses the default TextAssistant which comes from the Activity.
+     * Sets the {@link TextClassifier} for this TextView.
      */
-    public void setTextAssistant(TextAssistant textAssistant) {
-        mTextAssistant = textAssistant;
+    public void setTextClassifier(@Nullable TextClassifier textClassifier) {
+        mTextClassifier = textClassifier;
     }
 
     /**
-     * Returns the {@link TextAssistant} used by this TextView.
-     * If no TextAssistant is set, it'll use the one from this TextView's {@link Activity} or
-     * {@link Context}. If no TextAssistant is found, it'll use a no-op TextAssistant.
+     * Returns the {@link TextClassifier} used by this TextView.
+     * If no TextClassifier has been set, this TextView uses the default set by the
+     * {@link TextClassificationManager}.
      */
-    public TextAssistant getTextAssistant() {
-        if (mTextAssistant != null) {
-            return mTextAssistant;
+    @NonNull
+    public TextClassifier getTextClassifier() {
+        if (mTextClassifier == null) {
+            TextClassificationManager tcm =
+                    mContext.getSystemService(TextClassificationManager.class);
+            if (tcm != null) {
+                mTextClassifier = tcm.getDefaultTextClassifier();
+            } else {
+                mTextClassifier = TextClassifier.NO_OP;
+            }
         }
-        if (mContext instanceof Activity) {
-            mTextAssistant = ((Activity) mContext).getTextAssistant();
-        } else {
-            // The context of this TextView should be an Activity. If it is not and no
-            // text assistant has been set, return the TextClassificationManager.
-            mTextAssistant = mContext.getSystemService(TextClassificationManager.class);
-        }
-        return  mTextAssistant;
+        return mTextClassifier;
     }
 
     /**
@@ -10470,6 +10613,14 @@
      * @hide
      */
     protected void viewClicked(InputMethodManager imm) {
+        final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class);
+        if (afm != null) {
+            if (DEBUG_AUTOFILL) Log.v(LOG_TAG, "viewClicked(): id=" + getAutoFillViewId());
+
+            // TODO(b/33197203): integrate with onFocus and/or move to view?
+            afm.updateAutoFillInput(this, AutoFillManager.FLAG_UPDATE_UI_SHOW);
+        }
+
         if (imm != null) {
             imm.viewClicked(this);
         }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index d734d17..ab1d9b9 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -749,10 +749,7 @@
                     }
                 } else {
                     try {
-                        AppGlobals.getPackageManager().setLastChosenActivity(intent,
-                                intent.resolveType(getContentResolver()),
-                                PackageManager.MATCH_DEFAULT_ONLY,
-                                filter, bestMatch, intent.getComponent());
+                        mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch);
                     } catch (RemoteException re) {
                         Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
                     }
@@ -1312,10 +1309,7 @@
         protected boolean rebuildList() {
             List<ResolvedComponentInfo> currentResolveList = null;
             try {
-                final Intent primaryIntent = getTargetIntent();
-                mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
-                        primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()),
-                        PackageManager.MATCH_DEFAULT_ONLY);
+                mLastChosen = mResolverListController.getLastChosen();
             } catch (RemoteException re) {
                 Log.d(TAG, "Error calling getLastChosenActivity\n" + re);
             }
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index d9ab47e..096fcb8 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -343,53 +343,42 @@
     class LogisticRegressionAppRanker {
         private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
         private static final String BIAS_PREF_KEY = "bias";
-        private static final float LEARNING_RATE = 0.02f;
-        private static final float REGULARIZER_PARAM = 0.1f;
+        private static final String VERSION_PREF_KEY = "version";
+
+        // parameters for a pre-trained model, to initialize the app ranker. When updating the
+        // pre-trained model, please update these params, as well as initModel().
+        private static final int CURRENT_VERSION = 1;
+        private static final float LEARNING_RATE = 0.0001f;
+        private static final float REGULARIZER_PARAM = 0.0001f;
+
         private SharedPreferences mParamSharedPref;
         private ArrayMap<String, Float> mFeatureWeights;
         private float mBias;
 
         public LogisticRegressionAppRanker(Context context) {
             mParamSharedPref = getParamSharedPref(context);
+            initModel();
         }
 
         public float predict(ArrayMap<String, Float> target) {
-            if (target == null || mParamSharedPref == null) {
+            if (target == null) {
                 return 0.0f;
             }
             final int featureSize = target.size();
-            if (featureSize == 0) {
-                return 0.0f;
-            }
             float sum = 0.0f;
-            if (mFeatureWeights == null) {
-                mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
-                mFeatureWeights = new ArrayMap<>(featureSize);
-                for (int i = 0; i < featureSize; i++) {
-                    String featureName = target.keyAt(i);
-                    float weight = mParamSharedPref.getFloat(featureName, 0.0f);
-                    sum += weight * target.valueAt(i);
-                    mFeatureWeights.put(featureName, weight);
-                }
-            } else {
-                for (int i = 0; i < featureSize; i++) {
-                    String featureName = target.keyAt(i);
-                    float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
-                    sum += weight * target.valueAt(i);
-                }
+            for (int i = 0; i < featureSize; i++) {
+                String featureName = target.keyAt(i);
+                float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+                sum += weight * target.valueAt(i);
             }
             return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
         }
 
         public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
-            if (target == null || target.size() == 0) {
+            if (target == null) {
                 return;
             }
             final int featureSize = target.size();
-            if (mFeatureWeights == null) {
-                mBias = 0.0f;
-                mFeatureWeights = new ArrayMap<>(featureSize);
-            }
             float error = isSelected ? 1.0f - predict : -predict;
             for (int i = 0; i < featureSize; i++) {
                 String featureName = target.keyAt(i);
@@ -405,15 +394,13 @@
         }
 
         public void commitUpdate() {
-            if (mFeatureWeights == null || mFeatureWeights.size() == 0) {
-                return;
-            }
             SharedPreferences.Editor editor = mParamSharedPref.edit();
             editor.putFloat(BIAS_PREF_KEY, mBias);
             final int size = mFeatureWeights.size();
             for (int i = 0; i < size; i++) {
                 editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
             }
+            editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
             editor.apply();
         }
 
@@ -431,5 +418,27 @@
                     PARAM_SHARED_PREF_NAME + ".xml");
             return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
         }
+
+        private void initModel() {
+            mFeatureWeights = new ArrayMap<>(4);
+            if (mParamSharedPref == null ||
+                    mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
+                // Initializing the app ranker to a pre-trained model. When updating the pre-trained
+                // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
+                // REGULARIZER_PARAM.
+                mBias = -1.6568f;
+                mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
+                mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
+                mFeatureWeights.put(RECENCY_SCORE, 0.269f);
+                mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
+            } else {
+                mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
+                mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
+                mFeatureWeights.put(
+                        TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
+                mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
+                mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
+            }
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index f88f6f9..4071ff4 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -19,13 +19,16 @@
 
 import android.annotation.WorkerThread;
 import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
 import android.util.Log;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -66,6 +69,22 @@
     }
 
     @VisibleForTesting
+    public ResolveInfo getLastChosen() throws RemoteException {
+        return AppGlobals.getPackageManager().getLastChosenActivity(
+                mTargetIntent, mTargetIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                PackageManager.MATCH_DEFAULT_ONLY);
+    }
+
+    @VisibleForTesting
+    public void setLastChosen(Intent intent, IntentFilter filter, int match)
+            throws RemoteException {
+        AppGlobals.getPackageManager().setLastChosenActivity(intent,
+                intent.resolveType(mContext.getContentResolver()),
+                PackageManager.MATCH_DEFAULT_ONLY,
+                filter, match, intent.getComponent());
+    }
+
+    @VisibleForTesting
     public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent(
             boolean shouldGetResolvedFilter,
             boolean shouldGetActivityMetadata,
diff --git a/core/java/com/android/internal/font/IFontManager.aidl b/core/java/com/android/internal/font/IFontManager.aidl
new file mode 100644
index 0000000..52a6262
--- /dev/null
+++ b/core/java/com/android/internal/font/IFontManager.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.font;
+
+import android.text.FontConfig;
+
+/**
+ * Interface to the font manager.
+ * @hide
+ */
+interface IFontManager {
+    FontConfig getSystemFonts();
+}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 8d11783..a94b161 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -95,39 +95,32 @@
             }
         }
 
+        private static int compareNullableCharSequences(@Nullable CharSequence c1,
+                @Nullable CharSequence c2) {
+            // For historical reasons, an empty text needs to put at the last.
+            final boolean empty1 = TextUtils.isEmpty(c1);
+            final boolean empty2 = TextUtils.isEmpty(c2);
+            if (empty1 || empty2) {
+                return (empty1 ? 1 : 0) - (empty2 ? 1 : 0);
+            }
+            return c1.toString().compareTo(c2.toString());
+        }
+
         @Override
         public int compareTo(ImeSubtypeListItem other) {
-            if (TextUtils.isEmpty(mImeName)) {
-                return 1;
+            int result = compareNullableCharSequences(mImeName, other.mImeName);
+            if (result != 0) {
+                return result;
             }
-            if (TextUtils.isEmpty(other.mImeName)) {
-                return -1;
+            result = compareNullableCharSequences(mSubtypeName, other.mSubtypeName);
+            if (result != 0) {
+                return result;
             }
-            if (!TextUtils.equals(mImeName, other.mImeName)) {
-                return mImeName.toString().compareTo(other.mImeName.toString());
+            result = (mIsSystemLocale ? -1 : 0) - (other.mIsSystemLocale ? -1 : 0);
+            if (result != 0) {
+                return result;
             }
-            if (TextUtils.equals(mSubtypeName, other.mSubtypeName)) {
-                return 0;
-            }
-            if (mIsSystemLocale) {
-                return -1;
-            }
-            if (other.mIsSystemLocale) {
-                return 1;
-            }
-            if (mIsSystemLanguage) {
-                return -1;
-            }
-            if (other.mIsSystemLanguage) {
-                return 1;
-            }
-            if (TextUtils.isEmpty(mSubtypeName)) {
-                return 1;
-            }
-            if (TextUtils.isEmpty(other.mSubtypeName)) {
-                return -1;
-            }
-            return mSubtypeName.toString().compareTo(other.mSubtypeName.toString());
+            return (mIsSystemLanguage ? -1 : 0) - (other.mIsSystemLanguage ? -1 : 0);
         }
 
         @Override
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 716997f..c08cd72 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -1080,22 +1080,6 @@
             return enabledSubtypes;
         }
 
-        // At the initial boot, the settings for input methods are not set,
-        // so we need to enable IME in that case.
-        public void enableAllIMEsIfThereIsNoEnabledIME() {
-            if (TextUtils.isEmpty(getEnabledInputMethodsStr())) {
-                StringBuilder sb = new StringBuilder();
-                final int N = mMethodList.size();
-                for (int i = 0; i < N; i++) {
-                    InputMethodInfo imi = mMethodList.get(i);
-                    Slog.i(TAG, "Adding: " + imi.getId());
-                    if (i > 0) sb.append(':');
-                    sb.append(imi.getId());
-                }
-                putEnabledInputMethodsStr(sb.toString());
-            }
-        }
-
         public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
             return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(),
                     mInputMethodSplitter,
diff --git a/core/java/com/android/internal/logging/LogBuilder.java b/core/java/com/android/internal/logging/LogBuilder.java
deleted file mode 100644
index 8e2e114..0000000
--- a/core/java/com/android/internal/logging/LogBuilder.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.android.internal.logging;
-
-import android.util.EventLog;
-import android.util.SparseArray;
-import android.view.View;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-
-/**
- * Helper class to assemble more complex logs.
- *
- * @hide
- */
-
-public class LogBuilder {
-
-    private SparseArray<Object> entries = new SparseArray();
-
-    public LogBuilder(int mainCategory) {
-        setCategory(mainCategory);
-    }
-
-    public LogBuilder setView(View view) {
-        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_VIEW, view.getId());
-        return this;
-    }
-
-    public LogBuilder setCategory(int category) {
-        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_CATEGORY, category);
-        return this;
-    }
-
-    public LogBuilder setType(int type) {
-        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_TYPE, type);
-        return this;
-    }
-
-    /**
-     * @param tag From your MetricsEvent enum.
-     * @param value One of Integer, Long, Float, String
-     * @return
-     */
-    public LogBuilder addTaggedData(int tag, Object value) {
-        if (!(value instanceof Integer ||
-            value instanceof String ||
-            value instanceof Long ||
-            value instanceof Float)) {
-            throw new IllegalArgumentException(
-                    "Value must be loggable type - int, long, float, String");
-        }
-        entries.put(tag, value);
-        return this;
-    }
-
-    /**
-     * Assemble logs into structure suitable for EventLog.
-     */
-    public Object[] serialize() {
-        Object[] out = new Object[entries.size() * 2];
-        for (int i = 0; i < entries.size(); i++) {
-            out[i * 2] = entries.keyAt(i);
-            out[i * 2 + 1] = entries.valueAt(i);
-        }
-        return out;
-    }
-}
-
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 5eb39ae..c8bf302 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -16,13 +16,12 @@
 package com.android.internal.logging;
 
 import android.content.Context;
+import android.metrics.LogMaker;
 import android.os.Build;
-import android.util.EventLog;
 import android.view.View;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-
 /**
  * Log all the things.
  *
@@ -38,6 +37,10 @@
             throw new IllegalArgumentException("Must define metric category");
         }
         EventLogTags.writeSysuiViewVisibility(category, 100);
+        EventLogTags.writeSysuiMultiAction(
+                new LogMaker(category)
+                        .setType(MetricsEvent.TYPE_OPEN)
+                        .serialize());
     }
 
     public static void hidden(Context context, int category) throws IllegalArgumentException {
@@ -45,6 +48,10 @@
             throw new IllegalArgumentException("Must define metric category");
         }
         EventLogTags.writeSysuiViewVisibility(category, 0);
+        EventLogTags.writeSysuiMultiAction(
+                new LogMaker(category)
+                        .setType(MetricsEvent.TYPE_CLOSE)
+                        .serialize());
     }
 
     public static void visibility(Context context, int category, boolean visibile)
@@ -62,21 +69,35 @@
     }
 
     public static void action(Context context, int category) {
-        action(context, category, "");
+        EventLogTags.writeSysuiAction(category, "");
+        EventLogTags.writeSysuiMultiAction(
+                new LogMaker(category)
+                        .setType(MetricsEvent.TYPE_ACTION)
+                        .serialize());
     }
 
     public static void action(Context context, int category, int value) {
-        action(context, category, Integer.toString(value));
+        EventLogTags.writeSysuiAction(category, Integer.toString(value));
+        EventLogTags.writeSysuiMultiAction(
+                new LogMaker(category)
+                        .setType(MetricsEvent.TYPE_ACTION)
+                        .setSubtype(value)
+                        .serialize());
     }
 
     public static void action(Context context, int category, boolean value) {
-        action(context, category, Boolean.toString(value));
+        EventLogTags.writeSysuiAction(category, Boolean.toString(value));
+        EventLogTags.writeSysuiMultiAction(
+                new LogMaker(category)
+                        .setType(MetricsEvent.TYPE_ACTION)
+                        .setSubtype(value ? 1 : 0)
+                        .serialize());
     }
 
-    public static void action(LogBuilder content) {
-        //EventLog.writeEvent(524292, content.serialize());
-        // Below would be the *right* way to do this, using the generated
-        // EventLogTags method, but that doesn't work.
+    public static void action(LogMaker content) {
+        if (content.getType() == MetricsEvent.TYPE_UNKNOWN) {
+            content.setType(MetricsEvent.TYPE_ACTION);
+        }
         EventLogTags.writeSysuiMultiAction(content.serialize());
     }
 
@@ -86,15 +107,30 @@
             throw new IllegalArgumentException("Must define metric category");
         }
         EventLogTags.writeSysuiAction(category, pkg);
+        EventLogTags.writeSysuiMultiAction(new LogMaker(category)
+                .setType(MetricsEvent.TYPE_ACTION)
+                .setPackageName(pkg)
+                .serialize());
     }
 
     /** Add an integer value to the monotonically increasing counter with the given name. */
     public static void count(Context context, String name, int value) {
         EventLogTags.writeSysuiCount(name, value);
+        EventLogTags.writeSysuiMultiAction(
+                new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
+                        .setCounterName(name)
+                        .setCounterValue(value)
+                        .serialize());
     }
 
     /** Increment the bucket with the integer label on the histogram with the given name. */
     public static void histogram(Context context, String name, int bucket) {
         EventLogTags.writeSysuiHistogram(name, bucket);
+        EventLogTags.writeSysuiMultiAction(
+                new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
+                        .setCounterName(name)
+                        .setCounterBucket(bucket)
+                        .setCounterValue(1)
+                        .serialize());
     }
 }
diff --git a/core/java/com/android/internal/logging/legacy/CounterParser.java b/core/java/com/android/internal/logging/legacy/CounterParser.java
new file mode 100644
index 0000000..f318503
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/CounterParser.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+/**
+ * Parse the Android counter event logs.
+ * @hide
+ */
+public class CounterParser extends TagParser {
+    private static final String TAG = "CounterParser";
+    private static final int EVENTLOG_TAG = 524290;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length >= 2) {
+            try {
+                String name = ((String) operands[0]);
+                int value = (Integer) operands[1];
+                logCount(logger, name, value);
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.d(TAG, "unexpected operand type", e);
+                }
+            }
+        } else if (debug) {
+            Log.d(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+
+    protected void logCount(TronLogger logger, String name, int value) {
+        logger.incrementBy(TronCounters.TRON_AOSP_PREFIX + name, value);
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/EventLogCollector.java b/core/java/com/android/internal/logging/legacy/EventLogCollector.java
new file mode 100644
index 0000000..eba7d0f
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/EventLogCollector.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Scan the event log for interaction metrics events.
+ * @hide
+ */
+public class EventLogCollector {
+    private static final String TAG = "EventLogCollector";
+
+    // TODO replace this with GoogleLogTags.TRON_HEARTBEAT
+    @VisibleForTesting
+    static final int TRON_HEARTBEAT = 208000;
+
+    private static EventLogCollector sInstance;
+
+    private final ArrayMap<Integer, TagParser> mTagParsers;
+    private int[] mInterestingTags;
+
+    private LogReader mLogReader;
+
+    private EventLogCollector() {
+        mTagParsers = new ArrayMap<>();
+        addParser(new SysuiViewVisibilityParser());
+        addParser(new SysuiActionParser());
+        addParser(new SysuiQueryParser());
+        addParser(new NotificationPanelRevealedParser());
+        addParser(new NotificationPanelHiddenParser());
+        addParser(new NotificationClickedParser());
+        addParser(new NotificationActionClickedParser());
+        addParser(new NotificationCanceledParser());
+        addParser(new NotificationVisibilityParser());
+        addParser(new NotificationAlertParser());
+        addParser(new NotificationExpansionParser());
+        addParser(new CounterParser());
+        addParser(new HistogramParser());
+        addParser(new LockscreenGestureParser());
+        addParser(new StatusBarStateParser());
+        addParser(new PowerScreenStateParser());
+        addParser(new SysuiMultiActionParser());
+
+        mLogReader = new LogReader();
+    }
+
+    public static EventLogCollector getInstance() {
+        if (sInstance == null) {
+            sInstance = new EventLogCollector();
+        }
+        return sInstance;
+    }
+
+    @VisibleForTesting
+    public void setLogReader(LogReader logReader) {
+        mLogReader = logReader;
+    }
+
+    private int[] getInterestingTags() {
+        if (mInterestingTags == null) {
+            mInterestingTags = new int[mTagParsers.size()];
+            for (int i = 0; i < mTagParsers.size(); i++) {
+                mInterestingTags[i] = mTagParsers.valueAt(i).getTag();
+            }
+        }
+        return mInterestingTags;
+    }
+
+    // I would customize ArrayMap to add put(TagParser), but ArrayMap is final.
+    @VisibleForTesting
+    void addParser(TagParser parser) {
+        mTagParsers.put(parser.getTag(), parser);
+        mInterestingTags = null;
+    }
+
+    public void collect(LegacyConversionLogger logger) {
+        collect(logger, 0L);
+    }
+
+    public long collect(TronLogger logger, long lastSeenEventMs) {
+        long lastEventMs = 0L;
+        final boolean debug = Util.debug();
+
+        if (debug) {
+            Log.d(TAG, "Eventlog Collection");
+        }
+        ArrayList<Event> events = new ArrayList<>();
+        try {
+            mLogReader.readEvents(getInterestingTags(), events);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        if (debug) {
+            Log.d(TAG, "read this many events: " + events.size());
+        }
+
+        for (Event event : events) {
+            final long millis = event.getTimeNanos() / 1000000;
+            if (millis > lastSeenEventMs) {
+                final int tag = event.getTag();
+                TagParser parser = mTagParsers.get(tag);
+                if (parser == null) {
+                    if (debug) {
+                        Log.d(TAG, "unknown tag: " + tag);
+                    }
+                    continue;
+                }
+                if (debug) {
+                    Log.d(TAG, "parsing tag: " + tag);
+                }
+                parser.parseEvent(logger, event);
+                lastEventMs = Math.max(lastEventMs, millis);
+            } else {
+                if (debug) {
+                    Log.d(TAG, "old event: " + millis + " < " + lastSeenEventMs);
+                }
+            }
+        }
+        return lastEventMs;
+    }
+
+    @VisibleForTesting
+    static class Event {
+        long mTimeNanos;
+        int mTag;
+        Object mData;
+
+        Event(long timeNanos, int tag, Object data) {
+            super();
+            mTimeNanos = timeNanos;
+            mTag = tag;
+            mData = data;
+        }
+
+        Event(EventLog.Event event) {
+            mTimeNanos = event.getTimeNanos();
+            mTag = event.getTag();
+            mData = event.getData();
+        }
+
+        public long getTimeNanos() {
+            return mTimeNanos;
+        }
+
+        public int getTag() {
+            return mTag;
+        }
+
+        public Object getData() {
+            return mData;
+        }
+    }
+
+    @VisibleForTesting
+    static class LogReader {
+        public void readEvents(int[] tags, Collection<Event> events) throws IOException {
+            // Testing in Android: the Static Final Class Strikes Back!
+            ArrayList<EventLog.Event> nativeEvents = new ArrayList<>();
+            EventLog.readEventsOnWrapping(tags, 0L, nativeEvents);
+            for (EventLog.Event nativeEvent : nativeEvents) {
+                Event event = new Event(nativeEvent);
+                events.add(event);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/HistogramParser.java b/core/java/com/android/internal/logging/legacy/HistogramParser.java
new file mode 100644
index 0000000..bb7e75c
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/HistogramParser.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+/**
+ * Parse the Android histogram event logs.
+ * @hide
+ */
+public class HistogramParser extends CounterParser {
+    private static final String TAG = "HistogramParser";
+    private static final int EVENTLOG_TAG = 524291;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    protected void logCount(TronLogger logger, String name, int value) {
+        logger.incrementIntHistogram("tron_varz_" + name, value);
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java b/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java
new file mode 100644
index 0000000..91e968b
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/** @hide */
+public class LegacyConversionLogger implements TronLogger {
+    public static final String VIEW_KEY = "view";
+    public static final String TYPE_KEY = "type";
+    public static final String EVENT_KEY = "data";
+
+    public static final int TYPE_COUNTER = 1;
+    public static final int TYPE_HISTOGRAM = 2;
+    public static final int TYPE_EVENT = 3;
+
+    private final Queue<LogMaker> mQueue;
+    private HashMap<String, Boolean> mConfig;
+
+    public LegacyConversionLogger() {
+        mQueue = new LinkedList<>();
+    }
+
+    public Queue<LogMaker> getEvents() {
+        return mQueue;
+    }
+
+    @Override
+    public void increment(String counterName) {
+        LogMaker b = new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
+                .setCounterName(counterName)
+                .setCounterValue(1);
+        mQueue.add(b);
+    }
+
+    @Override
+    public void incrementBy(String counterName, int value) {
+        LogMaker b = new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
+                .setCounterName(counterName)
+                .setCounterValue(value);
+        mQueue.add(b);
+    }
+
+    @Override
+    public void incrementIntHistogram(String counterName, int bucket) {
+        LogMaker b = new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
+                .setCounterName(counterName)
+                .setCounterBucket(bucket)
+                .setCounterValue(1);
+        mQueue.add(b);
+    }
+
+    @Override
+    public void incrementLongHistogram(String counterName, long bucket) {
+        LogMaker b = new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
+                .setCounterName(counterName)
+                .setCounterBucket(bucket)
+                .setCounterValue(1);
+        mQueue.add(b);
+    }
+
+    @Override
+    public LogMaker obtain() {
+        return new LogMaker(MetricsEvent.VIEW_UNKNOWN);
+    }
+
+    @Override
+    public void dispose(LogMaker proto) {
+    }
+
+    @Override
+    public void addEvent(LogMaker proto) {
+        mQueue.add(proto);
+    }
+
+    @Override
+    public boolean getConfig(String configName) {
+        if (mConfig != null && mConfig.containsKey(configName)) {
+            return mConfig.get(configName);
+        }
+        return false;
+    }
+
+    @Override
+    public void setConfig(String configName, boolean newValue) {
+        if (mConfig == null) {
+            mConfig = new HashMap<>();
+        }
+        mConfig.put(configName, newValue);
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/LockscreenGestureParser.java b/core/java/com/android/internal/logging/legacy/LockscreenGestureParser.java
new file mode 100644
index 0000000..df08ee0
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/LockscreenGestureParser.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android lockscreen gesture logs.
+ * @hide
+ */
+public class LockscreenGestureParser extends TagParser {
+    private static final String TAG = "LockscreenGestureParser";
+    private static final int EVENTLOG_TAG = 36021;
+
+    // source of truth is com.android.systemui.EventLogConstants
+    public static final int[] GESTURE_TYPE_MAP = {
+            MetricsEvent.VIEW_UNKNOWN,  // there is no type 0
+            MetricsEvent.ACTION_LS_UNLOCK,  // SYSUI_LOCKSCREEN_GESTURE_SWIPE_UP_UNLOCK = 1
+            MetricsEvent.ACTION_LS_SHADE,  // SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_FULL_SHADE = 2
+            MetricsEvent.ACTION_LS_HINT,  // SYSUI_LOCKSCREEN_GESTURE_TAP_UNLOCK_HINT = 3
+            MetricsEvent.ACTION_LS_CAMERA,  // SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA = 4
+            MetricsEvent.ACTION_LS_DIALER,  // SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER = 5
+            MetricsEvent.ACTION_LS_LOCK,  // SYSUI_LOCKSCREEN_GESTURE_TAP_LOCK = 6
+            MetricsEvent.ACTION_LS_NOTE,  // SYSUI_LOCKSCREEN_GESTURE_TAP_NOTIFICATION_ACTIVATE = 7
+            MetricsEvent.ACTION_LS_QS,  // SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_QS = 8
+            MetricsEvent.ACTION_SHADE_QS_PULL,  // SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS = 9
+            MetricsEvent.ACTION_SHADE_QS_TAP  // SYSUI_TAP_TO_OPEN_QS = 10
+    };
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length >= 1) {
+            try {
+                int type = ((Integer) operands[0]).intValue();
+                // ignore gesture length in operands[1]
+                // ignore gesture velocity in operands[2]
+
+                int category = MetricsEvent.VIEW_UNKNOWN;
+                if (type < GESTURE_TYPE_MAP.length) {
+                    category = GESTURE_TYPE_MAP[type];
+                }
+                if (category != MetricsEvent.VIEW_UNKNOWN) {
+                    LogMaker proto = logger.obtain();
+                    proto.setCategory(category);
+                    proto.setType(MetricsEvent.TYPE_ACTION);
+                    proto.setTimestamp(eventTimeMs);
+                    logger.addEvent(proto);
+                }
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationActionClickedParser.java b/core/java/com/android/internal/logging/legacy/NotificationActionClickedParser.java
new file mode 100644
index 0000000..79f3eb8
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationActionClickedParser.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification action button interaction event logs.
+ * @hide
+ */
+public class NotificationActionClickedParser extends TagParser {
+    private static final String TAG = "NotificationAction";
+    private static final int EVENTLOG_TAG = 27521;
+
+    private final NotificationKey mKey;
+
+    public NotificationActionClickedParser() {
+        mKey = new NotificationKey();
+    }
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length > 1) {
+            try {
+                if (mKey.parse((String) operands[0])) {
+                    int index = (Integer) operands[1];
+                    parseTimes(operands, 2);
+                    LogMaker proto = logger.obtain();
+                    proto.setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION);
+                    proto.setType(MetricsEvent.TYPE_ACTION);
+                    proto.setSubtype(index);
+                    proto.setTimestamp(eventTimeMs);
+                    proto.setPackageName(mKey.mPackageName);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+                    filltimes(proto);
+                    logger.addEvent(proto);
+                } else if (debug) {
+                    Log.e(TAG, "unable to parse key.");
+                }
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationAlertParser.java b/core/java/com/android/internal/logging/legacy/NotificationAlertParser.java
new file mode 100644
index 0000000..9548fb0
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationAlertParser.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the new Android notification alert event logs.
+ * @hide
+ */
+public class NotificationAlertParser extends TagParser {
+    private static final String TAG = "NotificationAlertParser";
+    private static final int EVENTLOG_TAG = 27532;
+
+    @VisibleForTesting
+    static final int BUZZ = 0x00000001;
+    @VisibleForTesting
+    static final int BEEP = 0x00000002;
+    @VisibleForTesting
+    static final int BLINK = 0x00000004;
+
+    private final NotificationKey mKey;
+
+    public NotificationAlertParser() {
+        mKey = new NotificationKey();
+    }
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length > 3) {
+            try {
+                final String keyString = (String) operands[0];
+                final boolean buzz = ((Integer) operands[1]) == 1;
+                final boolean beep = ((Integer) operands[2]) == 1;
+                final boolean blink = ((Integer) operands[3]) == 1;
+
+                if (mKey.parse(keyString)) {
+                    LogMaker proto = logger.obtain();
+                    proto.setCategory(MetricsEvent.NOTIFICATION_ALERT);
+                    proto.setType(MetricsEvent.TYPE_OPEN);
+                    proto.setSubtype((buzz ? BUZZ : 0) | (beep ? BEEP : 0) | (blink ? BLINK : 0));
+                    proto.setTimestamp(eventTimeMs);
+                    proto.setPackageName(mKey.mPackageName);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+                    filltimes(proto);
+                    logger.addEvent(proto);
+                } else {
+                    if (debug) {
+                        Log.e(TAG, "unable to parse key: " + keyString);
+                    }
+                }
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+                return;
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationCanceledParser.java b/core/java/com/android/internal/logging/legacy/NotificationCanceledParser.java
new file mode 100644
index 0000000..80eb004
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationCanceledParser.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification cancellation event logs.
+ * @hide
+ */
+public class NotificationCanceledParser extends TagParser {
+    private static final String TAG = "NotificationCanceled";
+    private static final int EVENTLOG_TAG = 27530;
+
+    // from com.android.server.notification.NotificationManagerService
+    static final int REASON_DELEGATE_CLICK = 1;
+    static final int REASON_DELEGATE_CANCEL = 2;
+    static final int REASON_DELEGATE_CANCEL_ALL = 3;
+    static final int REASON_PACKAGE_BANNED = 7;
+    static final int REASON_LISTENER_CANCEL = 10;
+    static final int REASON_LISTENER_CANCEL_ALL = 11;
+
+    private final NotificationKey mKey;
+
+    public NotificationCanceledParser() {
+        mKey = new NotificationKey();
+    }
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length > 1) {
+            try {
+                final String keyString = (String) operands[0];
+                final int reason = (Integer) operands[1];
+                parseTimes(operands, 2);
+
+                // handle old style log
+                // TODO: delete once M is launched
+                if (operands.length < 5) {
+                    mSinceVisibleMillis = mSinceUpdateMillis;
+                    mSinceUpdateMillis = 0;
+                }
+
+                boolean intentional = true;
+                switch (reason) {
+                    case REASON_DELEGATE_CANCEL:
+                    case REASON_DELEGATE_CANCEL_ALL:
+                    case REASON_LISTENER_CANCEL:
+                    case REASON_LISTENER_CANCEL_ALL:
+                    case REASON_DELEGATE_CLICK:
+                    case REASON_PACKAGE_BANNED:
+                        break;
+                    default:
+                        intentional = false;
+                }
+
+                if (mKey.parse(keyString)) {
+                    if (intentional) {
+                        LogMaker proto = logger.obtain();
+                        proto.setCategory(MetricsEvent.NOTIFICATION_ITEM);
+                        proto.setType(MetricsEvent.TYPE_DISMISS);
+                        proto.setSubtype(reason);
+                        proto.setTimestamp(eventTimeMs);
+                        proto.setPackageName(mKey.mPackageName);
+                        proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+                        proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+                        filltimes(proto);
+                        logger.addEvent(proto);
+                    }
+                } else if (debug) {
+                    Log.e(TAG, "unable to parse key: " + keyString);
+                }
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationClickedParser.java b/core/java/com/android/internal/logging/legacy/NotificationClickedParser.java
new file mode 100644
index 0000000..eee4701
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationClickedParser.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification interaction event logs.
+ * @hide
+ */
+public class NotificationClickedParser extends TagParser {
+    private static final String TAG = "NotificationClicked";
+    private static final int EVENTLOG_TAG = 27520;
+
+    private final NotificationKey mKey;
+
+    public NotificationClickedParser() {
+        mKey = new NotificationKey();
+    }
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length > 0) {
+            try {
+                if (mKey.parse((String) operands[0])) {
+                    parseTimes(operands, 1);
+                    LogMaker proto = logger.obtain();
+                    proto.setCategory(MetricsEvent.NOTIFICATION_ITEM);
+                    proto.setType(MetricsEvent.TYPE_ACTION);
+                    proto.setTimestamp(eventTimeMs);
+                    proto.setPackageName(mKey.mPackageName);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+                    filltimes(proto);
+                    logger.addEvent(proto);
+                } else if (debug) {
+                    Log.e(TAG, "unable to parse key.");
+                }
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationExpansionParser.java b/core/java/com/android/internal/logging/legacy/NotificationExpansionParser.java
new file mode 100644
index 0000000..84cd999
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationExpansionParser.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification expansion event logs.
+ * @hide
+ */
+public class NotificationExpansionParser extends TagParser {
+    private static final String TAG = "NotificationExpansion";
+    private static final int EVENTLOG_TAG = 27511;
+
+    private final NotificationKey mKey;
+
+    public NotificationExpansionParser() {
+        mKey = new NotificationKey();
+    }
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length > 2) {
+            try {
+                if (mKey.parse((String) operands[0])) {
+                    boolean byUser = ((Integer) operands[1]) == 1;
+                    boolean expanded = ((Integer) operands[2]) == 1;
+                    parseTimes(operands, 3);
+
+                    if (!byUser || !expanded) {
+                        return;
+                    }
+                    LogMaker proto = logger.obtain();
+                    proto.setCategory(MetricsEvent.NOTIFICATION_ITEM);
+                    proto.setType(MetricsEvent.TYPE_DETAIL);
+                    proto.setTimestamp(eventTimeMs);
+                    proto.setPackageName(mKey.mPackageName);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+                    filltimes(proto);
+                    logger.addEvent(proto);
+                } else if (debug) {
+                    Log.e(TAG, "unable to parse key.");
+                }
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationKey.java b/core/java/com/android/internal/logging/legacy/NotificationKey.java
new file mode 100644
index 0000000..f8cac34
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationKey.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+/**
+ * Parse Android notification keys
+ * @hide
+ */
+public class NotificationKey {
+
+    private static final String TAG = "NotificationKey";
+
+    public int mUser;
+    public String mPackageName;
+    public int mId;
+    public String mTag;
+    public int mUid;
+
+    public boolean parse(String key) {
+        if (key == null) {
+            return false;
+        }
+        boolean debug = Util.debug();
+        String[] parts = key.split("\\|");
+        if (parts.length == 5) {
+            try {
+                mUser = Integer.valueOf(parts[0]);
+                mPackageName = parts[1];
+                mId = Integer.valueOf(parts[2]);
+                mTag = parts[3].equals("null") ? "" : parts[3];
+                mUid = Integer.valueOf(parts[4]);
+                return true;
+            } catch (NumberFormatException e) {
+                if (debug) {
+                    Log.w(TAG, "could not parse notification key.", e);
+                }
+                return false;
+            }
+        }
+        if (debug) {
+            Log.w(TAG, "wrong number of parts in notification key: " + key);
+        }
+        return false;
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationPanelHiddenParser.java b/core/java/com/android/internal/logging/legacy/NotificationPanelHiddenParser.java
new file mode 100644
index 0000000..a064a2e
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationPanelHiddenParser.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification panel visibility event logs.
+ * @hide
+ */
+public class NotificationPanelHiddenParser extends TagParser {
+    private static final String TAG = "NotificationPanelHidden";
+    private static final int EVENTLOG_TAG = 27501;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        LogMaker proto = logger.obtain();
+        proto.setCategory(MetricsEvent.NOTIFICATION_PANEL);
+        proto.setType(MetricsEvent.TYPE_CLOSE);
+        proto.setTimestamp(eventTimeMs);
+        logger.addEvent(proto);
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationPanelRevealedParser.java b/core/java/com/android/internal/logging/legacy/NotificationPanelRevealedParser.java
new file mode 100644
index 0000000..4d19564
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationPanelRevealedParser.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification panel visibility event logs.
+ * @hide
+ */
+public class NotificationPanelRevealedParser extends TagParser {
+    private static final String TAG = "NotificationPanelRevea";
+    private static final int EVENTLOG_TAG = 27500;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length >= 1) {
+            try {
+                int load = ((Integer) operands[0]).intValue();
+                //logger.incrementBy(TronCounters.TRON_NOTIFICATION_LOAD, load);
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        }
+
+        LogMaker proto = logger.obtain();
+        proto.setCategory(MetricsEvent.NOTIFICATION_PANEL);
+        proto.setType(MetricsEvent.TYPE_OPEN);
+        proto.setTimestamp(eventTimeMs);
+        logger.addEvent(proto);
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationVisibilityParser.java b/core/java/com/android/internal/logging/legacy/NotificationVisibilityParser.java
new file mode 100644
index 0000000..2d2cd909
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationVisibilityParser.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the new Android notification visibility event logs.
+ * @hide
+ */
+public class NotificationVisibilityParser extends TagParser {
+    private static final String TAG = "NotificationVisibility";
+    private static final int EVENTLOG_TAG = 27531;
+
+    private final NotificationKey mKey;
+
+    public NotificationVisibilityParser() {
+        mKey = new NotificationKey();
+    }
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length > 1) {
+            try {
+                final String keyString = (String) operands[0];
+                final boolean visible = ((Integer) operands[1]) == 1;
+                parseTimes(operands, 2);
+                int index = 0;
+                if (operands.length > 5 && operands[5] instanceof Integer) {
+                    index = (Integer) operands[5];
+                }
+
+                if (mKey.parse(keyString)) {
+                    LogMaker proto = logger.obtain();
+                    proto.setCategory(MetricsEvent.NOTIFICATION_ITEM);
+                    proto.setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE);
+                    proto.setTimestamp(eventTimeMs);
+                    proto.setPackageName(mKey.mPackageName);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+                    proto.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, index);
+                    filltimes(proto);
+                    logger.addEvent(proto);
+                } else {
+                    if (debug) {
+                        Log.e(TAG, "unable to parse key: " + keyString);
+                    }
+                }
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+                return;
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java b/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java
new file mode 100644
index 0000000..e9baf9b
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android lockscreen gesture logs.
+ * @hide
+ */
+public class PowerScreenStateParser extends TagParser {
+    private static final String TAG = "PowerScreenStateParser";
+    private static final int EVENTLOG_TAG = 2728;
+
+    // source of truth is android.view.WindowManagerPolicy, why:
+    // 0: on
+    // 1: OFF_BECAUSE_OF_ADMIN
+    // 2: OFF_BECAUSE_OF_USER
+    // 3: OFF_BECAUSE_OF_TIMEOUT
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length >= 2) {
+            try {
+                // (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)
+                boolean state = (((Integer) operands[0]).intValue()) == 1;
+                int why = ((Integer) operands[1]).intValue();
+
+                LogMaker proto = logger.obtain();
+                proto.setCategory(MetricsEvent.SCREEN);
+                proto.setType(state ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE);
+                proto.setTimestamp(eventTimeMs);
+                proto.setSubtype(why);
+                logger.addEvent(proto);
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/StatusBarStateParser.java b/core/java/com/android/internal/logging/legacy/StatusBarStateParser.java
new file mode 100644
index 0000000..226253f
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/StatusBarStateParser.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android lockscreen gesture logs.
+ * @hide
+ */
+public class StatusBarStateParser extends TagParser {
+    private static final String TAG = "StatusBarStateParser";
+    private static final int EVENTLOG_TAG = 36004;
+
+    // source of truth is com.android.systemui.statusbar.StatusBarState
+    public static final int SHADE = 0;
+    public static final int KEYGUARD = 1;
+    public static final int SHADE_LOCKED = 2;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length >= 6) {
+            try {
+                // [state, isShowing, isOccluded, isBouncerShowing, isSecure, isCurrentlyInsecure]
+                int state = ((Integer) operands[0]).intValue();
+                boolean isBouncerShowing = (((Integer) operands[3]).intValue()) == 1;
+                int isSecure = ((Integer) operands[4]).intValue();
+
+                int view = MetricsEvent.LOCKSCREEN;
+                int type = MetricsEvent.TYPE_OPEN;
+                if (state == SHADE) {
+                    type = MetricsEvent.TYPE_CLOSE;
+                } else if (isBouncerShowing) {
+                    view = MetricsEvent.BOUNCER;
+                }
+
+                LogMaker proto = logger.obtain();
+                proto.setCategory(view);
+                proto.setType(type);
+                proto.setTimestamp(eventTimeMs);
+                proto.setSubtype(isSecure);
+                logger.addEvent(proto);
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiActionParser.java b/core/java/com/android/internal/logging/legacy/SysuiActionParser.java
new file mode 100644
index 0000000..1148ee5
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/SysuiActionParser.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android framework sysui action logs.
+ * @hide
+ */
+public class SysuiActionParser extends TagParser {
+    private static final String TAG = "SysuiActionParser";
+    private static final int EVENTLOG_TAG = 524288;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        try {
+            String packageName = null;
+            int subType = -1;
+            boolean hasSubType = false;
+            if (operands.length > 1) {
+                String arg = (String) operands[1];
+                if (arg.equals("true")) {
+                    hasSubType = true;
+                    subType = 1;
+                } else if (arg.equals("false")) {
+                    hasSubType = true;
+                    subType = 0;
+                } else if (arg.matches("^-?\\d+$")) {
+                    try {
+                        subType = Integer.valueOf(arg);
+                        hasSubType = true;
+                    } catch (NumberFormatException e) {
+                    }
+                } else {
+                    packageName = arg;
+                }
+            }
+            if (operands.length > 0) {
+                int category = ((Integer) operands[0]).intValue();
+                LogMaker proto = logger.obtain();
+                proto.setCategory(category);
+                proto.setType(MetricsEvent.TYPE_ACTION);
+                proto.setTimestamp(eventTimeMs);
+                if (packageName != null) {
+                    proto.setPackageName(packageName);
+                }
+                if (hasSubType) {
+                    proto.setSubtype(subType);
+                }
+                logger.addEvent(proto);
+            }
+        } catch (ClassCastException e) {
+            if (debug) {
+                Log.e(TAG, "unexpected operand type: ", e);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java b/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java
new file mode 100644
index 0000000..0c77b7a
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+
+/**
+ * ...and one parser to rule them all.
+ *
+ * This should, at some point in the future, be the only parser.
+ * @hide
+ */
+public class SysuiMultiActionParser extends TagParser {
+    private static final String TAG = "SysuiMultiActionParser";
+    private static final int EVENTLOG_TAG = 524292;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        try {
+            logger.addEvent(new LogMaker(operands).setTimestamp(eventTimeMs));
+        } catch (ClassCastException e) {
+            if (debug) {
+                Log.e(TAG, "unexpected operand type: ", e);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiQueryParser.java b/core/java/com/android/internal/logging/legacy/SysuiQueryParser.java
new file mode 100644
index 0000000..7b3c0a7
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/SysuiQueryParser.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+/**
+ * Parse the Android framework sysui search query logs.
+ * For now just treat them like actions.
+ * @hide
+ */
+public class SysuiQueryParser extends SysuiActionParser {
+    private static final String TAG = "SysuiQueryParser";
+
+    private static final int EVENTLOG_TAG = 524289;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiViewVisibilityParser.java b/core/java/com/android/internal/logging/legacy/SysuiViewVisibilityParser.java
new file mode 100644
index 0000000..1223b8d
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/SysuiViewVisibilityParser.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android framework sysui view visibility logs.
+ * @hide
+ */
+public class SysuiViewVisibilityParser extends TagParser {
+    private static final String TAG = "SysuiViewVisibility";
+    private static final int EVENTLOG_TAG = 524287;
+
+    @Override
+    public int getTag() {
+        return EVENTLOG_TAG;
+    }
+
+    @Override
+    public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+        final boolean debug = Util.debug();
+        if (operands.length >= 2) {
+            try {
+                int category = ((Integer) operands[0]).intValue();
+                boolean visibility = ((Integer) operands[1]).intValue() != 0;
+
+                LogMaker proto = logger.obtain();
+                proto.setCategory(category);
+                proto.setType(visibility ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE);
+                proto.setTimestamp(eventTimeMs);
+                logger.addEvent(proto);
+            } catch (ClassCastException e) {
+                if (debug) {
+                    Log.e(TAG, "unexpected operand type: ", e);
+                }
+            }
+        } else if (debug) {
+            Log.w(TAG, "wrong number of operands: " + operands.length);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/TagParser.java b/core/java/com/android/internal/logging/legacy/TagParser.java
new file mode 100755
index 0000000..3bffdd5
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/TagParser.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import android.metrics.LogMaker;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Abstraction layer between EventLog static classes and the actual TagParsers.
+ * @hide
+ */
+public abstract class TagParser {
+    private static final String TAG = "TagParser";
+
+    protected int mSinceCreationMillis;
+    protected int mSinceUpdateMillis;
+    protected int mSinceVisibleMillis;
+
+    abstract int getTag();
+
+    @VisibleForTesting
+    abstract public void parseEvent(TronLogger logger, long eventTimeMs, Object[] objects);
+
+    /**
+     * Parse the event into the proto: return true if proto was modified.
+     */
+    public void  parseEvent(TronLogger logger, EventLogCollector.Event event) {
+        final boolean debug = Util.debug();
+        Object data = event.getData();
+        Object[] objects;
+        if (data instanceof Object[]) {
+            objects = (Object[]) data;
+            for (int i = 0; i < objects.length; i++) {
+                if (objects[i] == null) {
+                    if (debug) {
+                        Log.d(TAG, "unexpected null value:" + event.getTag());
+                    }
+                    return;
+                }
+            }
+        } else {
+            // wrap scalar objects
+            objects = new Object[1];
+            objects[0] = data;
+        }
+
+        parseEvent(logger, event.getTimeNanos() / 1000000, objects);
+    }
+
+    protected void resetTimes() {
+        mSinceCreationMillis = 0;
+        mSinceUpdateMillis = 0;
+        mSinceVisibleMillis = 0;
+    }
+
+    public void parseTimes(Object[] operands, int index) {
+        resetTimes();
+
+        if (operands.length > index && operands[index] instanceof Integer) {
+            mSinceCreationMillis = (Integer) operands[index];
+        }
+
+        index++;
+        if (operands.length > index && operands[index] instanceof Integer) {
+            mSinceUpdateMillis = (Integer) operands[index];
+        }
+
+        index++;
+        if (operands.length > index && operands[index] instanceof Integer) {
+            mSinceVisibleMillis = (Integer) operands[index];
+        }
+    }
+
+   public void filltimes(LogMaker proto) {
+        if (mSinceCreationMillis != 0) {
+            proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS,
+                    mSinceCreationMillis);
+        }
+        if (mSinceUpdateMillis != 0) {
+            proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS,
+                    mSinceUpdateMillis);
+        }
+        if (mSinceVisibleMillis != 0) {
+            proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS,
+                    mSinceVisibleMillis);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/logging/legacy/TronCounters.java b/core/java/com/android/internal/logging/legacy/TronCounters.java
new file mode 100644
index 0000000..e0828a2
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/TronCounters.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+/**
+ * Names of the counters that the Tron package defines.
+ *
+ * Other counter names may be generated by AOSP code.
+ * @hide
+ */
+public class TronCounters {
+
+    static final String TRON_COLLECTIONS = "tron_collections";
+
+    static final String TRON_COLLECTION_PERIOD = "tron_collection_period_minutes";
+
+    static final String TRON_EVENTLOG_LENGTH = "tron_eventlog_horizon";
+
+    static final String TRON_NOTE_DISMISS = "tron_note_dismiss";
+
+    static final String TRON_NOTE_DISMISS_BY_USER = "tron_note_dismiss_user";
+
+    static final String TRON_NOTE_DISMISS_BY_CLICK = "tron_note_dismiss_click";
+
+    static final String TRON_NOTE_DISMISS_BY_LISTENER = "tron_note_dismiss_listener";
+
+    static final String TRON_NOTE_DISMISS_BY_BAN = "tron_note_dismiss_ban";
+
+    static final String TRON_NOTE_REVEALED = "tron_note_revealed";
+
+    static final String TRON_NOTIFICATION_LOAD = "tron_notification_load";
+
+    static final String TRON_VIEW = "tron_view";
+
+    static final String TRON_ACTION = "tron_action";
+
+    static final String TRON_DETAIL = "tron_detail";
+
+    static final String TRON_NOTE_LIFETIME = "tron_note_lifetime";
+
+    /** Append the AOSP-generated name */
+    static final String TRON_AOSP_PREFIX = "tron_varz_";
+
+    static final String TRON_ACTION_BAD_INT = "tron_action_bad_int";
+
+    static final String TRON_NOTE_FRESHNESS = "tron_note_freshness";
+
+    static final String TRON_NOTE_BUZZ = "tron_note_buzz";
+
+    static final String TRON_NOTE_BEEP = "tron_note_beep";
+
+    static final String TRON_NOTE_BLINK = "tron_note_blink";
+
+    static final String TRON_DISABLE = "tron_disable";
+
+    static final String TRON_LAST_HEART_AGE = "tron_last_heart_minutes";
+
+    static final String TRON_HEARTS_SEEN = "tron_hearts_seen";
+}
diff --git a/core/java/com/android/internal/logging/legacy/TronLogger.java b/core/java/com/android/internal/logging/legacy/TronLogger.java
new file mode 100644
index 0000000..ee9341a
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/TronLogger.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.metrics.LogMaker;
+
+/**
+ * An entity that knows how to log events and counters.
+ */
+public interface TronLogger {
+    /** Add one to the named counter. */
+    void increment(String counterName);
+
+    /** Add an arbitrary value to the named counter. */
+    void incrementBy(String counterName, int value);
+
+    /** Increment a specified bucket on the named histogram by one. */
+    void incrementIntHistogram(String counterName, int bucket);
+
+    /** Increment the specified bucket on the named histogram by one. */
+    void incrementLongHistogram(String counterName, long bucket);
+
+    /** Obtain a SystemUiEvent proto, must release this with dispose() or addEvent(). */
+    LogMaker obtain();
+
+    void dispose(LogMaker proto);
+
+    /** Submit an event to be logged. Logger will dispose of proto. */
+    void addEvent(LogMaker proto);
+
+    /** Get a config flag. */
+    boolean getConfig(String configName);
+
+    /** Set a config flag. */
+    void setConfig(String configName, boolean newValue);
+}
diff --git a/core/java/com/android/internal/logging/legacy/Util.java b/core/java/com/android/internal/logging/legacy/Util.java
new file mode 100644
index 0000000..99f71ca
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/Util.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+/**
+ * Created by cwren on 11/21/16.
+ */
+public class Util {
+    public static boolean debug() {
+        return false;
+    }
+}
diff --git a/core/java/com/android/internal/os/AppFuseMount.aidl b/core/java/com/android/internal/os/AppFuseMount.aidl
new file mode 100644
index 0000000..66cf95b
--- /dev/null
+++ b/core/java/com/android/internal/os/AppFuseMount.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+parcelable AppFuseMount;
diff --git a/core/java/com/android/internal/os/AppFuseMount.java b/core/java/com/android/internal/os/AppFuseMount.java
index b392186..04d7211 100644
--- a/core/java/com/android/internal/os/AppFuseMount.java
+++ b/core/java/com/android/internal/os/AppFuseMount.java
@@ -19,14 +19,26 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
-import java.io.File;
+import android.os.storage.IStorageManager;
+import com.android.internal.util.Preconditions;
 
+/**
+ * Parcelable class representing AppFuse mount.
+ * This conveys the result for IStorageManager#openProxyFileDescriptor.
+ * @see IStorageManager#openProxyFileDescriptor
+ */
 public class AppFuseMount implements Parcelable {
-    final public File mountPoint;
+    final public int mountPointId;
     final public ParcelFileDescriptor fd;
 
-    public AppFuseMount(File mountPoint, ParcelFileDescriptor fd) {
-        this.mountPoint = mountPoint;
+    /**
+     * @param mountPointId Integer number for mount point that is unique in the lifetime of
+     *     StorageManagerService.
+     * @param fd File descriptor pointing /dev/fuse and tagged with the mount point.
+     */
+    public AppFuseMount(int mountPointId, ParcelFileDescriptor fd) {
+        Preconditions.checkNotNull(fd);
+        this.mountPointId = mountPointId;
         this.fd = fd;
     }
 
@@ -37,7 +49,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(this.mountPoint.getPath());
+        dest.writeInt(this.mountPointId);
         dest.writeParcelable(fd, flags);
     }
 
@@ -45,7 +57,7 @@
             new Parcelable.Creator<AppFuseMount>() {
         @Override
         public AppFuseMount createFromParcel(Parcel in) {
-            return new AppFuseMount(new File(in.readString()), in.readParcelable(null));
+            return new AppFuseMount(in.readInt(), in.readParcelable(null));
         }
 
         @Override
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5fd68e6..c34c379 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -9713,7 +9713,7 @@
                 resetAllStatsLocked();
                 if (chargeUAh > 0) {
                     // Only use the reported coulomb charge value if it is supported and reported.
-                    mEstimatedBatteryCapacity = (int) ((level / 100.0) * (chargeUAh / 1000));
+                    mEstimatedBatteryCapacity = (int) ((chargeUAh / 1000) / (level / 100.0));
                 }
                 mDischargeStartLevel = level;
                 reset = true;
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index 34253ce..3603b6d 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -18,34 +18,38 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.IProxyFileDescriptorCallback;
+import android.os.ProxyFileDescriptorCallback;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.util.Log;
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.concurrent.ThreadFactory;
 
 public class FuseAppLoop {
     private static final String TAG = "FuseAppLoop";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final int ROOT_INODE = 1;
     private static final int MIN_INODE = 2;
+    private static final ThreadFactory sDefaultThreadFactory = new ThreadFactory() {
+        @Override
+        public Thread newThread(Runnable r) {
+            return new Thread(r, TAG);
+        }
+    };
 
     private final Object mLock = new Object();
-    private final File mParent;
+    private final int mMountPointId;
+    private final Thread mThread;
 
     @GuardedBy("mLock")
     private final SparseArray<CallbackEntry> mCallbackMap = new SparseArray<>();
 
-    @GuardedBy("mLock")
-    private boolean mActive = true;
-
     /**
      * Sequential number can be used as file name and inode in AppFuse.
      * 0 is regarded as an error, 1 is mount point. So we start the number from 2.
@@ -53,35 +57,40 @@
     @GuardedBy("mLock")
     private int mNextInode = MIN_INODE;
 
-    private FuseAppLoop(@NonNull File parent) {
-        mParent = parent;
-    }
-
-    public static @NonNull FuseAppLoop open(
-            @NonNull File parent, @NonNull ParcelFileDescriptor fd) {
-        Preconditions.checkNotNull(parent);
-        Preconditions.checkNotNull(fd);
-        final FuseAppLoop bridge = new FuseAppLoop(parent);
+    private FuseAppLoop(
+            int mountPointId, @NonNull ParcelFileDescriptor fd, @Nullable ThreadFactory factory) {
+        mMountPointId = mountPointId;
         final int rawFd = fd.detachFd();
-        new Thread(new Runnable() {
+        if (factory == null) {
+            factory = sDefaultThreadFactory;
+        }
+        mThread = factory.newThread(new Runnable() {
             @Override
             public void run() {
-                bridge.native_start_loop(rawFd);
+                // rawFd is closed by native_start_loop. Java code does not need to close it.
+                native_start_loop(rawFd);
             }
-        }, TAG).start();
-        return bridge;
+        });
     }
 
-    public @NonNull ParcelFileDescriptor openFile(int mode, IProxyFileDescriptorCallback callback)
+    public static @NonNull FuseAppLoop open(int mountPointId, @NonNull ParcelFileDescriptor fd,
+            @Nullable ThreadFactory factory) {
+        Preconditions.checkNotNull(fd);
+        final FuseAppLoop loop = new FuseAppLoop(mountPointId, fd, factory);
+        loop.mThread.start();
+        return loop;
+    }
+
+    public int registerCallback(@NonNull ProxyFileDescriptorCallback callback)
             throws UnmountedException, IOException {
-        int id;
+        if (mThread.getState() == Thread.State.TERMINATED) {
+            throw new UnmountedException();
+        }
         synchronized (mLock) {
-            if (!mActive) {
-                throw new UnmountedException();
-            }
             if (mCallbackMap.size() >= Integer.MAX_VALUE - MIN_INODE) {
                 throw new IOException("Too many opened files.");
             }
+            int id;
             while (true) {
                 id = mNextInode;
                 mNextInode++;
@@ -92,24 +101,17 @@
                     break;
                 }
             }
-
-            // Register callback after we succeed to create pfd.
             mCallbackMap.put(id, new CallbackEntry(callback));
-        }
-        try {
-            return ParcelFileDescriptor.open(new File(mParent, String.valueOf(id)), mode);
-        } catch (FileNotFoundException error) {
-            synchronized (mLock) {
-                mCallbackMap.remove(id);
-            }
-            throw error;
+            return id;
         }
     }
 
-    public @Nullable File getMountPoint() {
-        synchronized (mLock) {
-            return mActive ? mParent : null;
-        }
+    public void unregisterCallback(int id) {
+        mCallbackMap.remove(id);
+    }
+
+    public int getMountPointId() {
+        return mMountPointId;
     }
 
     private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
@@ -128,7 +130,7 @@
             try {
                 return getCallbackEntryOrThrowLocked(inode).callback.onGetSize();
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
@@ -147,7 +149,7 @@
                 // file twice.
                 return (int) inode;
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
@@ -160,7 +162,7 @@
                 getCallbackEntryOrThrowLocked(inode).callback.onFsync();
                 return 0;
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
@@ -169,12 +171,14 @@
     @SuppressWarnings("unused")
     private int onRelease(long inode) {
         synchronized(mLock) {
-            mCallbackMap.remove(checkInode(inode));
-            if (mCallbackMap.size() == 0) {
-                mActive = false;
-                return -1;
+            try {
+                getCallbackEntryOrThrowLocked(inode).callback.onRelease();
+                return 0;
+            } catch (ErrnoException exp) {
+                return getError(exp);
+            } finally {
+                mCallbackMap.remove(checkInode(inode));
             }
-            return 0;
         }
     }
 
@@ -185,7 +189,7 @@
             try {
                 return getCallbackEntryOrThrowLocked(inode).callback.onRead(offset, size, bytes);
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
@@ -197,11 +201,17 @@
             try {
                 return getCallbackEntryOrThrowLocked(inode).callback.onWrite(offset, size, bytes);
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
 
+    private static int getError(@NonNull ErrnoException exp) {
+        // Should not return ENOSYS because the kernel stops
+        // dispatching the FUSE action once FUSE implementation returns ENOSYS for the action.
+        return exp.errno != OsConstants.ENOSYS ? -exp.errno : -OsConstants.EIO;
+    }
+
     native boolean native_start_loop(int fd);
 
     private static int checkInode(long inode) {
@@ -212,9 +222,9 @@
     public static class UnmountedException extends Exception {}
 
     private static class CallbackEntry {
-        final IProxyFileDescriptorCallback callback;
+        final ProxyFileDescriptorCallback callback;
         boolean opened;
-        CallbackEntry(IProxyFileDescriptorCallback callback) {
+        CallbackEntry(ProxyFileDescriptorCallback callback) {
             Preconditions.checkNotNull(callback);
             this.callback = callback;
         }
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index c828d11..e8919ed 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -120,7 +120,7 @@
                             sb.append(" s=");
                             TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
                             sb.append(" p=").append(powerDeltaMaUs / 1000).append("mAms");
-                            Slog.wtf(TAG, sb.toString());
+                            Slog.e(TAG, sb.toString());
 
                             userTimeDeltaUs = 0;
                             systemTimeDeltaUs = 0;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8eb75c0..a3b066a 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -25,7 +25,6 @@
 import android.icu.text.DecimalFormatSymbols;
 import android.icu.util.ULocale;
 import android.net.LocalServerSocket;
-import android.opengl.EGL14;
 import android.os.IInstalld;
 import android.os.Process;
 import android.os.RemoteException;
@@ -125,9 +124,6 @@
         bootTimingsTraceLog.traceBegin("PreloadResources");
         preloadResources();
         bootTimingsTraceLog.traceEnd(); // PreloadResources
-        bootTimingsTraceLog.traceBegin("PreloadOpenGL");
-        preloadOpenGL();
-        bootTimingsTraceLog.traceEnd(); // PreloadOpenGL
         preloadSharedLibraries();
         preloadTextResources();
         // Ask the WebViewFactory to do any initialization that must run in the zygote process,
@@ -177,12 +173,6 @@
         System.loadLibrary("jnigraphics");
     }
 
-    private static void preloadOpenGL() {
-        if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
-            EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
-        }
-    }
-
     private static void preloadTextResources() {
         Hyphenator.init();
         TextView.preloadFontCache();
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 789e9d4..7ff115b 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -2243,6 +2243,14 @@
     }
 
     @Override
+    public void dispatchPointerCaptureChanged(boolean hasCapture) {
+        super.dispatchPointerCaptureChanged(hasCapture);
+        if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
+            mWindow.getCallback().onPointerCaptureChanged(hasCapture);
+        }
+    }
+
+    @Override
     public String toString() {
         return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
                 + getTitleSuffix(mWindow.getAttributes()) + "]";
diff --git a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
index fb0edea..ebc2c71 100644
--- a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
+++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
@@ -84,7 +84,8 @@
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false);
+                MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
+                        event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
                 return true;
             }
 
@@ -215,7 +216,8 @@
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 if (!event.isCanceled()) {
-                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false);
+                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
+                            event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
                 }
                 return true;
             }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e68ebc4..84195b2 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -1856,27 +1856,25 @@
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                int direction = 0;
-                switch (keyCode) {
-                    case KeyEvent.KEYCODE_VOLUME_UP:
-                        direction = AudioManager.ADJUST_RAISE;
-                        break;
-                    case KeyEvent.KEYCODE_VOLUME_DOWN:
-                        direction = AudioManager.ADJUST_LOWER;
-                        break;
-                    case KeyEvent.KEYCODE_VOLUME_MUTE:
-                        direction = AudioManager.ADJUST_TOGGLE_MUTE;
-                        break;
-                }
                 // If we have a session send it the volume command, otherwise
                 // use the suggested stream.
                 if (mMediaController != null) {
+                    int direction = 0;
+                    switch (keyCode) {
+                        case KeyEvent.KEYCODE_VOLUME_UP:
+                            direction = AudioManager.ADJUST_RAISE;
+                            break;
+                        case KeyEvent.KEYCODE_VOLUME_DOWN:
+                            direction = AudioManager.ADJUST_LOWER;
+                            break;
+                        case KeyEvent.KEYCODE_VOLUME_MUTE:
+                            direction = AudioManager.ADJUST_TOGGLE_MUTE;
+                            break;
+                    }
                     mMediaController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI);
                 } else {
-                    MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy(
-                            mVolumeControlStreamType, direction,
-                            AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE
-                                    | AudioManager.FLAG_FROM_KEY);
+                    MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent(
+                            event, mVolumeControlStreamType, false);
                 }
                 return true;
             }
@@ -1954,15 +1952,15 @@
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN: {
-                final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
-                        | AudioManager.FLAG_FROM_KEY;
                 // If we have a session send it the volume command, otherwise
                 // use the suggested stream.
                 if (mMediaController != null) {
+                    final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE
+                            | AudioManager.FLAG_FROM_KEY;
                     mMediaController.adjustVolume(0, flags);
                 } else {
-                    MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy(
-                            mVolumeControlStreamType, 0, flags);
+                    MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent(
+                            event, mVolumeControlStreamType, false);
                 }
                 return true;
             }
@@ -1971,7 +1969,8 @@
                 // doesn't have one of these.  In this case, we execute it here and
                 // eat the event instead, because we have mVolumeControlStreamType
                 // and they don't.
-                getAudioManager().handleKeyUp(event, mVolumeControlStreamType);
+                MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent(
+                        event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
                 return true;
             }
             // These are all the recognized media key codes in
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 4dd3360..d7bd212 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -39,14 +39,20 @@
     private static final int SNAP_MODE_CORNERS_AND_SIDES = 1;
     // Allows snapping to anywhere along the edge of the screen
     private static final int SNAP_MODE_EDGE = 2;
+    // Allows snapping to four corners on a fling towards a corner or slow move near a corner
+    // snaps anywhere along the edge of screen otherwise
+    private static final int SNAP_MODE_CORNERS_AND_EDGES = 3;
 
     // The friction multiplier to control how slippery the PIP is when flung
     private static final float SCROLL_FRICTION_MULTIPLIER = 8f;
 
+    // Threshold to magnet to a corner
+    private static final float CORNER_MAGNET_THRESHOLD = 0.3f;
+
     private final Context mContext;
 
     private final ArrayList<Integer> mSnapGravities = new ArrayList<>();
-    private final int mDefaultSnapMode = SNAP_MODE_CORNERS_ONLY;
+    private final int mDefaultSnapMode = SNAP_MODE_CORNERS_AND_EDGES;
     private int mSnapMode = mDefaultSnapMode;
 
     private Scroller mScroller;
@@ -107,7 +113,24 @@
                 movementBounds.right + stackBounds.width(),
                 movementBounds.bottom + stackBounds.height());
         final Rect newBounds = new Rect(stackBounds);
-        if (mSnapMode == SNAP_MODE_EDGE) {
+        if (mSnapMode == SNAP_MODE_CORNERS_AND_EDGES) {
+            final Rect tmpBounds = new Rect();
+            final Point[] snapTargets = new Point[mSnapGravities.size()];
+            for (int i = 0; i < mSnapGravities.size(); i++) {
+                Gravity.apply(mSnapGravities.get(i), stackBounds.width(), stackBounds.height(),
+                        pipBounds, 0, 0, tmpBounds);
+                snapTargets[i] = new Point(tmpBounds.left, tmpBounds.top);
+            }
+            Point snapTarget = findClosestPoint(stackBounds.left, stackBounds.top, snapTargets);
+            float distance = distanceToPoint(snapTarget, stackBounds.left, stackBounds.top);
+            final float thresh = stackBounds.width() * CORNER_MAGNET_THRESHOLD;
+            if (distance < thresh) {
+                newBounds.offsetTo(snapTarget.x, snapTarget.y);
+            } else {
+                // Otherwise we snap to the edge
+                snapRectToClosestEdge(stackBounds, movementBounds, newBounds);
+            }
+        } else if (mSnapMode == SNAP_MODE_EDGE) {
             // Find the closest edge to the given stack bounds and snap to it
             snapRectToClosestEdge(stackBounds, movementBounds, newBounds);
         } else {
@@ -128,11 +151,14 @@
     /**
      * Applies the offset to the {@param stackBounds} to adjust it to a minimized state.
      */
-    public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize) {
+    public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize,
+            Rect stableInsets) {
         if (stackBounds.left <= movementBounds.centerX()) {
-            stackBounds.offsetTo(-stackBounds.width() + mMinimizedVisibleSize, stackBounds.top);
+            stackBounds.offsetTo(stableInsets.left + mMinimizedVisibleSize - stackBounds.width(),
+                    stackBounds.top);
         } else {
-            stackBounds.offsetTo(displaySize.x - mMinimizedVisibleSize, stackBounds.top);
+            stackBounds.offsetTo(displaySize.x - stableInsets.right - mMinimizedVisibleSize,
+                    stackBounds.top);
         }
     }
 
@@ -270,6 +296,7 @@
                 }
                 // Fall through
             case SNAP_MODE_CORNERS_ONLY:
+            case SNAP_MODE_CORNERS_AND_EDGES:
                 mSnapGravities.add(Gravity.TOP | Gravity.LEFT);
                 mSnapGravities.add(Gravity.TOP | Gravity.RIGHT);
                 mSnapGravities.add(Gravity.BOTTOM | Gravity.LEFT);
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 7ee5170..0fe580a 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -33,6 +33,8 @@
 import android.graphics.drawable.VectorDrawable;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
+import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
 import android.text.style.TextAppearanceSpan;
 import android.util.Log;
 import android.util.Pair;
@@ -184,8 +186,24 @@
             SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
             for (Object span : spans) {
                 Object resultSpan = span;
-                if (span instanceof TextAppearanceSpan) {
-                    resultSpan = processTextAppearanceSpan((TextAppearanceSpan) span);
+                if (resultSpan instanceof CharacterStyle) {
+                    resultSpan = ((CharacterStyle) span).getUnderlying();
+                }
+                if (resultSpan instanceof TextAppearanceSpan) {
+                    TextAppearanceSpan processedSpan = processTextAppearanceSpan(
+                            (TextAppearanceSpan) span);
+                    if (processedSpan != resultSpan) {
+                        resultSpan = processedSpan;
+                    } else {
+                        // we need to still take the orgininal for wrapped spans
+                        resultSpan = span;
+                    }
+                } else if (resultSpan instanceof ForegroundColorSpan) {
+                    ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
+                    int foregroundColor = originalSpan.getForegroundColor();
+                    resultSpan = new ForegroundColorSpan(processColor(foregroundColor));
+                } else {
+                    resultSpan = span;
                 }
                 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
                         ss.getSpanFlags(span));
@@ -306,6 +324,10 @@
         return ColorUtilsFromCompat.LABToColor(high, a, b);
     }
 
+    public static int ensureTextContrastOnBlack(int color) {
+        return findContrastColorAgainstDark(color, Color.BLACK, true /* fg */, 12);
+    }
+
     /**
      * Finds a text color with sufficient contrast over bg that has the same hue as the original
      * color, assuming it is for large text.
@@ -393,6 +415,67 @@
         return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]);
     }
 
+    public static int resolveAmbientColor(Context context, int notificationColor) {
+        final int resolvedColor = resolveColor(context, notificationColor);
+
+        int color = resolvedColor;
+        color = NotificationColorUtil.ensureTextContrastOnBlack(color);
+
+        if (color != resolvedColor) {
+            if (DEBUG){
+                Log.w(TAG, String.format(
+                        "Ambient contrast of notification for %s is %s (over black)"
+                                + " by changing #%s to #%s",
+                        context.getPackageName(),
+                        NotificationColorUtil.contrastChange(resolvedColor, color, Color.BLACK),
+                        Integer.toHexString(resolvedColor), Integer.toHexString(color)));
+            }
+        }
+        return color;
+    }
+
+    public static int resolvePrimaryColor(Context context, int backgroundColor) {
+        boolean useDark = shouldUseDark(backgroundColor);
+        if (useDark) {
+            return context.getColor(
+                    com.android.internal.R.color.notification_primary_text_color_light);
+        } else {
+            return context.getColor(
+                    com.android.internal.R.color.notification_primary_text_color_dark);
+        }
+    }
+
+    public static int resolveSecondaryColor(Context context, int backgroundColor) {
+        boolean useDark = shouldUseDark(backgroundColor);
+        if (useDark) {
+            return context.getColor(
+                    com.android.internal.R.color.notification_secondary_text_color_light);
+        } else {
+            return context.getColor(
+                    com.android.internal.R.color.notification_secondary_text_color_dark);
+        }
+    }
+
+    public static int resolveActionBarColor(int backgroundColor) {
+        boolean useDark = shouldUseDark(backgroundColor);
+        final double[] result = ColorUtilsFromCompat.getTempDouble3Array();
+        ColorUtilsFromCompat.colorToLAB(backgroundColor, result);
+        if (useDark && result[0] < 97 || !useDark && result[0] < 4) {
+            result[0] = Math.min(100, result[0] + 7);
+        } else {
+            result[0] = Math.max(0, result[0] - 7);
+        }
+        return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]);
+    }
+
+    private static boolean shouldUseDark(int backgroundColor) {
+        boolean useDark = backgroundColor == Notification.COLOR_DEFAULT;
+        if (!useDark) {
+            useDark = ColorUtilsFromCompat.calculateLuminance(backgroundColor) > 0.5;
+        }
+        return useDark;
+    }
+
     /**
      * Framework copy of functions needed from android.support.v4.graphics.ColorUtils.
      */
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index c9c8292..dd91d2f 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -122,4 +122,8 @@
     @Override
     public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
     }
+
+    @Override
+    public void dispatchPointerCaptureChanged(boolean hasCapture) {
+    }
 }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index c8697dd..1fce0b7 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -48,7 +48,7 @@
     private MenuItem.OnMenuItemClickListener mClickListener;
 
     private CharSequence mContentDescription;
-    private CharSequence mTooltip;
+    private CharSequence mTooltipText;
 
     private static final int NO_ICON = 0;
 
@@ -290,13 +290,13 @@
     }
 
     @Override
-    public MenuItem setTooltip(CharSequence tooltip) {
-        mTooltip = tooltip;
+    public MenuItem setTooltipText(CharSequence tooltipText) {
+        mTooltipText = tooltipText;
         return this;
     }
 
     @Override
-    public CharSequence getTooltip() {
-        return mTooltip;
+    public CharSequence getTooltipText() {
+        return mTooltipText;
     }
 }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index 4ee5993..92e1d80 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -193,12 +193,12 @@
             setContentDescription(contentDescription);
         }
 
-        final CharSequence tooltip = mItemData.getTooltip();
-        if (TextUtils.isEmpty(tooltip)) {
+        final CharSequence tooltipText = mItemData.getTooltipText();
+        if (TextUtils.isEmpty(tooltipText)) {
             // Use the uncondensed title for tooltip, but only if the title is not shown already.
-            setTooltip(visible ? null : mItemData.getTitle());
+            setTooltipText(visible ? null : mItemData.getTitle());
         } else {
-            setTooltip(tooltip);
+            setTooltipText(tooltipText);
         }
     }
 
diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java
index f9ebdbc..6c8f330 100644
--- a/core/java/com/android/internal/view/menu/IconMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java
@@ -113,7 +113,7 @@
             } else {
                 setContentDescription(contentDescription);
             }
-            setTooltip(mItemData.getTooltip());
+            setTooltipText(mItemData.getTooltipText());
         }
     }
 
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 7c9f709..43005e6 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -115,7 +115,7 @@
         setEnabled(itemData.isEnabled());
         setSubMenuArrowVisible(itemData.hasSubMenu());
         setContentDescription(itemData.getContentDescription());
-        setTooltip(itemData.getTooltip());
+        setTooltipText(itemData.getTooltipText());
     }
 
     public void setForceShowIcon(boolean forceShow) {
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index cad0276..342943f 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -94,7 +94,7 @@
     private ContextMenuInfo mMenuInfo;
 
     private CharSequence mContentDescription;
-    private CharSequence mTooltip;
+    private CharSequence mTooltipText;
 
     private static String sLanguage;
     private static String sPrependShortcutLabel;
@@ -689,8 +689,8 @@
     }
 
     @Override
-    public MenuItem setTooltip(CharSequence tooltip) {
-        mTooltip = tooltip;
+    public MenuItem setTooltipText(CharSequence tooltipText) {
+        mTooltipText = tooltipText;
 
         mMenu.onItemsChanged(false);
 
@@ -698,7 +698,7 @@
     }
 
     @Override
-    public CharSequence getTooltip() {
-        return mTooltip;
+    public CharSequence getTooltipText() {
+        return mTooltipText;
     }
 }
diff --git a/core/java/com/android/internal/widget/AdapterHelper.java b/core/java/com/android/internal/widget/AdapterHelper.java
new file mode 100644
index 0000000..f47d430
--- /dev/null
+++ b/core/java/com/android/internal/widget/AdapterHelper.java
@@ -0,0 +1,775 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.util.Log;
+import android.util.Pools;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Helper class that can enqueue and process adapter update operations.
+ * <p>
+ * To support animations, RecyclerView presents an older version the Adapter to best represent
+ * previous state of the layout. Sometimes, this is not trivial when items are removed that were
+ * not laid out, in which case, RecyclerView has no way of providing that item's view for
+ * animations.
+ * <p>
+ * AdapterHelper creates an UpdateOp for each adapter data change then pre-processes them. During
+ * pre processing, AdapterHelper finds out which UpdateOps can be deferred to second layout pass
+ * and which cannot. For the UpdateOps that cannot be deferred, AdapterHelper will change them
+ * according to previously deferred operation and dispatch them before the first layout pass. It
+ * also takes care of updating deferred UpdateOps since order of operations is changed by this
+ * process.
+ * <p>
+ * Although operations may be forwarded to LayoutManager in different orders, resulting data set
+ * is guaranteed to be the consistent.
+ */
+class AdapterHelper implements OpReorderer.Callback {
+
+    static final int POSITION_TYPE_INVISIBLE = 0;
+
+    static final int POSITION_TYPE_NEW_OR_LAID_OUT = 1;
+
+    private static final boolean DEBUG = false;
+
+    private static final String TAG = "AHT";
+
+    private Pools.Pool<UpdateOp> mUpdateOpPool = new Pools.SimplePool<UpdateOp>(UpdateOp.POOL_SIZE);
+
+    final ArrayList<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();
+
+    final ArrayList<UpdateOp> mPostponedList = new ArrayList<UpdateOp>();
+
+    final Callback mCallback;
+
+    Runnable mOnItemProcessedCallback;
+
+    final boolean mDisableRecycler;
+
+    final OpReorderer mOpReorderer;
+
+    private int mExistingUpdateTypes = 0;
+
+    AdapterHelper(Callback callback) {
+        this(callback, false);
+    }
+
+    AdapterHelper(Callback callback, boolean disableRecycler) {
+        mCallback = callback;
+        mDisableRecycler = disableRecycler;
+        mOpReorderer = new OpReorderer(this);
+    }
+
+    AdapterHelper addUpdateOp(UpdateOp... ops) {
+        Collections.addAll(mPendingUpdates, ops);
+        return this;
+    }
+
+    void reset() {
+        recycleUpdateOpsAndClearList(mPendingUpdates);
+        recycleUpdateOpsAndClearList(mPostponedList);
+        mExistingUpdateTypes = 0;
+    }
+
+    void preProcess() {
+        mOpReorderer.reorderOps(mPendingUpdates);
+        final int count = mPendingUpdates.size();
+        for (int i = 0; i < count; i++) {
+            UpdateOp op = mPendingUpdates.get(i);
+            switch (op.cmd) {
+                case UpdateOp.ADD:
+                    applyAdd(op);
+                    break;
+                case UpdateOp.REMOVE:
+                    applyRemove(op);
+                    break;
+                case UpdateOp.UPDATE:
+                    applyUpdate(op);
+                    break;
+                case UpdateOp.MOVE:
+                    applyMove(op);
+                    break;
+            }
+            if (mOnItemProcessedCallback != null) {
+                mOnItemProcessedCallback.run();
+            }
+        }
+        mPendingUpdates.clear();
+    }
+
+    void consumePostponedUpdates() {
+        final int count = mPostponedList.size();
+        for (int i = 0; i < count; i++) {
+            mCallback.onDispatchSecondPass(mPostponedList.get(i));
+        }
+        recycleUpdateOpsAndClearList(mPostponedList);
+        mExistingUpdateTypes = 0;
+    }
+
+    private void applyMove(UpdateOp op) {
+        // MOVE ops are pre-processed so at this point, we know that item is still in the adapter.
+        // otherwise, it would be converted into a REMOVE operation
+        postponeAndUpdateViewHolders(op);
+    }
+
+    private void applyRemove(UpdateOp op) {
+        int tmpStart = op.positionStart;
+        int tmpCount = 0;
+        int tmpEnd = op.positionStart + op.itemCount;
+        int type = -1;
+        for (int position = op.positionStart; position < tmpEnd; position++) {
+            boolean typeChanged = false;
+            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
+            if (vh != null || canFindInPreLayout(position)) {
+                // If a ViewHolder exists or this is a newly added item, we can defer this update
+                // to post layout stage.
+                // * For existing ViewHolders, we'll fake its existence in the pre-layout phase.
+                // * For items that are added and removed in the same process cycle, they won't
+                // have any effect in pre-layout since their add ops are already deferred to
+                // post-layout pass.
+                if (type == POSITION_TYPE_INVISIBLE) {
+                    // Looks like we have other updates that we cannot merge with this one.
+                    // Create an UpdateOp and dispatch it to LayoutManager.
+                    UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
+                    dispatchAndUpdateViewHolders(newOp);
+                    typeChanged = true;
+                }
+                type = POSITION_TYPE_NEW_OR_LAID_OUT;
+            } else {
+                // This update cannot be recovered because we don't have a ViewHolder representing
+                // this position. Instead, post it to LayoutManager immediately
+                if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
+                    // Looks like we have other updates that we cannot merge with this one.
+                    // Create UpdateOp op and dispatch it to LayoutManager.
+                    UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
+                    postponeAndUpdateViewHolders(newOp);
+                    typeChanged = true;
+                }
+                type = POSITION_TYPE_INVISIBLE;
+            }
+            if (typeChanged) {
+                position -= tmpCount; // also equal to tmpStart
+                tmpEnd -= tmpCount;
+                tmpCount = 1;
+            } else {
+                tmpCount++;
+            }
+        }
+        if (tmpCount != op.itemCount) { // all 1 effect
+            recycleUpdateOp(op);
+            op = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
+        }
+        if (type == POSITION_TYPE_INVISIBLE) {
+            dispatchAndUpdateViewHolders(op);
+        } else {
+            postponeAndUpdateViewHolders(op);
+        }
+    }
+
+    private void applyUpdate(UpdateOp op) {
+        int tmpStart = op.positionStart;
+        int tmpCount = 0;
+        int tmpEnd = op.positionStart + op.itemCount;
+        int type = -1;
+        for (int position = op.positionStart; position < tmpEnd; position++) {
+            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
+            if (vh != null || canFindInPreLayout(position)) { // deferred
+                if (type == POSITION_TYPE_INVISIBLE) {
+                    UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
+                            op.payload);
+                    dispatchAndUpdateViewHolders(newOp);
+                    tmpCount = 0;
+                    tmpStart = position;
+                }
+                type = POSITION_TYPE_NEW_OR_LAID_OUT;
+            } else { // applied
+                if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
+                    UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
+                            op.payload);
+                    postponeAndUpdateViewHolders(newOp);
+                    tmpCount = 0;
+                    tmpStart = position;
+                }
+                type = POSITION_TYPE_INVISIBLE;
+            }
+            tmpCount++;
+        }
+        if (tmpCount != op.itemCount) { // all 1 effect
+            Object payload = op.payload;
+            recycleUpdateOp(op);
+            op = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount, payload);
+        }
+        if (type == POSITION_TYPE_INVISIBLE) {
+            dispatchAndUpdateViewHolders(op);
+        } else {
+            postponeAndUpdateViewHolders(op);
+        }
+    }
+
+    private void dispatchAndUpdateViewHolders(UpdateOp op) {
+        // tricky part.
+        // traverse all postpones and revert their changes on this op if necessary, apply updated
+        // dispatch to them since now they are after this op.
+        if (op.cmd == UpdateOp.ADD || op.cmd == UpdateOp.MOVE) {
+            throw new IllegalArgumentException("should not dispatch add or move for pre layout");
+        }
+        if (DEBUG) {
+            Log.d(TAG, "dispatch (pre)" + op);
+            Log.d(TAG, "postponed state before:");
+            for (UpdateOp updateOp : mPostponedList) {
+                Log.d(TAG, updateOp.toString());
+            }
+            Log.d(TAG, "----");
+        }
+
+        // handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial
+        // TODO Since move ops are pushed to end, we should not need this anymore
+        int tmpStart = updatePositionWithPostponed(op.positionStart, op.cmd);
+        if (DEBUG) {
+            Log.d(TAG, "pos:" + op.positionStart + ",updatedPos:" + tmpStart);
+        }
+        int tmpCnt = 1;
+        int offsetPositionForPartial = op.positionStart;
+        final int positionMultiplier;
+        switch (op.cmd) {
+            case UpdateOp.UPDATE:
+                positionMultiplier = 1;
+                break;
+            case UpdateOp.REMOVE:
+                positionMultiplier = 0;
+                break;
+            default:
+                throw new IllegalArgumentException("op should be remove or update." + op);
+        }
+        for (int p = 1; p < op.itemCount; p++) {
+            final int pos = op.positionStart + (positionMultiplier * p);
+            int updatedPos = updatePositionWithPostponed(pos, op.cmd);
+            if (DEBUG) {
+                Log.d(TAG, "pos:" + pos + ",updatedPos:" + updatedPos);
+            }
+            boolean continuous = false;
+            switch (op.cmd) {
+                case UpdateOp.UPDATE:
+                    continuous = updatedPos == tmpStart + 1;
+                    break;
+                case UpdateOp.REMOVE:
+                    continuous = updatedPos == tmpStart;
+                    break;
+            }
+            if (continuous) {
+                tmpCnt++;
+            } else {
+                // need to dispatch this separately
+                UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt, op.payload);
+                if (DEBUG) {
+                    Log.d(TAG, "need to dispatch separately " + tmp);
+                }
+                dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
+                recycleUpdateOp(tmp);
+                if (op.cmd == UpdateOp.UPDATE) {
+                    offsetPositionForPartial += tmpCnt;
+                }
+                tmpStart = updatedPos; // need to remove previously dispatched
+                tmpCnt = 1;
+            }
+        }
+        Object payload = op.payload;
+        recycleUpdateOp(op);
+        if (tmpCnt > 0) {
+            UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt, payload);
+            if (DEBUG) {
+                Log.d(TAG, "dispatching:" + tmp);
+            }
+            dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
+            recycleUpdateOp(tmp);
+        }
+        if (DEBUG) {
+            Log.d(TAG, "post dispatch");
+            Log.d(TAG, "postponed state after:");
+            for (UpdateOp updateOp : mPostponedList) {
+                Log.d(TAG, updateOp.toString());
+            }
+            Log.d(TAG, "----");
+        }
+    }
+
+    void dispatchFirstPassAndUpdateViewHolders(UpdateOp op, int offsetStart) {
+        mCallback.onDispatchFirstPass(op);
+        switch (op.cmd) {
+            case UpdateOp.REMOVE:
+                mCallback.offsetPositionsForRemovingInvisible(offsetStart, op.itemCount);
+                break;
+            case UpdateOp.UPDATE:
+                mCallback.markViewHoldersUpdated(offsetStart, op.itemCount, op.payload);
+                break;
+            default:
+                throw new IllegalArgumentException("only remove and update ops can be dispatched"
+                        + " in first pass");
+        }
+    }
+
+    private int updatePositionWithPostponed(int pos, int cmd) {
+        final int count = mPostponedList.size();
+        for (int i = count - 1; i >= 0; i--) {
+            UpdateOp postponed = mPostponedList.get(i);
+            if (postponed.cmd == UpdateOp.MOVE) {
+                int start, end;
+                if (postponed.positionStart < postponed.itemCount) {
+                    start = postponed.positionStart;
+                    end = postponed.itemCount;
+                } else {
+                    start = postponed.itemCount;
+                    end = postponed.positionStart;
+                }
+                if (pos >= start && pos <= end) {
+                    //i'm affected
+                    if (start == postponed.positionStart) {
+                        if (cmd == UpdateOp.ADD) {
+                            postponed.itemCount++;
+                        } else if (cmd == UpdateOp.REMOVE) {
+                            postponed.itemCount--;
+                        }
+                        // op moved to left, move it right to revert
+                        pos++;
+                    } else {
+                        if (cmd == UpdateOp.ADD) {
+                            postponed.positionStart++;
+                        } else if (cmd == UpdateOp.REMOVE) {
+                            postponed.positionStart--;
+                        }
+                        // op was moved right, move left to revert
+                        pos--;
+                    }
+                } else if (pos < postponed.positionStart) {
+                    // postponed MV is outside the dispatched OP. if it is before, offset
+                    if (cmd == UpdateOp.ADD) {
+                        postponed.positionStart++;
+                        postponed.itemCount++;
+                    } else if (cmd == UpdateOp.REMOVE) {
+                        postponed.positionStart--;
+                        postponed.itemCount--;
+                    }
+                }
+            } else {
+                if (postponed.positionStart <= pos) {
+                    if (postponed.cmd == UpdateOp.ADD) {
+                        pos -= postponed.itemCount;
+                    } else if (postponed.cmd == UpdateOp.REMOVE) {
+                        pos += postponed.itemCount;
+                    }
+                } else {
+                    if (cmd == UpdateOp.ADD) {
+                        postponed.positionStart++;
+                    } else if (cmd == UpdateOp.REMOVE) {
+                        postponed.positionStart--;
+                    }
+                }
+            }
+            if (DEBUG) {
+                Log.d(TAG, "dispath (step" + i + ")");
+                Log.d(TAG, "postponed state:" + i + ", pos:" + pos);
+                for (UpdateOp updateOp : mPostponedList) {
+                    Log.d(TAG, updateOp.toString());
+                }
+                Log.d(TAG, "----");
+            }
+        }
+        for (int i = mPostponedList.size() - 1; i >= 0; i--) {
+            UpdateOp op = mPostponedList.get(i);
+            if (op.cmd == UpdateOp.MOVE) {
+                if (op.itemCount == op.positionStart || op.itemCount < 0) {
+                    mPostponedList.remove(i);
+                    recycleUpdateOp(op);
+                }
+            } else if (op.itemCount <= 0) {
+                mPostponedList.remove(i);
+                recycleUpdateOp(op);
+            }
+        }
+        return pos;
+    }
+
+    private boolean canFindInPreLayout(int position) {
+        final int count = mPostponedList.size();
+        for (int i = 0; i < count; i++) {
+            UpdateOp op = mPostponedList.get(i);
+            if (op.cmd == UpdateOp.MOVE) {
+                if (findPositionOffset(op.itemCount, i + 1) == position) {
+                    return true;
+                }
+            } else if (op.cmd == UpdateOp.ADD) {
+                // TODO optimize.
+                final int end = op.positionStart + op.itemCount;
+                for (int pos = op.positionStart; pos < end; pos++) {
+                    if (findPositionOffset(pos, i + 1) == position) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private void applyAdd(UpdateOp op) {
+        postponeAndUpdateViewHolders(op);
+    }
+
+    private void postponeAndUpdateViewHolders(UpdateOp op) {
+        if (DEBUG) {
+            Log.d(TAG, "postponing " + op);
+        }
+        mPostponedList.add(op);
+        switch (op.cmd) {
+            case UpdateOp.ADD:
+                mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
+                break;
+            case UpdateOp.MOVE:
+                mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
+                break;
+            case UpdateOp.REMOVE:
+                mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,
+                        op.itemCount);
+                break;
+            case UpdateOp.UPDATE:
+                mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown update op type for " + op);
+        }
+    }
+
+    boolean hasPendingUpdates() {
+        return mPendingUpdates.size() > 0;
+    }
+
+    boolean hasAnyUpdateTypes(int updateTypes) {
+        return (mExistingUpdateTypes & updateTypes) != 0;
+    }
+
+    int findPositionOffset(int position) {
+        return findPositionOffset(position, 0);
+    }
+
+    int findPositionOffset(int position, int firstPostponedItem) {
+        int count = mPostponedList.size();
+        for (int i = firstPostponedItem; i < count; ++i) {
+            UpdateOp op = mPostponedList.get(i);
+            if (op.cmd == UpdateOp.MOVE) {
+                if (op.positionStart == position) {
+                    position = op.itemCount;
+                } else {
+                    if (op.positionStart < position) {
+                        position--; // like a remove
+                    }
+                    if (op.itemCount <= position) {
+                        position++; // like an add
+                    }
+                }
+            } else if (op.positionStart <= position) {
+                if (op.cmd == UpdateOp.REMOVE) {
+                    if (position < op.positionStart + op.itemCount) {
+                        return -1;
+                    }
+                    position -= op.itemCount;
+                } else if (op.cmd == UpdateOp.ADD) {
+                    position += op.itemCount;
+                }
+            }
+        }
+        return position;
+    }
+
+    /**
+     * @return True if updates should be processed.
+     */
+    boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
+        if (itemCount < 1) {
+            return false;
+        }
+        mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
+        mExistingUpdateTypes |= UpdateOp.UPDATE;
+        return mPendingUpdates.size() == 1;
+    }
+
+    /**
+     * @return True if updates should be processed.
+     */
+    boolean onItemRangeInserted(int positionStart, int itemCount) {
+        if (itemCount < 1) {
+            return false;
+        }
+        mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount, null));
+        mExistingUpdateTypes |= UpdateOp.ADD;
+        return mPendingUpdates.size() == 1;
+    }
+
+    /**
+     * @return True if updates should be processed.
+     */
+    boolean onItemRangeRemoved(int positionStart, int itemCount) {
+        if (itemCount < 1) {
+            return false;
+        }
+        mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
+        mExistingUpdateTypes |= UpdateOp.REMOVE;
+        return mPendingUpdates.size() == 1;
+    }
+
+    /**
+     * @return True if updates should be processed.
+     */
+    boolean onItemRangeMoved(int from, int to, int itemCount) {
+        if (from == to) {
+            return false; // no-op
+        }
+        if (itemCount != 1) {
+            throw new IllegalArgumentException("Moving more than 1 item is not supported yet");
+        }
+        mPendingUpdates.add(obtainUpdateOp(UpdateOp.MOVE, from, to, null));
+        mExistingUpdateTypes |= UpdateOp.MOVE;
+        return mPendingUpdates.size() == 1;
+    }
+
+    /**
+     * Skips pre-processing and applies all updates in one pass.
+     */
+    void consumeUpdatesInOnePass() {
+        // we still consume postponed updates (if there is) in case there was a pre-process call
+        // w/o a matching consumePostponedUpdates.
+        consumePostponedUpdates();
+        final int count = mPendingUpdates.size();
+        for (int i = 0; i < count; i++) {
+            UpdateOp op = mPendingUpdates.get(i);
+            switch (op.cmd) {
+                case UpdateOp.ADD:
+                    mCallback.onDispatchSecondPass(op);
+                    mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
+                    break;
+                case UpdateOp.REMOVE:
+                    mCallback.onDispatchSecondPass(op);
+                    mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount);
+                    break;
+                case UpdateOp.UPDATE:
+                    mCallback.onDispatchSecondPass(op);
+                    mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
+                    break;
+                case UpdateOp.MOVE:
+                    mCallback.onDispatchSecondPass(op);
+                    mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
+                    break;
+            }
+            if (mOnItemProcessedCallback != null) {
+                mOnItemProcessedCallback.run();
+            }
+        }
+        recycleUpdateOpsAndClearList(mPendingUpdates);
+        mExistingUpdateTypes = 0;
+    }
+
+    public int applyPendingUpdatesToPosition(int position) {
+        final int size = mPendingUpdates.size();
+        for (int i = 0; i < size; i++) {
+            UpdateOp op = mPendingUpdates.get(i);
+            switch (op.cmd) {
+                case UpdateOp.ADD:
+                    if (op.positionStart <= position) {
+                        position += op.itemCount;
+                    }
+                    break;
+                case UpdateOp.REMOVE:
+                    if (op.positionStart <= position) {
+                        final int end = op.positionStart + op.itemCount;
+                        if (end > position) {
+                            return RecyclerView.NO_POSITION;
+                        }
+                        position -= op.itemCount;
+                    }
+                    break;
+                case UpdateOp.MOVE:
+                    if (op.positionStart == position) {
+                        position = op.itemCount; //position end
+                    } else {
+                        if (op.positionStart < position) {
+                            position -= 1;
+                        }
+                        if (op.itemCount <= position) {
+                            position += 1;
+                        }
+                    }
+                    break;
+            }
+        }
+        return position;
+    }
+
+    boolean hasUpdates() {
+        return !mPostponedList.isEmpty() && !mPendingUpdates.isEmpty();
+    }
+
+    /**
+     * Queued operation to happen when child views are updated.
+     */
+    static class UpdateOp {
+
+        static final int ADD = 1;
+
+        static final int REMOVE = 1 << 1;
+
+        static final int UPDATE = 1 << 2;
+
+        static final int MOVE = 1 << 3;
+
+        static final int POOL_SIZE = 30;
+
+        int cmd;
+
+        int positionStart;
+
+        Object payload;
+
+        // holds the target position if this is a MOVE
+        int itemCount;
+
+        UpdateOp(int cmd, int positionStart, int itemCount, Object payload) {
+            this.cmd = cmd;
+            this.positionStart = positionStart;
+            this.itemCount = itemCount;
+            this.payload = payload;
+        }
+
+        String cmdToString() {
+            switch (cmd) {
+                case ADD:
+                    return "add";
+                case REMOVE:
+                    return "rm";
+                case UPDATE:
+                    return "up";
+                case MOVE:
+                    return "mv";
+            }
+            return "??";
+        }
+
+        @Override
+        public String toString() {
+            return Integer.toHexString(System.identityHashCode(this))
+                    + "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount
+                    + ",p:" + payload + "]";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            UpdateOp op = (UpdateOp) o;
+
+            if (cmd != op.cmd) {
+                return false;
+            }
+            if (cmd == MOVE && Math.abs(itemCount - positionStart) == 1) {
+                // reverse of this is also true
+                if (itemCount == op.positionStart && positionStart == op.itemCount) {
+                    return true;
+                }
+            }
+            if (itemCount != op.itemCount) {
+                return false;
+            }
+            if (positionStart != op.positionStart) {
+                return false;
+            }
+            if (payload != null) {
+                if (!payload.equals(op.payload)) {
+                    return false;
+                }
+            } else if (op.payload != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = cmd;
+            result = 31 * result + positionStart;
+            result = 31 * result + itemCount;
+            return result;
+        }
+    }
+
+    @Override
+    public UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount, Object payload) {
+        UpdateOp op = mUpdateOpPool.acquire();
+        if (op == null) {
+            op = new UpdateOp(cmd, positionStart, itemCount, payload);
+        } else {
+            op.cmd = cmd;
+            op.positionStart = positionStart;
+            op.itemCount = itemCount;
+            op.payload = payload;
+        }
+        return op;
+    }
+
+    @Override
+    public void recycleUpdateOp(UpdateOp op) {
+        if (!mDisableRecycler) {
+            op.payload = null;
+            mUpdateOpPool.release(op);
+        }
+    }
+
+    void recycleUpdateOpsAndClearList(List<UpdateOp> ops) {
+        final int count = ops.size();
+        for (int i = 0; i < count; i++) {
+            recycleUpdateOp(ops.get(i));
+        }
+        ops.clear();
+    }
+
+    /**
+     * Contract between AdapterHelper and RecyclerView.
+     */
+    interface Callback {
+
+        RecyclerView.ViewHolder findViewHolder(int position);
+
+        void offsetPositionsForRemovingInvisible(int positionStart, int itemCount);
+
+        void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount);
+
+        void markViewHoldersUpdated(int positionStart, int itemCount, Object payloads);
+
+        void onDispatchFirstPass(UpdateOp updateOp);
+
+        void onDispatchSecondPass(UpdateOp updateOp);
+
+        void offsetPositionsForAdd(int positionStart, int itemCount);
+
+        void offsetPositionsForMove(int from, int to);
+    }
+}
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 20230cd..b172dbc 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -187,6 +187,7 @@
     }
 
     @Override
+    @RemotableViewMethod
     public void setVisibility(int visibility) {
         mDesiredVisibility = visibility;
         updateVisibility();
diff --git a/core/java/com/android/internal/widget/ChildHelper.java b/core/java/com/android/internal/widget/ChildHelper.java
new file mode 100644
index 0000000..e9136d0
--- /dev/null
+++ b/core/java/com/android/internal/widget/ChildHelper.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to manage children.
+ * <p>
+ * It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods
+ * provided by this class. <b>Regular</b> methods are the ones that replicate ViewGroup methods
+ * like getChildAt, getChildCount etc. These methods ignore hidden children.
+ * <p>
+ * When RecyclerView needs direct access to the view group children, it can call unfiltered
+ * methods like get getUnfilteredChildCount or getUnfilteredChildAt.
+ */
+class ChildHelper {
+
+    private static final boolean DEBUG = false;
+
+    private static final String TAG = "ChildrenHelper";
+
+    final Callback mCallback;
+
+    final Bucket mBucket;
+
+    final List<View> mHiddenViews;
+
+    ChildHelper(Callback callback) {
+        mCallback = callback;
+        mBucket = new Bucket();
+        mHiddenViews = new ArrayList<View>();
+    }
+
+    /**
+     * Marks a child view as hidden
+     *
+     * @param child  View to hide.
+     */
+    private void hideViewInternal(View child) {
+        mHiddenViews.add(child);
+        mCallback.onEnteredHiddenState(child);
+    }
+
+    /**
+     * Unmarks a child view as hidden.
+     *
+     * @param child  View to hide.
+     */
+    private boolean unhideViewInternal(View child) {
+        if (mHiddenViews.remove(child)) {
+            mCallback.onLeftHiddenState(child);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Adds a view to the ViewGroup
+     *
+     * @param child  View to add.
+     * @param hidden If set to true, this item will be invisible from regular methods.
+     */
+    void addView(View child, boolean hidden) {
+        addView(child, -1, hidden);
+    }
+
+    /**
+     * Add a view to the ViewGroup at an index
+     *
+     * @param child  View to add.
+     * @param index  Index of the child from the regular perspective (excluding hidden views).
+     *               ChildHelper offsets this index to actual ViewGroup index.
+     * @param hidden If set to true, this item will be invisible from regular methods.
+     */
+    void addView(View child, int index, boolean hidden) {
+        final int offset;
+        if (index < 0) {
+            offset = mCallback.getChildCount();
+        } else {
+            offset = getOffset(index);
+        }
+        mBucket.insert(offset, hidden);
+        if (hidden) {
+            hideViewInternal(child);
+        }
+        mCallback.addView(child, offset);
+        if (DEBUG) {
+            Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this);
+        }
+    }
+
+    private int getOffset(int index) {
+        if (index < 0) {
+            return -1; //anything below 0 won't work as diff will be undefined.
+        }
+        final int limit = mCallback.getChildCount();
+        int offset = index;
+        while (offset < limit) {
+            final int removedBefore = mBucket.countOnesBefore(offset);
+            final int diff = index - (offset - removedBefore);
+            if (diff == 0) {
+                while (mBucket.get(offset)) { // ensure this offset is not hidden
+                    offset++;
+                }
+                return offset;
+            } else {
+                offset += diff;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Removes the provided View from underlying RecyclerView.
+     *
+     * @param view The view to remove.
+     */
+    void removeView(View view) {
+        int index = mCallback.indexOfChild(view);
+        if (index < 0) {
+            return;
+        }
+        if (mBucket.remove(index)) {
+            unhideViewInternal(view);
+        }
+        mCallback.removeViewAt(index);
+        if (DEBUG) {
+            Log.d(TAG, "remove View off:" + index + "," + this);
+        }
+    }
+
+    /**
+     * Removes the view at the provided index from RecyclerView.
+     *
+     * @param index Index of the child from the regular perspective (excluding hidden views).
+     *              ChildHelper offsets this index to actual ViewGroup index.
+     */
+    void removeViewAt(int index) {
+        final int offset = getOffset(index);
+        final View view = mCallback.getChildAt(offset);
+        if (view == null) {
+            return;
+        }
+        if (mBucket.remove(offset)) {
+            unhideViewInternal(view);
+        }
+        mCallback.removeViewAt(offset);
+        if (DEBUG) {
+            Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this);
+        }
+    }
+
+    /**
+     * Returns the child at provided index.
+     *
+     * @param index Index of the child to return in regular perspective.
+     */
+    View getChildAt(int index) {
+        final int offset = getOffset(index);
+        return mCallback.getChildAt(offset);
+    }
+
+    /**
+     * Removes all views from the ViewGroup including the hidden ones.
+     */
+    void removeAllViewsUnfiltered() {
+        mBucket.reset();
+        for (int i = mHiddenViews.size() - 1; i >= 0; i--) {
+            mCallback.onLeftHiddenState(mHiddenViews.get(i));
+            mHiddenViews.remove(i);
+        }
+        mCallback.removeAllViews();
+        if (DEBUG) {
+            Log.d(TAG, "removeAllViewsUnfiltered");
+        }
+    }
+
+    /**
+     * This can be used to find a disappearing view by position.
+     *
+     * @param position The adapter position of the item.
+     * @return         A hidden view with a valid ViewHolder that matches the position.
+     */
+    View findHiddenNonRemovedView(int position) {
+        final int count = mHiddenViews.size();
+        for (int i = 0; i < count; i++) {
+            final View view = mHiddenViews.get(i);
+            RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view);
+            if (holder.getLayoutPosition() == position
+                    && !holder.isInvalid()
+                    && !holder.isRemoved()) {
+                return view;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Attaches the provided view to the underlying ViewGroup.
+     *
+     * @param child        Child to attach.
+     * @param index        Index of the child to attach in regular perspective.
+     * @param layoutParams LayoutParams for the child.
+     * @param hidden       If set to true, this item will be invisible to the regular methods.
+     */
+    void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams,
+            boolean hidden) {
+        final int offset;
+        if (index < 0) {
+            offset = mCallback.getChildCount();
+        } else {
+            offset = getOffset(index);
+        }
+        mBucket.insert(offset, hidden);
+        if (hidden) {
+            hideViewInternal(child);
+        }
+        mCallback.attachViewToParent(child, offset, layoutParams);
+        if (DEBUG) {
+            Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + ","
+                    + "h:" + hidden + ", " + this);
+        }
+    }
+
+    /**
+     * Returns the number of children that are not hidden.
+     *
+     * @return Number of children that are not hidden.
+     * @see #getChildAt(int)
+     */
+    int getChildCount() {
+        return mCallback.getChildCount() - mHiddenViews.size();
+    }
+
+    /**
+     * Returns the total number of children.
+     *
+     * @return The total number of children including the hidden views.
+     * @see #getUnfilteredChildAt(int)
+     */
+    int getUnfilteredChildCount() {
+        return mCallback.getChildCount();
+    }
+
+    /**
+     * Returns a child by ViewGroup offset. ChildHelper won't offset this index.
+     *
+     * @param index ViewGroup index of the child to return.
+     * @return The view in the provided index.
+     */
+    View getUnfilteredChildAt(int index) {
+        return mCallback.getChildAt(index);
+    }
+
+    /**
+     * Detaches the view at the provided index.
+     *
+     * @param index Index of the child to return in regular perspective.
+     */
+    void detachViewFromParent(int index) {
+        final int offset = getOffset(index);
+        mBucket.remove(offset);
+        mCallback.detachViewFromParent(offset);
+        if (DEBUG) {
+            Log.d(TAG, "detach view from parent " + index + ", off:" + offset);
+        }
+    }
+
+    /**
+     * Returns the index of the child in regular perspective.
+     *
+     * @param child The child whose index will be returned.
+     * @return The regular perspective index of the child or -1 if it does not exists.
+     */
+    int indexOfChild(View child) {
+        final int index = mCallback.indexOfChild(child);
+        if (index == -1) {
+            return -1;
+        }
+        if (mBucket.get(index)) {
+            if (DEBUG) {
+                throw new IllegalArgumentException("cannot get index of a hidden child");
+            } else {
+                return -1;
+            }
+        }
+        // reverse the index
+        return index - mBucket.countOnesBefore(index);
+    }
+
+    /**
+     * Returns whether a View is visible to LayoutManager or not.
+     *
+     * @param view The child view to check. Should be a child of the Callback.
+     * @return True if the View is not visible to LayoutManager
+     */
+    boolean isHidden(View view) {
+        return mHiddenViews.contains(view);
+    }
+
+    /**
+     * Marks a child view as hidden.
+     *
+     * @param view The view to hide.
+     */
+    void hide(View view) {
+        final int offset = mCallback.indexOfChild(view);
+        if (offset < 0) {
+            throw new IllegalArgumentException("view is not a child, cannot hide " + view);
+        }
+        if (DEBUG && mBucket.get(offset)) {
+            throw new RuntimeException("trying to hide same view twice, how come ? " + view);
+        }
+        mBucket.set(offset);
+        hideViewInternal(view);
+        if (DEBUG) {
+            Log.d(TAG, "hiding child " + view + " at offset " + offset + ", " + this);
+        }
+    }
+
+    /**
+     * Moves a child view from hidden list to regular list.
+     * Calling this method should probably be followed by a detach, otherwise, it will suddenly
+     * show up in LayoutManager's children list.
+     *
+     * @param view The hidden View to unhide
+     */
+    void unhide(View view) {
+        final int offset = mCallback.indexOfChild(view);
+        if (offset < 0) {
+            throw new IllegalArgumentException("view is not a child, cannot hide " + view);
+        }
+        if (!mBucket.get(offset)) {
+            throw new RuntimeException("trying to unhide a view that was not hidden" + view);
+        }
+        mBucket.clear(offset);
+        unhideViewInternal(view);
+    }
+
+    @Override
+    public String toString() {
+        return mBucket.toString() + ", hidden list:" + mHiddenViews.size();
+    }
+
+    /**
+     * Removes a view from the ViewGroup if it is hidden.
+     *
+     * @param view The view to remove.
+     * @return True if the View is found and it is hidden. False otherwise.
+     */
+    boolean removeViewIfHidden(View view) {
+        final int index = mCallback.indexOfChild(view);
+        if (index == -1) {
+            if (unhideViewInternal(view) && DEBUG) {
+                throw new IllegalStateException("view is in hidden list but not in view group");
+            }
+            return true;
+        }
+        if (mBucket.get(index)) {
+            mBucket.remove(index);
+            if (!unhideViewInternal(view) && DEBUG) {
+                throw new IllegalStateException(
+                        "removed a hidden view but it is not in hidden views list");
+            }
+            mCallback.removeViewAt(index);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Bitset implementation that provides methods to offset indices.
+     */
+    static class Bucket {
+
+        static final int BITS_PER_WORD = Long.SIZE;
+
+        static final long LAST_BIT = 1L << (Long.SIZE - 1);
+
+        long mData = 0;
+
+        Bucket mNext;
+
+        void set(int index) {
+            if (index >= BITS_PER_WORD) {
+                ensureNext();
+                mNext.set(index - BITS_PER_WORD);
+            } else {
+                mData |= 1L << index;
+            }
+        }
+
+        private void ensureNext() {
+            if (mNext == null) {
+                mNext = new Bucket();
+            }
+        }
+
+        void clear(int index) {
+            if (index >= BITS_PER_WORD) {
+                if (mNext != null) {
+                    mNext.clear(index - BITS_PER_WORD);
+                }
+            } else {
+                mData &= ~(1L << index);
+            }
+
+        }
+
+        boolean get(int index) {
+            if (index >= BITS_PER_WORD) {
+                ensureNext();
+                return mNext.get(index - BITS_PER_WORD);
+            } else {
+                return (mData & (1L << index)) != 0;
+            }
+        }
+
+        void reset() {
+            mData = 0;
+            if (mNext != null) {
+                mNext.reset();
+            }
+        }
+
+        void insert(int index, boolean value) {
+            if (index >= BITS_PER_WORD) {
+                ensureNext();
+                mNext.insert(index - BITS_PER_WORD, value);
+            } else {
+                final boolean lastBit = (mData & LAST_BIT) != 0;
+                long mask = (1L << index) - 1;
+                final long before = mData & mask;
+                final long after = ((mData & ~mask)) << 1;
+                mData = before | after;
+                if (value) {
+                    set(index);
+                } else {
+                    clear(index);
+                }
+                if (lastBit || mNext != null) {
+                    ensureNext();
+                    mNext.insert(0, lastBit);
+                }
+            }
+        }
+
+        boolean remove(int index) {
+            if (index >= BITS_PER_WORD) {
+                ensureNext();
+                return mNext.remove(index - BITS_PER_WORD);
+            } else {
+                long mask = (1L << index);
+                final boolean value = (mData & mask) != 0;
+                mData &= ~mask;
+                mask = mask - 1;
+                final long before = mData & mask;
+                // cannot use >> because it adds one.
+                final long after = Long.rotateRight(mData & ~mask, 1);
+                mData = before | after;
+                if (mNext != null) {
+                    if (mNext.get(0)) {
+                        set(BITS_PER_WORD - 1);
+                    }
+                    mNext.remove(0);
+                }
+                return value;
+            }
+        }
+
+        int countOnesBefore(int index) {
+            if (mNext == null) {
+                if (index >= BITS_PER_WORD) {
+                    return Long.bitCount(mData);
+                }
+                return Long.bitCount(mData & ((1L << index) - 1));
+            }
+            if (index < BITS_PER_WORD) {
+                return Long.bitCount(mData & ((1L << index) - 1));
+            } else {
+                return mNext.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return mNext == null ? Long.toBinaryString(mData)
+                    : mNext.toString() + "xx" + Long.toBinaryString(mData);
+        }
+    }
+
+    interface Callback {
+
+        int getChildCount();
+
+        void addView(View child, int index);
+
+        int indexOfChild(View view);
+
+        void removeViewAt(int index);
+
+        View getChildAt(int offset);
+
+        void removeAllViews();
+
+        RecyclerView.ViewHolder getChildViewHolder(View view);
+
+        void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams);
+
+        void detachViewFromParent(int offset);
+
+        void onEnteredHiddenState(View child);
+
+        void onLeftHiddenState(View child);
+    }
+}
+
diff --git a/core/java/com/android/internal/widget/DefaultItemAnimator.java b/core/java/com/android/internal/widget/DefaultItemAnimator.java
new file mode 100644
index 0000000..92345af
--- /dev/null
+++ b/core/java/com/android/internal/widget/DefaultItemAnimator.java
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+
+import com.android.internal.widget.RecyclerView.ViewHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This implementation of {@link RecyclerView.ItemAnimator} provides basic
+ * animations on remove, add, and move events that happen to the items in
+ * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.
+ *
+ * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)
+ */
+public class DefaultItemAnimator extends SimpleItemAnimator {
+    private static final boolean DEBUG = false;
+
+    private static TimeInterpolator sDefaultInterpolator;
+
+    private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
+    private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
+    private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
+    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
+
+    ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
+    ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
+    ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
+
+    ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
+    ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
+    ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
+    ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
+
+    private static class MoveInfo {
+        public ViewHolder holder;
+        public int fromX, fromY, toX, toY;
+
+        MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
+            this.holder = holder;
+            this.fromX = fromX;
+            this.fromY = fromY;
+            this.toX = toX;
+            this.toY = toY;
+        }
+    }
+
+    private static class ChangeInfo {
+        public ViewHolder oldHolder, newHolder;
+        public int fromX, fromY, toX, toY;
+        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
+            this.oldHolder = oldHolder;
+            this.newHolder = newHolder;
+        }
+
+        ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
+                int fromX, int fromY, int toX, int toY) {
+            this(oldHolder, newHolder);
+            this.fromX = fromX;
+            this.fromY = fromY;
+            this.toX = toX;
+            this.toY = toY;
+        }
+
+        @Override
+        public String toString() {
+            return "ChangeInfo{"
+                    + "oldHolder=" + oldHolder
+                    + ", newHolder=" + newHolder
+                    + ", fromX=" + fromX
+                    + ", fromY=" + fromY
+                    + ", toX=" + toX
+                    + ", toY=" + toY
+                    + '}';
+        }
+    }
+
+    @Override
+    public void runPendingAnimations() {
+        boolean removalsPending = !mPendingRemovals.isEmpty();
+        boolean movesPending = !mPendingMoves.isEmpty();
+        boolean changesPending = !mPendingChanges.isEmpty();
+        boolean additionsPending = !mPendingAdditions.isEmpty();
+        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
+            // nothing to animate
+            return;
+        }
+        // First, remove stuff
+        for (ViewHolder holder : mPendingRemovals) {
+            animateRemoveImpl(holder);
+        }
+        mPendingRemovals.clear();
+        // Next, move stuff
+        if (movesPending) {
+            final ArrayList<MoveInfo> moves = new ArrayList<>();
+            moves.addAll(mPendingMoves);
+            mMovesList.add(moves);
+            mPendingMoves.clear();
+            Runnable mover = new Runnable() {
+                @Override
+                public void run() {
+                    for (MoveInfo moveInfo : moves) {
+                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
+                                moveInfo.toX, moveInfo.toY);
+                    }
+                    moves.clear();
+                    mMovesList.remove(moves);
+                }
+            };
+            if (removalsPending) {
+                View view = moves.get(0).holder.itemView;
+                view.postOnAnimationDelayed(mover, getRemoveDuration());
+            } else {
+                mover.run();
+            }
+        }
+        // Next, change stuff, to run in parallel with move animations
+        if (changesPending) {
+            final ArrayList<ChangeInfo> changes = new ArrayList<>();
+            changes.addAll(mPendingChanges);
+            mChangesList.add(changes);
+            mPendingChanges.clear();
+            Runnable changer = new Runnable() {
+                @Override
+                public void run() {
+                    for (ChangeInfo change : changes) {
+                        animateChangeImpl(change);
+                    }
+                    changes.clear();
+                    mChangesList.remove(changes);
+                }
+            };
+            if (removalsPending) {
+                ViewHolder holder = changes.get(0).oldHolder;
+                holder.itemView.postOnAnimationDelayed(changer, getRemoveDuration());
+            } else {
+                changer.run();
+            }
+        }
+        // Next, add stuff
+        if (additionsPending) {
+            final ArrayList<ViewHolder> additions = new ArrayList<>();
+            additions.addAll(mPendingAdditions);
+            mAdditionsList.add(additions);
+            mPendingAdditions.clear();
+            Runnable adder = new Runnable() {
+                @Override
+                public void run() {
+                    for (ViewHolder holder : additions) {
+                        animateAddImpl(holder);
+                    }
+                    additions.clear();
+                    mAdditionsList.remove(additions);
+                }
+            };
+            if (removalsPending || movesPending || changesPending) {
+                long removeDuration = removalsPending ? getRemoveDuration() : 0;
+                long moveDuration = movesPending ? getMoveDuration() : 0;
+                long changeDuration = changesPending ? getChangeDuration() : 0;
+                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
+                View view = additions.get(0).itemView;
+                view.postOnAnimationDelayed(adder, totalDelay);
+            } else {
+                adder.run();
+            }
+        }
+    }
+
+    @Override
+    public boolean animateRemove(final ViewHolder holder) {
+        resetAnimation(holder);
+        mPendingRemovals.add(holder);
+        return true;
+    }
+
+    private void animateRemoveImpl(final ViewHolder holder) {
+        final View view = holder.itemView;
+        final ViewPropertyAnimator animation = view.animate();
+        mRemoveAnimations.add(holder);
+        animation.setDuration(getRemoveDuration()).alpha(0).setListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animator) {
+                        dispatchRemoveStarting(holder);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animator) {
+                        animation.setListener(null);
+                        view.setAlpha(1);
+                        dispatchRemoveFinished(holder);
+                        mRemoveAnimations.remove(holder);
+                        dispatchFinishedWhenDone();
+                    }
+                }).start();
+    }
+
+    @Override
+    public boolean animateAdd(final ViewHolder holder) {
+        resetAnimation(holder);
+        holder.itemView.setAlpha(0);
+        mPendingAdditions.add(holder);
+        return true;
+    }
+
+    void animateAddImpl(final ViewHolder holder) {
+        final View view = holder.itemView;
+        final ViewPropertyAnimator animation = view.animate();
+        mAddAnimations.add(holder);
+        animation.alpha(1).setDuration(getAddDuration())
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animator) {
+                        dispatchAddStarting(holder);
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animator) {
+                        view.setAlpha(1);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animator) {
+                        animation.setListener(null);
+                        dispatchAddFinished(holder);
+                        mAddAnimations.remove(holder);
+                        dispatchFinishedWhenDone();
+                    }
+                }).start();
+    }
+
+    @Override
+    public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
+            int toX, int toY) {
+        final View view = holder.itemView;
+        fromX += holder.itemView.getTranslationX();
+        fromY += holder.itemView.getTranslationY();
+        resetAnimation(holder);
+        int deltaX = toX - fromX;
+        int deltaY = toY - fromY;
+        if (deltaX == 0 && deltaY == 0) {
+            dispatchMoveFinished(holder);
+            return false;
+        }
+        if (deltaX != 0) {
+            view.setTranslationX(-deltaX);
+        }
+        if (deltaY != 0) {
+            view.setTranslationY(-deltaY);
+        }
+        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
+        return true;
+    }
+
+    void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
+        final View view = holder.itemView;
+        final int deltaX = toX - fromX;
+        final int deltaY = toY - fromY;
+        if (deltaX != 0) {
+            view.animate().translationX(0);
+        }
+        if (deltaY != 0) {
+            view.animate().translationY(0);
+        }
+        // TODO: make EndActions end listeners instead, since end actions aren't called when
+        // vpas are canceled (and can't end them. why?)
+        // need listener functionality in VPACompat for this. Ick.
+        final ViewPropertyAnimator animation = view.animate();
+        mMoveAnimations.add(holder);
+        animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animator) {
+                dispatchMoveStarting(holder);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animator) {
+                if (deltaX != 0) {
+                    view.setTranslationX(0);
+                }
+                if (deltaY != 0) {
+                    view.setTranslationY(0);
+                }
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                animation.setListener(null);
+                dispatchMoveFinished(holder);
+                mMoveAnimations.remove(holder);
+                dispatchFinishedWhenDone();
+            }
+        }).start();
+    }
+
+    @Override
+    public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
+            int fromX, int fromY, int toX, int toY) {
+        if (oldHolder == newHolder) {
+            // Don't know how to run change animations when the same view holder is re-used.
+            // run a move animation to handle position changes.
+            return animateMove(oldHolder, fromX, fromY, toX, toY);
+        }
+        final float prevTranslationX = oldHolder.itemView.getTranslationX();
+        final float prevTranslationY = oldHolder.itemView.getTranslationY();
+        final float prevAlpha = oldHolder.itemView.getAlpha();
+        resetAnimation(oldHolder);
+        int deltaX = (int) (toX - fromX - prevTranslationX);
+        int deltaY = (int) (toY - fromY - prevTranslationY);
+        // recover prev translation state after ending animation
+        oldHolder.itemView.setTranslationX(prevTranslationX);
+        oldHolder.itemView.setTranslationY(prevTranslationY);
+        oldHolder.itemView.setAlpha(prevAlpha);
+        if (newHolder != null) {
+            // carry over translation values
+            resetAnimation(newHolder);
+            newHolder.itemView.setTranslationX(-deltaX);
+            newHolder.itemView.setTranslationY(-deltaY);
+            newHolder.itemView.setAlpha(0);
+        }
+        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
+        return true;
+    }
+
+    void animateChangeImpl(final ChangeInfo changeInfo) {
+        final ViewHolder holder = changeInfo.oldHolder;
+        final View view = holder == null ? null : holder.itemView;
+        final ViewHolder newHolder = changeInfo.newHolder;
+        final View newView = newHolder != null ? newHolder.itemView : null;
+        if (view != null) {
+            final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
+                    getChangeDuration());
+            mChangeAnimations.add(changeInfo.oldHolder);
+            oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
+            oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
+            oldViewAnim.alpha(0).setListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animator) {
+                    dispatchChangeStarting(changeInfo.oldHolder, true);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animator) {
+                    oldViewAnim.setListener(null);
+                    view.setAlpha(1);
+                    view.setTranslationX(0);
+                    view.setTranslationY(0);
+                    dispatchChangeFinished(changeInfo.oldHolder, true);
+                    mChangeAnimations.remove(changeInfo.oldHolder);
+                    dispatchFinishedWhenDone();
+                }
+            }).start();
+        }
+        if (newView != null) {
+            final ViewPropertyAnimator newViewAnimation = newView.animate();
+            mChangeAnimations.add(changeInfo.newHolder);
+            newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
+                    .alpha(1).setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(Animator animator) {
+                            dispatchChangeStarting(changeInfo.newHolder, false);
+                        }
+                        @Override
+                        public void onAnimationEnd(Animator animator) {
+                            newViewAnimation.setListener(null);
+                            newView.setAlpha(1);
+                            newView.setTranslationX(0);
+                            newView.setTranslationY(0);
+                            dispatchChangeFinished(changeInfo.newHolder, false);
+                            mChangeAnimations.remove(changeInfo.newHolder);
+                            dispatchFinishedWhenDone();
+                        }
+                    }).start();
+        }
+    }
+
+    private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {
+        for (int i = infoList.size() - 1; i >= 0; i--) {
+            ChangeInfo changeInfo = infoList.get(i);
+            if (endChangeAnimationIfNecessary(changeInfo, item)) {
+                if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
+                    infoList.remove(changeInfo);
+                }
+            }
+        }
+    }
+
+    private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
+        if (changeInfo.oldHolder != null) {
+            endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
+        }
+        if (changeInfo.newHolder != null) {
+            endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
+        }
+    }
+    private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
+        boolean oldItem = false;
+        if (changeInfo.newHolder == item) {
+            changeInfo.newHolder = null;
+        } else if (changeInfo.oldHolder == item) {
+            changeInfo.oldHolder = null;
+            oldItem = true;
+        } else {
+            return false;
+        }
+        item.itemView.setAlpha(1);
+        item.itemView.setTranslationX(0);
+        item.itemView.setTranslationY(0);
+        dispatchChangeFinished(item, oldItem);
+        return true;
+    }
+
+    @Override
+    public void endAnimation(ViewHolder item) {
+        final View view = item.itemView;
+        // this will trigger end callback which should set properties to their target values.
+        view.animate().cancel();
+        // TODO if some other animations are chained to end, how do we cancel them as well?
+        for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
+            MoveInfo moveInfo = mPendingMoves.get(i);
+            if (moveInfo.holder == item) {
+                view.setTranslationY(0);
+                view.setTranslationX(0);
+                dispatchMoveFinished(item);
+                mPendingMoves.remove(i);
+            }
+        }
+        endChangeAnimation(mPendingChanges, item);
+        if (mPendingRemovals.remove(item)) {
+            view.setAlpha(1);
+            dispatchRemoveFinished(item);
+        }
+        if (mPendingAdditions.remove(item)) {
+            view.setAlpha(1);
+            dispatchAddFinished(item);
+        }
+
+        for (int i = mChangesList.size() - 1; i >= 0; i--) {
+            ArrayList<ChangeInfo> changes = mChangesList.get(i);
+            endChangeAnimation(changes, item);
+            if (changes.isEmpty()) {
+                mChangesList.remove(i);
+            }
+        }
+        for (int i = mMovesList.size() - 1; i >= 0; i--) {
+            ArrayList<MoveInfo> moves = mMovesList.get(i);
+            for (int j = moves.size() - 1; j >= 0; j--) {
+                MoveInfo moveInfo = moves.get(j);
+                if (moveInfo.holder == item) {
+                    view.setTranslationY(0);
+                    view.setTranslationX(0);
+                    dispatchMoveFinished(item);
+                    moves.remove(j);
+                    if (moves.isEmpty()) {
+                        mMovesList.remove(i);
+                    }
+                    break;
+                }
+            }
+        }
+        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
+            ArrayList<ViewHolder> additions = mAdditionsList.get(i);
+            if (additions.remove(item)) {
+                view.setAlpha(1);
+                dispatchAddFinished(item);
+                if (additions.isEmpty()) {
+                    mAdditionsList.remove(i);
+                }
+            }
+        }
+
+        // animations should be ended by the cancel above.
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (mRemoveAnimations.remove(item) && DEBUG) {
+            throw new IllegalStateException("after animation is cancelled, item should not be in "
+                    + "mRemoveAnimations list");
+        }
+
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (mAddAnimations.remove(item) && DEBUG) {
+            throw new IllegalStateException("after animation is cancelled, item should not be in "
+                    + "mAddAnimations list");
+        }
+
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (mChangeAnimations.remove(item) && DEBUG) {
+            throw new IllegalStateException("after animation is cancelled, item should not be in "
+                    + "mChangeAnimations list");
+        }
+
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (mMoveAnimations.remove(item) && DEBUG) {
+            throw new IllegalStateException("after animation is cancelled, item should not be in "
+                    + "mMoveAnimations list");
+        }
+        dispatchFinishedWhenDone();
+    }
+
+    private void resetAnimation(ViewHolder holder) {
+        if (sDefaultInterpolator == null) {
+            sDefaultInterpolator = new ValueAnimator().getInterpolator();
+        }
+        holder.itemView.animate().setInterpolator(sDefaultInterpolator);
+        endAnimation(holder);
+    }
+
+    @Override
+    public boolean isRunning() {
+        return (!mPendingAdditions.isEmpty()
+                || !mPendingChanges.isEmpty()
+                || !mPendingMoves.isEmpty()
+                || !mPendingRemovals.isEmpty()
+                || !mMoveAnimations.isEmpty()
+                || !mRemoveAnimations.isEmpty()
+                || !mAddAnimations.isEmpty()
+                || !mChangeAnimations.isEmpty()
+                || !mMovesList.isEmpty()
+                || !mAdditionsList.isEmpty()
+                || !mChangesList.isEmpty());
+    }
+
+    /**
+     * Check the state of currently pending and running animations. If there are none
+     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
+     * listeners.
+     */
+    void dispatchFinishedWhenDone() {
+        if (!isRunning()) {
+            dispatchAnimationsFinished();
+        }
+    }
+
+    @Override
+    public void endAnimations() {
+        int count = mPendingMoves.size();
+        for (int i = count - 1; i >= 0; i--) {
+            MoveInfo item = mPendingMoves.get(i);
+            View view = item.holder.itemView;
+            view.setTranslationY(0);
+            view.setTranslationX(0);
+            dispatchMoveFinished(item.holder);
+            mPendingMoves.remove(i);
+        }
+        count = mPendingRemovals.size();
+        for (int i = count - 1; i >= 0; i--) {
+            ViewHolder item = mPendingRemovals.get(i);
+            dispatchRemoveFinished(item);
+            mPendingRemovals.remove(i);
+        }
+        count = mPendingAdditions.size();
+        for (int i = count - 1; i >= 0; i--) {
+            ViewHolder item = mPendingAdditions.get(i);
+            item.itemView.setAlpha(1);
+            dispatchAddFinished(item);
+            mPendingAdditions.remove(i);
+        }
+        count = mPendingChanges.size();
+        for (int i = count - 1; i >= 0; i--) {
+            endChangeAnimationIfNecessary(mPendingChanges.get(i));
+        }
+        mPendingChanges.clear();
+        if (!isRunning()) {
+            return;
+        }
+
+        int listCount = mMovesList.size();
+        for (int i = listCount - 1; i >= 0; i--) {
+            ArrayList<MoveInfo> moves = mMovesList.get(i);
+            count = moves.size();
+            for (int j = count - 1; j >= 0; j--) {
+                MoveInfo moveInfo = moves.get(j);
+                ViewHolder item = moveInfo.holder;
+                View view = item.itemView;
+                view.setTranslationY(0);
+                view.setTranslationX(0);
+                dispatchMoveFinished(moveInfo.holder);
+                moves.remove(j);
+                if (moves.isEmpty()) {
+                    mMovesList.remove(moves);
+                }
+            }
+        }
+        listCount = mAdditionsList.size();
+        for (int i = listCount - 1; i >= 0; i--) {
+            ArrayList<ViewHolder> additions = mAdditionsList.get(i);
+            count = additions.size();
+            for (int j = count - 1; j >= 0; j--) {
+                ViewHolder item = additions.get(j);
+                View view = item.itemView;
+                view.setAlpha(1);
+                dispatchAddFinished(item);
+                additions.remove(j);
+                if (additions.isEmpty()) {
+                    mAdditionsList.remove(additions);
+                }
+            }
+        }
+        listCount = mChangesList.size();
+        for (int i = listCount - 1; i >= 0; i--) {
+            ArrayList<ChangeInfo> changes = mChangesList.get(i);
+            count = changes.size();
+            for (int j = count - 1; j >= 0; j--) {
+                endChangeAnimationIfNecessary(changes.get(j));
+                if (changes.isEmpty()) {
+                    mChangesList.remove(changes);
+                }
+            }
+        }
+
+        cancelAll(mRemoveAnimations);
+        cancelAll(mMoveAnimations);
+        cancelAll(mAddAnimations);
+        cancelAll(mChangeAnimations);
+
+        dispatchAnimationsFinished();
+    }
+
+    void cancelAll(List<ViewHolder> viewHolders) {
+        for (int i = viewHolders.size() - 1; i >= 0; i--) {
+            viewHolders.get(i).itemView.animate().cancel();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
+     * When this is the case:
+     * <ul>
+     * <li>If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both
+     * ViewHolder arguments will be the same instance.
+     * </li>
+     * <li>
+     * If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)},
+     * then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and
+     * run a move animation instead.
+     * </li>
+     * </ul>
+     */
+    @Override
+    public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
+            @NonNull List<Object> payloads) {
+        return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
+    }
+}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 04e09a8..58e694a 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1591,7 +1591,7 @@
                     } else {
                         menuButton.setContentDescription(contentDescription);
                     }
-                    menuButton.setTooltip(menuItem.getTooltip());
+                    menuButton.setTooltipText(menuItem.getTooltipText());
                     menuButton.setMinimumWidth(minimumWidth);
                 }
                 return menuButton;
@@ -1642,17 +1642,17 @@
             ((ImageButton) menuItemButton
                     .findViewById(R.id.floating_toolbar_menu_item_image_button))
                     .setImageDrawable(menuItem.getIcon());
-            final CharSequence tooltip = menuItem.getTooltip();
-            if (TextUtils.isEmpty(tooltip)) {
-                menuItemButton.setTooltip(menuItem.getTitle());
+            final CharSequence tooltipText = menuItem.getTooltipText();
+            if (TextUtils.isEmpty(tooltipText)) {
+                menuItemButton.setTooltipText(menuItem.getTitle());
             } else {
-                menuItemButton.setTooltip(tooltip);
+                menuItemButton.setTooltipText(tooltipText);
             }
         } else {
             menuItemButton = LayoutInflater.from(context)
                     .inflate(R.layout.floating_popup_menu_button, null);
             ((Button) menuItemButton).setText(menuItem.getTitle());
-            menuItemButton.setTooltip(menuItem.getTooltip());
+            menuItemButton.setTooltipText(menuItem.getTooltipText());
         }
         final CharSequence contentDescription = menuItem.getContentDescription();
         if (TextUtils.isEmpty(contentDescription)) {
diff --git a/core/java/com/android/internal/widget/GapWorker.java b/core/java/com/android/internal/widget/GapWorker.java
new file mode 100644
index 0000000..5972396
--- /dev/null
+++ b/core/java/com/android/internal/widget/GapWorker.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.Nullable;
+import android.os.Trace;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.concurrent.TimeUnit;
+
+final class GapWorker implements Runnable {
+
+    static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
+
+    ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>();
+    long mPostTimeNs;
+    long mFrameIntervalNs;
+
+    static class Task {
+        public boolean immediate;
+        public int viewVelocity;
+        public int distanceToItem;
+        public RecyclerView view;
+        public int position;
+
+        public void clear() {
+            immediate = false;
+            viewVelocity = 0;
+            distanceToItem = 0;
+            view = null;
+            position = 0;
+        }
+    }
+
+    /**
+     * Temporary storage for prefetch Tasks that execute in {@link #prefetch(long)}. Task objects
+     * are pooled in the ArrayList, and never removed to avoid allocations, but always cleared
+     * in between calls.
+     */
+    private ArrayList<Task> mTasks = new ArrayList<>();
+
+    /**
+     * Prefetch information associated with a specific RecyclerView.
+     */
+    static class LayoutPrefetchRegistryImpl
+            implements RecyclerView.LayoutManager.LayoutPrefetchRegistry {
+        int mPrefetchDx;
+        int mPrefetchDy;
+        int[] mPrefetchArray;
+
+        int mCount;
+
+        void setPrefetchVector(int dx, int dy) {
+            mPrefetchDx = dx;
+            mPrefetchDy = dy;
+        }
+
+        void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) {
+            mCount = 0;
+            if (mPrefetchArray != null) {
+                Arrays.fill(mPrefetchArray, -1);
+            }
+
+            final RecyclerView.LayoutManager layout = view.mLayout;
+            if (view.mAdapter != null
+                    && layout != null
+                    && layout.isItemPrefetchEnabled()) {
+                if (nested) {
+                    // nested prefetch, only if no adapter updates pending. Note: we don't query
+                    // view.hasPendingAdapterUpdates(), as first layout may not have occurred
+                    if (!view.mAdapterHelper.hasPendingUpdates()) {
+                        layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this);
+                    }
+                } else {
+                    // momentum based prefetch, only if we trust current child/adapter state
+                    if (!view.hasPendingAdapterUpdates()) {
+                        layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy,
+                                view.mState, this);
+                    }
+                }
+
+                if (mCount > layout.mPrefetchMaxCountObserved) {
+                    layout.mPrefetchMaxCountObserved = mCount;
+                    layout.mPrefetchMaxObservedInInitialPrefetch = nested;
+                    view.mRecycler.updateViewCacheSize();
+                }
+            }
+        }
+
+        @Override
+        public void addPosition(int layoutPosition, int pixelDistance) {
+            if (pixelDistance < 0) {
+                throw new IllegalArgumentException("Pixel distance must be non-negative");
+            }
+
+            // allocate or expand array as needed, doubling when needed
+            final int storagePosition = mCount * 2;
+            if (mPrefetchArray == null) {
+                mPrefetchArray = new int[4];
+                Arrays.fill(mPrefetchArray, -1);
+            } else if (storagePosition >= mPrefetchArray.length) {
+                final int[] oldArray = mPrefetchArray;
+                mPrefetchArray = new int[storagePosition * 2];
+                System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length);
+            }
+
+            // add position
+            mPrefetchArray[storagePosition] = layoutPosition;
+            mPrefetchArray[storagePosition + 1] = pixelDistance;
+
+            mCount++;
+        }
+
+        boolean lastPrefetchIncludedPosition(int position) {
+            if (mPrefetchArray != null) {
+                final int count = mCount * 2;
+                for (int i = 0; i < count; i += 2) {
+                    if (mPrefetchArray[i] == position) return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Called when prefetch indices are no longer valid for cache prioritization.
+         */
+        void clearPrefetchPositions() {
+            if (mPrefetchArray != null) {
+                Arrays.fill(mPrefetchArray, -1);
+            }
+        }
+    }
+
+    public void add(RecyclerView recyclerView) {
+        if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) {
+            throw new IllegalStateException("RecyclerView already present in worker list!");
+        }
+        mRecyclerViews.add(recyclerView);
+    }
+
+    public void remove(RecyclerView recyclerView) {
+        boolean removeSuccess = mRecyclerViews.remove(recyclerView);
+        if (RecyclerView.DEBUG && !removeSuccess) {
+            throw new IllegalStateException("RecyclerView removal failed!");
+        }
+    }
+
+    /**
+     * Schedule a prefetch immediately after the current traversal.
+     */
+    void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
+        if (recyclerView.isAttachedToWindow()) {
+            if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) {
+                throw new IllegalStateException("attempting to post unregistered view!");
+            }
+            if (mPostTimeNs == 0) {
+                mPostTimeNs = recyclerView.getNanoTime();
+                recyclerView.post(this);
+            }
+        }
+
+        recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
+    }
+
+    static Comparator<Task> sTaskComparator = new Comparator<Task>() {
+        @Override
+        public int compare(Task lhs, Task rhs) {
+            // first, prioritize non-cleared tasks
+            if ((lhs.view == null) != (rhs.view == null)) {
+                return lhs.view == null ? 1 : -1;
+            }
+
+            // then prioritize immediate
+            if (lhs.immediate != rhs.immediate) {
+                return lhs.immediate ? -1 : 1;
+            }
+
+            // then prioritize _highest_ view velocity
+            int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity;
+            if (deltaViewVelocity != 0) return deltaViewVelocity;
+
+            // then prioritize _lowest_ distance to item
+            int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem;
+            if (deltaDistanceToItem != 0) return deltaDistanceToItem;
+
+            return 0;
+        }
+    };
+
+    private void buildTaskList() {
+        // Update PrefetchRegistry in each view
+        final int viewCount = mRecyclerViews.size();
+        int totalTaskCount = 0;
+        for (int i = 0; i < viewCount; i++) {
+            RecyclerView view = mRecyclerViews.get(i);
+            view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false);
+            totalTaskCount += view.mPrefetchRegistry.mCount;
+        }
+
+        // Populate task list from prefetch data...
+        mTasks.ensureCapacity(totalTaskCount);
+        int totalTaskIndex = 0;
+        for (int i = 0; i < viewCount; i++) {
+            RecyclerView view = mRecyclerViews.get(i);
+            LayoutPrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry;
+            final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx)
+                    + Math.abs(prefetchRegistry.mPrefetchDy);
+            for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) {
+                final Task task;
+                if (totalTaskIndex >= mTasks.size()) {
+                    task = new Task();
+                    mTasks.add(task);
+                } else {
+                    task = mTasks.get(totalTaskIndex);
+                }
+                final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1];
+
+                task.immediate = distanceToItem <= viewVelocity;
+                task.viewVelocity = viewVelocity;
+                task.distanceToItem = distanceToItem;
+                task.view = view;
+                task.position = prefetchRegistry.mPrefetchArray[j];
+
+                totalTaskIndex++;
+            }
+        }
+
+        // ... and priority sort
+        Collections.sort(mTasks, sTaskComparator);
+    }
+
+    static boolean isPrefetchPositionAttached(RecyclerView view, int position) {
+        final int childCount = view.mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View attachedView = view.mChildHelper.getUnfilteredChildAt(i);
+            RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(attachedView);
+            // Note: can use mPosition here because adapter doesn't have pending updates
+            if (holder.mPosition == position && !holder.isInvalid()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view,
+            int position, long deadlineNs) {
+        if (isPrefetchPositionAttached(view, position)) {
+            // don't attempt to prefetch attached views
+            return null;
+        }
+
+        RecyclerView.Recycler recycler = view.mRecycler;
+        RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
+                position, false, deadlineNs);
+
+        if (holder != null) {
+            if (holder.isBound()) {
+                // Only give the view a chance to go into the cache if binding succeeded
+                // Note that we must use public method, since item may need cleanup
+                recycler.recycleView(holder.itemView);
+            } else {
+                // Didn't bind, so we can't cache the view, but it will stay in the pool until
+                // next prefetch/traversal. If a View fails to bind, it means we didn't have
+                // enough time prior to the deadline (and won't for other instances of this
+                // type, during this GapWorker prefetch pass).
+                recycler.addViewHolderToRecycledViewPool(holder, false);
+            }
+        }
+        return holder;
+    }
+
+    private void prefetchInnerRecyclerViewWithDeadline(@Nullable RecyclerView innerView,
+            long deadlineNs) {
+        if (innerView == null) {
+            return;
+        }
+
+        if (innerView.mDataSetHasChangedAfterLayout
+                && innerView.mChildHelper.getUnfilteredChildCount() != 0) {
+            // RecyclerView has new data, but old attached views. Clear everything, so that
+            // we can prefetch without partially stale data.
+            innerView.removeAndRecycleViews();
+        }
+
+        // do nested prefetch!
+        final LayoutPrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry;
+        innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true);
+
+        if (innerPrefetchRegistry.mCount != 0) {
+            try {
+                Trace.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG);
+                innerView.mState.prepareForNestedPrefetch(innerView.mAdapter);
+                for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) {
+                    // Note that we ignore immediate flag for inner items because
+                    // we have lower confidence they're needed next frame.
+                    final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i];
+                    prefetchPositionWithDeadline(innerView, innerPosition, deadlineNs);
+                }
+            } finally {
+                Trace.endSection();
+            }
+        }
+    }
+
+    private void flushTaskWithDeadline(Task task, long deadlineNs) {
+        long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs;
+        RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view,
+                task.position, taskDeadlineNs);
+        if (holder != null && holder.mNestedRecyclerView != null) {
+            prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs);
+        }
+    }
+
+    private void flushTasksWithDeadline(long deadlineNs) {
+        for (int i = 0; i < mTasks.size(); i++) {
+            final Task task = mTasks.get(i);
+            if (task.view == null) {
+                break; // done with populated tasks
+            }
+            flushTaskWithDeadline(task, deadlineNs);
+            task.clear();
+        }
+    }
+
+    void prefetch(long deadlineNs) {
+        buildTaskList();
+        flushTasksWithDeadline(deadlineNs);
+    }
+
+    @Override
+    public void run() {
+        try {
+            Trace.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
+
+            if (mRecyclerViews.isEmpty()) {
+                // abort - no work to do
+                return;
+            }
+
+            // Query last vsync so we can predict next one. Note that drawing time not yet
+            // valid in animation/input callbacks, so query it here to be safe.
+            long lastFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos(
+                    mRecyclerViews.get(0).getDrawingTime());
+            if (lastFrameVsyncNs == 0) {
+                // abort - couldn't get last vsync for estimating next
+                return;
+            }
+
+            // TODO: consider rebasing deadline if frame was already dropped due to long UI work.
+            // Next frame will still wait for VSYNC, so we can still use the gap if it exists.
+            long nextFrameNs = lastFrameVsyncNs + mFrameIntervalNs;
+
+            prefetch(nextFrameNs);
+
+            // TODO: consider rescheduling self, if there's more work to do
+        } finally {
+            mPostTimeNs = 0;
+            Trace.endSection();
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/LinearLayoutManager.java b/core/java/com/android/internal/widget/LinearLayoutManager.java
new file mode 100644
index 0000000..d82c746
--- /dev/null
+++ b/core/java/com/android/internal/widget/LinearLayoutManager.java
@@ -0,0 +1,2398 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static com.android.internal.widget.RecyclerView.NO_POSITION;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.widget.RecyclerView.LayoutParams;
+import com.android.internal.widget.helper.ItemTouchHelper;
+
+import java.util.List;
+
+/**
+ * A {@link com.android.internal.widget.RecyclerView.LayoutManager} implementation which provides
+ * similar functionality to {@link android.widget.ListView}.
+ */
+public class LinearLayoutManager extends RecyclerView.LayoutManager implements
+        ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider {
+
+    private static final String TAG = "LinearLayoutManager";
+
+    static final boolean DEBUG = false;
+
+    public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
+
+    public static final int VERTICAL = OrientationHelper.VERTICAL;
+
+    public static final int INVALID_OFFSET = Integer.MIN_VALUE;
+
+
+    /**
+     * While trying to find next view to focus, LayoutManager will not try to scroll more
+     * than this factor times the total space of the list. If layout is vertical, total space is the
+     * height minus padding, if layout is horizontal, total space is the width minus padding.
+     */
+    private static final float MAX_SCROLL_FACTOR = 1 / 3f;
+
+
+    /**
+     * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}
+     */
+    int mOrientation;
+
+    /**
+     * Helper class that keeps temporary layout state.
+     * It does not keep state after layout is complete but we still keep a reference to re-use
+     * the same object.
+     */
+    private LayoutState mLayoutState;
+
+    /**
+     * Many calculations are made depending on orientation. To keep it clean, this interface
+     * helps {@link LinearLayoutManager} make those decisions.
+     * Based on {@link #mOrientation}, an implementation is lazily created in
+     * {@link #ensureLayoutState} method.
+     */
+    OrientationHelper mOrientationHelper;
+
+    /**
+     * We need to track this so that we can ignore current position when it changes.
+     */
+    private boolean mLastStackFromEnd;
+
+
+    /**
+     * Defines if layout should be calculated from end to start.
+     *
+     * @see #mShouldReverseLayout
+     */
+    private boolean mReverseLayout = false;
+
+    /**
+     * This keeps the final value for how LayoutManager should start laying out views.
+     * It is calculated by checking {@link #getReverseLayout()} and View's layout direction.
+     * {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run.
+     */
+    boolean mShouldReverseLayout = false;
+
+    /**
+     * Works the same way as {@link android.widget.AbsListView#setStackFromBottom(boolean)} and
+     * it supports both orientations.
+     * see {@link android.widget.AbsListView#setStackFromBottom(boolean)}
+     */
+    private boolean mStackFromEnd = false;
+
+    /**
+     * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}.
+     * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}
+     */
+    private boolean mSmoothScrollbarEnabled = true;
+
+    /**
+     * When LayoutManager needs to scroll to a position, it sets this variable and requests a
+     * layout which will check this variable and re-layout accordingly.
+     */
+    int mPendingScrollPosition = NO_POSITION;
+
+    /**
+     * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is
+     * called.
+     */
+    int mPendingScrollPositionOffset = INVALID_OFFSET;
+
+    private boolean mRecycleChildrenOnDetach;
+
+    SavedState mPendingSavedState = null;
+
+    /**
+     *  Re-used variable to keep anchor information on re-layout.
+     *  Anchor position and coordinate defines the reference point for LLM while doing a layout.
+     * */
+    final AnchorInfo mAnchorInfo = new AnchorInfo();
+
+    /**
+     * Stashed to avoid allocation, currently only used in #fill()
+     */
+    private final LayoutChunkResult mLayoutChunkResult = new LayoutChunkResult();
+
+    /**
+     * Number of items to prefetch when first coming on screen with new data.
+     */
+    private int mInitialItemPrefetchCount = 2;
+
+    /**
+     * Creates a vertical LinearLayoutManager
+     *
+     * @param context Current context, will be used to access resources.
+     */
+    public LinearLayoutManager(Context context) {
+        this(context, VERTICAL, false);
+    }
+
+    /**
+     * @param context       Current context, will be used to access resources.
+     * @param orientation   Layout orientation. Should be {@link #HORIZONTAL} or {@link
+     *                      #VERTICAL}.
+     * @param reverseLayout When set to true, layouts from end to start.
+     */
+    public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
+        setOrientation(orientation);
+        setReverseLayout(reverseLayout);
+        setAutoMeasureEnabled(true);
+    }
+
+    /**
+     * Constructor used when layout manager is set in XML by RecyclerView attribute
+     * "layoutManager". Defaults to vertical orientation.
+     *
+     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
+     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
+     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
+     */
+    public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
+        setOrientation(properties.orientation);
+        setReverseLayout(properties.reverseLayout);
+        setStackFromEnd(properties.stackFromEnd);
+        setAutoMeasureEnabled(true);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * Returns whether LayoutManager will recycle its children when it is detached from
+     * RecyclerView.
+     *
+     * @return true if LayoutManager will recycle its children when it is detached from
+     * RecyclerView.
+     */
+    public boolean getRecycleChildrenOnDetach() {
+        return mRecycleChildrenOnDetach;
+    }
+
+    /**
+     * Set whether LayoutManager will recycle its children when it is detached from
+     * RecyclerView.
+     * <p>
+     * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set
+     * this flag to <code>true</code> so that views will be available to other RecyclerViews
+     * immediately.
+     * <p>
+     * Note that, setting this flag will result in a performance drop if RecyclerView
+     * is restored.
+     *
+     * @param recycleChildrenOnDetach Whether children should be recycled in detach or not.
+     */
+    public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
+        mRecycleChildrenOnDetach = recycleChildrenOnDetach;
+    }
+
+    @Override
+    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
+        super.onDetachedFromWindow(view, recycler);
+        if (mRecycleChildrenOnDetach) {
+            removeAndRecycleAllViews(recycler);
+            recycler.clear();
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        if (getChildCount() > 0) {
+            event.setFromIndex(findFirstVisibleItemPosition());
+            event.setToIndex(findLastVisibleItemPosition());
+        }
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        if (mPendingSavedState != null) {
+            return new SavedState(mPendingSavedState);
+        }
+        SavedState state = new SavedState();
+        if (getChildCount() > 0) {
+            ensureLayoutState();
+            boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout;
+            state.mAnchorLayoutFromEnd = didLayoutFromEnd;
+            if (didLayoutFromEnd) {
+                final View refChild = getChildClosestToEnd();
+                state.mAnchorOffset = mOrientationHelper.getEndAfterPadding()
+                        - mOrientationHelper.getDecoratedEnd(refChild);
+                state.mAnchorPosition = getPosition(refChild);
+            } else {
+                final View refChild = getChildClosestToStart();
+                state.mAnchorPosition = getPosition(refChild);
+                state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild)
+                        - mOrientationHelper.getStartAfterPadding();
+            }
+        } else {
+            state.invalidateAnchor();
+        }
+        return state;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        if (state instanceof SavedState) {
+            mPendingSavedState = (SavedState) state;
+            requestLayout();
+            if (DEBUG) {
+                Log.d(TAG, "loaded saved state");
+            }
+        } else if (DEBUG) {
+            Log.d(TAG, "invalid saved state class");
+        }
+    }
+
+    /**
+     * @return true if {@link #getOrientation()} is {@link #HORIZONTAL}
+     */
+    @Override
+    public boolean canScrollHorizontally() {
+        return mOrientation == HORIZONTAL;
+    }
+
+    /**
+     * @return true if {@link #getOrientation()} is {@link #VERTICAL}
+     */
+    @Override
+    public boolean canScrollVertically() {
+        return mOrientation == VERTICAL;
+    }
+
+    /**
+     * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)}
+     */
+    public void setStackFromEnd(boolean stackFromEnd) {
+        assertNotInLayoutOrScroll(null);
+        if (mStackFromEnd == stackFromEnd) {
+            return;
+        }
+        mStackFromEnd = stackFromEnd;
+        requestLayout();
+    }
+
+    public boolean getStackFromEnd() {
+        return mStackFromEnd;
+    }
+
+    /**
+     * Returns the current orientation of the layout.
+     *
+     * @return Current orientation,  either {@link #HORIZONTAL} or {@link #VERTICAL}
+     * @see #setOrientation(int)
+     */
+    public int getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * Sets the orientation of the layout. {@link com.android.internal.widget.LinearLayoutManager}
+     * will do its best to keep scroll position.
+     *
+     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
+     */
+    public void setOrientation(int orientation) {
+        if (orientation != HORIZONTAL && orientation != VERTICAL) {
+            throw new IllegalArgumentException("invalid orientation:" + orientation);
+        }
+        assertNotInLayoutOrScroll(null);
+        if (orientation == mOrientation) {
+            return;
+        }
+        mOrientation = orientation;
+        mOrientationHelper = null;
+        requestLayout();
+    }
+
+    /**
+     * Calculates the view layout order. (e.g. from end to start or start to end)
+     * RTL layout support is applied automatically. So if layout is RTL and
+     * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.
+     */
+    private void resolveShouldLayoutReverse() {
+        // A == B is the same result, but we rather keep it readable
+        if (mOrientation == VERTICAL || !isLayoutRTL()) {
+            mShouldReverseLayout = mReverseLayout;
+        } else {
+            mShouldReverseLayout = !mReverseLayout;
+        }
+    }
+
+    /**
+     * Returns if views are laid out from the opposite direction of the layout.
+     *
+     * @return If layout is reversed or not.
+     * @see #setReverseLayout(boolean)
+     */
+    public boolean getReverseLayout() {
+        return mReverseLayout;
+    }
+
+    /**
+     * Used to reverse item traversal and layout order.
+     * This behaves similar to the layout change for RTL views. When set to true, first item is
+     * laid out at the end of the UI, second item is laid out before it etc.
+     *
+     * For horizontal layouts, it depends on the layout direction.
+     * When set to true, If {@link com.android.internal.widget.RecyclerView} is LTR, than it will
+     * layout from RTL, if {@link com.android.internal.widget.RecyclerView}} is RTL, it will layout
+     * from LTR.
+     *
+     * If you are looking for the exact same behavior of
+     * {@link android.widget.AbsListView#setStackFromBottom(boolean)}, use
+     * {@link #setStackFromEnd(boolean)}
+     */
+    public void setReverseLayout(boolean reverseLayout) {
+        assertNotInLayoutOrScroll(null);
+        if (reverseLayout == mReverseLayout) {
+            return;
+        }
+        mReverseLayout = reverseLayout;
+        requestLayout();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public View findViewByPosition(int position) {
+        final int childCount = getChildCount();
+        if (childCount == 0) {
+            return null;
+        }
+        final int firstChild = getPosition(getChildAt(0));
+        final int viewPosition = position - firstChild;
+        if (viewPosition >= 0 && viewPosition < childCount) {
+            final View child = getChildAt(viewPosition);
+            if (getPosition(child) == position) {
+                return child; // in pre-layout, this may not match
+            }
+        }
+        // fallback to traversal. This might be necessary in pre-layout.
+        return super.findViewByPosition(position);
+    }
+
+    /**
+     * <p>Returns the amount of extra space that should be laid out by LayoutManager.</p>
+     *
+     * <p>By default, {@link com.android.internal.widget.LinearLayoutManager} lays out 1 extra page
+     * of items while smooth scrolling and 0 otherwise. You can override this method to implement
+     * your custom layout pre-cache logic.</p>
+     *
+     * <p><strong>Note:</strong>Laying out invisible elements generally comes with significant
+     * performance cost. It's typically only desirable in places like smooth scrolling to an unknown
+     * location, where 1) the extra content helps LinearLayoutManager know in advance when its
+     * target is approaching, so it can decelerate early and smoothly and 2) while motion is
+     * continuous.</p>
+     *
+     * <p>Extending the extra layout space is especially expensive if done while the user may change
+     * scrolling direction. Changing direction will cause the extra layout space to swap to the
+     * opposite side of the viewport, incurring many rebinds/recycles, unless the cache is large
+     * enough to handle it.</p>
+     *
+     * @return The extra space that should be laid out (in pixels).
+     */
+    protected int getExtraLayoutSpace(RecyclerView.State state) {
+        if (state.hasTargetScrollPosition()) {
+            return mOrientationHelper.getTotalSpace();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
+            int position) {
+        LinearSmoothScroller linearSmoothScroller =
+                new LinearSmoothScroller(recyclerView.getContext());
+        linearSmoothScroller.setTargetPosition(position);
+        startSmoothScroll(linearSmoothScroller);
+    }
+
+    @Override
+    public PointF computeScrollVectorForPosition(int targetPosition) {
+        if (getChildCount() == 0) {
+            return null;
+        }
+        final int firstChildPos = getPosition(getChildAt(0));
+        final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1;
+        if (mOrientation == HORIZONTAL) {
+            return new PointF(direction, 0);
+        } else {
+            return new PointF(0, direction);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+        // layout algorithm:
+        // 1) by checking children and other variables, find an anchor coordinate and an anchor
+        //  item position.
+        // 2) fill towards start, stacking from bottom
+        // 3) fill towards end, stacking from top
+        // 4) scroll to fulfill requirements like stack from bottom.
+        // create layout state
+        if (DEBUG) {
+            Log.d(TAG, "is pre layout:" + state.isPreLayout());
+        }
+        if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) {
+            if (state.getItemCount() == 0) {
+                removeAndRecycleAllViews(recycler);
+                return;
+            }
+        }
+        if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
+            mPendingScrollPosition = mPendingSavedState.mAnchorPosition;
+        }
+
+        ensureLayoutState();
+        mLayoutState.mRecycle = false;
+        // resolve layout direction
+        resolveShouldLayoutReverse();
+
+        if (!mAnchorInfo.mValid || mPendingScrollPosition != NO_POSITION
+                || mPendingSavedState != null) {
+            mAnchorInfo.reset();
+            mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
+            // calculate anchor position and coordinate
+            updateAnchorInfoForLayout(recycler, state, mAnchorInfo);
+            mAnchorInfo.mValid = true;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Anchor info:" + mAnchorInfo);
+        }
+
+        // LLM may decide to layout items for "extra" pixels to account for scrolling target,
+        // caching or predictive animations.
+        int extraForStart;
+        int extraForEnd;
+        final int extra = getExtraLayoutSpace(state);
+        // If the previous scroll delta was less than zero, the extra space should be laid out
+        // at the start. Otherwise, it should be at the end.
+        if (mLayoutState.mLastScrollDelta >= 0) {
+            extraForEnd = extra;
+            extraForStart = 0;
+        } else {
+            extraForStart = extra;
+            extraForEnd = 0;
+        }
+        extraForStart += mOrientationHelper.getStartAfterPadding();
+        extraForEnd += mOrientationHelper.getEndPadding();
+        if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION
+                && mPendingScrollPositionOffset != INVALID_OFFSET) {
+            // if the child is visible and we are going to move it around, we should layout
+            // extra items in the opposite direction to make sure new items animate nicely
+            // instead of just fading in
+            final View existing = findViewByPosition(mPendingScrollPosition);
+            if (existing != null) {
+                final int current;
+                final int upcomingOffset;
+                if (mShouldReverseLayout) {
+                    current = mOrientationHelper.getEndAfterPadding()
+                            - mOrientationHelper.getDecoratedEnd(existing);
+                    upcomingOffset = current - mPendingScrollPositionOffset;
+                } else {
+                    current = mOrientationHelper.getDecoratedStart(existing)
+                            - mOrientationHelper.getStartAfterPadding();
+                    upcomingOffset = mPendingScrollPositionOffset - current;
+                }
+                if (upcomingOffset > 0) {
+                    extraForStart += upcomingOffset;
+                } else {
+                    extraForEnd -= upcomingOffset;
+                }
+            }
+        }
+        int startOffset;
+        int endOffset;
+        final int firstLayoutDirection;
+        if (mAnchorInfo.mLayoutFromEnd) {
+            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
+                    : LayoutState.ITEM_DIRECTION_HEAD;
+        } else {
+            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
+                    : LayoutState.ITEM_DIRECTION_TAIL;
+        }
+
+        onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);
+        detachAndScrapAttachedViews(recycler);
+        mLayoutState.mInfinite = resolveIsInfinite();
+        mLayoutState.mIsPreLayout = state.isPreLayout();
+        if (mAnchorInfo.mLayoutFromEnd) {
+            // fill towards start
+            updateLayoutStateToFillStart(mAnchorInfo);
+            mLayoutState.mExtra = extraForStart;
+            fill(recycler, mLayoutState, state, false);
+            startOffset = mLayoutState.mOffset;
+            final int firstElement = mLayoutState.mCurrentPosition;
+            if (mLayoutState.mAvailable > 0) {
+                extraForEnd += mLayoutState.mAvailable;
+            }
+            // fill towards end
+            updateLayoutStateToFillEnd(mAnchorInfo);
+            mLayoutState.mExtra = extraForEnd;
+            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
+            fill(recycler, mLayoutState, state, false);
+            endOffset = mLayoutState.mOffset;
+
+            if (mLayoutState.mAvailable > 0) {
+                // end could not consume all. add more items towards start
+                extraForStart = mLayoutState.mAvailable;
+                updateLayoutStateToFillStart(firstElement, startOffset);
+                mLayoutState.mExtra = extraForStart;
+                fill(recycler, mLayoutState, state, false);
+                startOffset = mLayoutState.mOffset;
+            }
+        } else {
+            // fill towards end
+            updateLayoutStateToFillEnd(mAnchorInfo);
+            mLayoutState.mExtra = extraForEnd;
+            fill(recycler, mLayoutState, state, false);
+            endOffset = mLayoutState.mOffset;
+            final int lastElement = mLayoutState.mCurrentPosition;
+            if (mLayoutState.mAvailable > 0) {
+                extraForStart += mLayoutState.mAvailable;
+            }
+            // fill towards start
+            updateLayoutStateToFillStart(mAnchorInfo);
+            mLayoutState.mExtra = extraForStart;
+            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
+            fill(recycler, mLayoutState, state, false);
+            startOffset = mLayoutState.mOffset;
+
+            if (mLayoutState.mAvailable > 0) {
+                extraForEnd = mLayoutState.mAvailable;
+                // start could not consume all it should. add more items towards end
+                updateLayoutStateToFillEnd(lastElement, endOffset);
+                mLayoutState.mExtra = extraForEnd;
+                fill(recycler, mLayoutState, state, false);
+                endOffset = mLayoutState.mOffset;
+            }
+        }
+
+        // changes may cause gaps on the UI, try to fix them.
+        // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have
+        // changed
+        if (getChildCount() > 0) {
+            // because layout from end may be changed by scroll to position
+            // we re-calculate it.
+            // find which side we should check for gaps.
+            if (mShouldReverseLayout ^ mStackFromEnd) {
+                int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true);
+                startOffset += fixOffset;
+                endOffset += fixOffset;
+                fixOffset = fixLayoutStartGap(startOffset, recycler, state, false);
+                startOffset += fixOffset;
+                endOffset += fixOffset;
+            } else {
+                int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);
+                startOffset += fixOffset;
+                endOffset += fixOffset;
+                fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);
+                startOffset += fixOffset;
+                endOffset += fixOffset;
+            }
+        }
+        layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);
+        if (!state.isPreLayout()) {
+            mOrientationHelper.onLayoutComplete();
+        } else {
+            mAnchorInfo.reset();
+        }
+        mLastStackFromEnd = mStackFromEnd;
+        if (DEBUG) {
+            validateChildOrder();
+        }
+    }
+
+    @Override
+    public void onLayoutCompleted(RecyclerView.State state) {
+        super.onLayoutCompleted(state);
+        mPendingSavedState = null; // we don't need this anymore
+        mPendingScrollPosition = NO_POSITION;
+        mPendingScrollPositionOffset = INVALID_OFFSET;
+        mAnchorInfo.reset();
+    }
+
+    /**
+     * Method called when Anchor position is decided. Extending class can setup accordingly or
+     * even update anchor info if necessary.
+     * @param recycler The recycler for the layout
+     * @param state The layout state
+     * @param anchorInfo The mutable POJO that keeps the position and offset.
+     * @param firstLayoutItemDirection The direction of the first layout filling in terms of adapter
+     *                                 indices.
+     */
+    void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state,
+            AnchorInfo anchorInfo, int firstLayoutItemDirection) {
+    }
+
+    /**
+     * If necessary, layouts new items for predictive animations
+     */
+    private void layoutForPredictiveAnimations(RecyclerView.Recycler recycler,
+            RecyclerView.State state, int startOffset,  int endOffset) {
+        // If there are scrap children that we did not layout, we need to find where they did go
+        // and layout them accordingly so that animations can work as expected.
+        // This case may happen if new views are added or an existing view expands and pushes
+        // another view out of bounds.
+        if (!state.willRunPredictiveAnimations() ||  getChildCount() == 0 || state.isPreLayout()
+                || !supportsPredictiveItemAnimations()) {
+            return;
+        }
+        // to make the logic simpler, we calculate the size of children and call fill.
+        int scrapExtraStart = 0, scrapExtraEnd = 0;
+        final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
+        final int scrapSize = scrapList.size();
+        final int firstChildPos = getPosition(getChildAt(0));
+        for (int i = 0; i < scrapSize; i++) {
+            RecyclerView.ViewHolder scrap = scrapList.get(i);
+            if (scrap.isRemoved()) {
+                continue;
+            }
+            final int position = scrap.getLayoutPosition();
+            final int direction = position < firstChildPos != mShouldReverseLayout
+                    ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END;
+            if (direction == LayoutState.LAYOUT_START) {
+                scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
+            } else {
+                scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
+            }
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart
+                    + " towards start and " + scrapExtraEnd + " towards end");
+        }
+        mLayoutState.mScrapList = scrapList;
+        if (scrapExtraStart > 0) {
+            View anchor = getChildClosestToStart();
+            updateLayoutStateToFillStart(getPosition(anchor), startOffset);
+            mLayoutState.mExtra = scrapExtraStart;
+            mLayoutState.mAvailable = 0;
+            mLayoutState.assignPositionFromScrapList();
+            fill(recycler, mLayoutState, state, false);
+        }
+
+        if (scrapExtraEnd > 0) {
+            View anchor = getChildClosestToEnd();
+            updateLayoutStateToFillEnd(getPosition(anchor), endOffset);
+            mLayoutState.mExtra = scrapExtraEnd;
+            mLayoutState.mAvailable = 0;
+            mLayoutState.assignPositionFromScrapList();
+            fill(recycler, mLayoutState, state, false);
+        }
+        mLayoutState.mScrapList = null;
+    }
+
+    private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state,
+            AnchorInfo anchorInfo) {
+        if (updateAnchorFromPendingData(state, anchorInfo)) {
+            if (DEBUG) {
+                Log.d(TAG, "updated anchor info from pending information");
+            }
+            return;
+        }
+
+        if (updateAnchorFromChildren(recycler, state, anchorInfo)) {
+            if (DEBUG) {
+                Log.d(TAG, "updated anchor info from existing children");
+            }
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "deciding anchor info for fresh state");
+        }
+        anchorInfo.assignCoordinateFromPadding();
+        anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0;
+    }
+
+    /**
+     * Finds an anchor child from existing Views. Most of the time, this is the view closest to
+     * start or end that has a valid position (e.g. not removed).
+     * <p>
+     * If a child has focus, it is given priority.
+     */
+    private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler,
+            RecyclerView.State state, AnchorInfo anchorInfo) {
+        if (getChildCount() == 0) {
+            return false;
+        }
+        final View focused = getFocusedChild();
+        if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) {
+            anchorInfo.assignFromViewAndKeepVisibleRect(focused);
+            return true;
+        }
+        if (mLastStackFromEnd != mStackFromEnd) {
+            return false;
+        }
+        View referenceChild = anchorInfo.mLayoutFromEnd
+                ? findReferenceChildClosestToEnd(recycler, state)
+                : findReferenceChildClosestToStart(recycler, state);
+        if (referenceChild != null) {
+            anchorInfo.assignFromView(referenceChild);
+            // If all visible views are removed in 1 pass, reference child might be out of bounds.
+            // If that is the case, offset it back to 0 so that we use these pre-layout children.
+            if (!state.isPreLayout() && supportsPredictiveItemAnimations()) {
+                // validate this child is at least partially visible. if not, offset it to start
+                final boolean notVisible =
+                        mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper
+                                .getEndAfterPadding()
+                                || mOrientationHelper.getDecoratedEnd(referenceChild)
+                                < mOrientationHelper.getStartAfterPadding();
+                if (notVisible) {
+                    anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd
+                            ? mOrientationHelper.getEndAfterPadding()
+                            : mOrientationHelper.getStartAfterPadding();
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * If there is a pending scroll position or saved states, updates the anchor info from that
+     * data and returns true
+     */
+    private boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) {
+        if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) {
+            return false;
+        }
+        // validate scroll position
+        if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) {
+            mPendingScrollPosition = NO_POSITION;
+            mPendingScrollPositionOffset = INVALID_OFFSET;
+            if (DEBUG) {
+                Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition);
+            }
+            return false;
+        }
+
+        // if child is visible, try to make it a reference child and ensure it is fully visible.
+        // if child is not visible, align it depending on its virtual position.
+        anchorInfo.mPosition = mPendingScrollPosition;
+        if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
+            // Anchor offset depends on how that child was laid out. Here, we update it
+            // according to our current view bounds
+            anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
+            if (anchorInfo.mLayoutFromEnd) {
+                anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding()
+                        - mPendingSavedState.mAnchorOffset;
+            } else {
+                anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding()
+                        + mPendingSavedState.mAnchorOffset;
+            }
+            return true;
+        }
+
+        if (mPendingScrollPositionOffset == INVALID_OFFSET) {
+            View child = findViewByPosition(mPendingScrollPosition);
+            if (child != null) {
+                final int childSize = mOrientationHelper.getDecoratedMeasurement(child);
+                if (childSize > mOrientationHelper.getTotalSpace()) {
+                    // item does not fit. fix depending on layout direction
+                    anchorInfo.assignCoordinateFromPadding();
+                    return true;
+                }
+                final int startGap = mOrientationHelper.getDecoratedStart(child)
+                        - mOrientationHelper.getStartAfterPadding();
+                if (startGap < 0) {
+                    anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding();
+                    anchorInfo.mLayoutFromEnd = false;
+                    return true;
+                }
+                final int endGap = mOrientationHelper.getEndAfterPadding()
+                        - mOrientationHelper.getDecoratedEnd(child);
+                if (endGap < 0) {
+                    anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding();
+                    anchorInfo.mLayoutFromEnd = true;
+                    return true;
+                }
+                anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd
+                        ? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper
+                                .getTotalSpaceChange())
+                        : mOrientationHelper.getDecoratedStart(child);
+            } else { // item is not visible.
+                if (getChildCount() > 0) {
+                    // get position of any child, does not matter
+                    int pos = getPosition(getChildAt(0));
+                    anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos
+                            == mShouldReverseLayout;
+                }
+                anchorInfo.assignCoordinateFromPadding();
+            }
+            return true;
+        }
+        // override layout from end values for consistency
+        anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
+        // if this changes, we should update prepareForDrop as well
+        if (mShouldReverseLayout) {
+            anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding()
+                    - mPendingScrollPositionOffset;
+        } else {
+            anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding()
+                    + mPendingScrollPositionOffset;
+        }
+        return true;
+    }
+
+    /**
+     * @return The final offset amount for children
+     */
+    private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler,
+            RecyclerView.State state, boolean canOffsetChildren) {
+        int gap = mOrientationHelper.getEndAfterPadding() - endOffset;
+        int fixOffset = 0;
+        if (gap > 0) {
+            fixOffset = -scrollBy(-gap, recycler, state);
+        } else {
+            return 0; // nothing to fix
+        }
+        // move offset according to scroll amount
+        endOffset += fixOffset;
+        if (canOffsetChildren) {
+            // re-calculate gap, see if we could fix it
+            gap = mOrientationHelper.getEndAfterPadding() - endOffset;
+            if (gap > 0) {
+                mOrientationHelper.offsetChildren(gap);
+                return gap + fixOffset;
+            }
+        }
+        return fixOffset;
+    }
+
+    /**
+     * @return The final offset amount for children
+     */
+    private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler,
+            RecyclerView.State state, boolean canOffsetChildren) {
+        int gap = startOffset - mOrientationHelper.getStartAfterPadding();
+        int fixOffset = 0;
+        if (gap > 0) {
+            // check if we should fix this gap.
+            fixOffset = -scrollBy(gap, recycler, state);
+        } else {
+            return 0; // nothing to fix
+        }
+        startOffset += fixOffset;
+        if (canOffsetChildren) {
+            // re-calculate gap, see if we could fix it
+            gap = startOffset - mOrientationHelper.getStartAfterPadding();
+            if (gap > 0) {
+                mOrientationHelper.offsetChildren(-gap);
+                return fixOffset - gap;
+            }
+        }
+        return fixOffset;
+    }
+
+    private void updateLayoutStateToFillEnd(AnchorInfo anchorInfo) {
+        updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate);
+    }
+
+    private void updateLayoutStateToFillEnd(int itemPosition, int offset) {
+        mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset;
+        mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :
+                LayoutState.ITEM_DIRECTION_TAIL;
+        mLayoutState.mCurrentPosition = itemPosition;
+        mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END;
+        mLayoutState.mOffset = offset;
+        mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN;
+    }
+
+    private void updateLayoutStateToFillStart(AnchorInfo anchorInfo) {
+        updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate);
+    }
+
+    private void updateLayoutStateToFillStart(int itemPosition, int offset) {
+        mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding();
+        mLayoutState.mCurrentPosition = itemPosition;
+        mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL :
+                LayoutState.ITEM_DIRECTION_HEAD;
+        mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START;
+        mLayoutState.mOffset = offset;
+        mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN;
+
+    }
+
+    protected boolean isLayoutRTL() {
+        return getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+    }
+
+    void ensureLayoutState() {
+        if (mLayoutState == null) {
+            mLayoutState = createLayoutState();
+        }
+        if (mOrientationHelper == null) {
+            mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
+        }
+    }
+
+    /**
+     * Test overrides this to plug some tracking and verification.
+     *
+     * @return A new LayoutState
+     */
+    LayoutState createLayoutState() {
+        return new LayoutState();
+    }
+
+    /**
+     * <p>Scroll the RecyclerView to make the position visible.</p>
+     *
+     * <p>RecyclerView will scroll the minimum amount that is necessary to make the
+     * target position visible. If you are looking for a similar behavior to
+     * {@link android.widget.ListView#setSelection(int)} or
+     * {@link android.widget.ListView#setSelectionFromTop(int, int)}, use
+     * {@link #scrollToPositionWithOffset(int, int)}.</p>
+     *
+     * <p>Note that scroll position change will not be reflected until the next layout call.</p>
+     *
+     * @param position Scroll to this adapter position
+     * @see #scrollToPositionWithOffset(int, int)
+     */
+    @Override
+    public void scrollToPosition(int position) {
+        mPendingScrollPosition = position;
+        mPendingScrollPositionOffset = INVALID_OFFSET;
+        if (mPendingSavedState != null) {
+            mPendingSavedState.invalidateAnchor();
+        }
+        requestLayout();
+    }
+
+    /**
+     * Scroll to the specified adapter position with the given offset from resolved layout
+     * start. Resolved layout start depends on {@link #getReverseLayout()},
+     * {@link View#getLayoutDirection()} and {@link #getStackFromEnd()}.
+     * <p>
+     * For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling
+     * <code>scrollToPositionWithOffset(10, 20)</code> will layout such that
+     * <code>item[10]</code>'s bottom is 20 pixels above the RecyclerView's bottom.
+     * <p>
+     * Note that scroll position change will not be reflected until the next layout call.
+     * <p>
+     * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.
+     *
+     * @param position Index (starting at 0) of the reference item.
+     * @param offset   The distance (in pixels) between the start edge of the item view and
+     *                 start edge of the RecyclerView.
+     * @see #setReverseLayout(boolean)
+     * @see #scrollToPosition(int)
+     */
+    public void scrollToPositionWithOffset(int position, int offset) {
+        mPendingScrollPosition = position;
+        mPendingScrollPositionOffset = offset;
+        if (mPendingSavedState != null) {
+            mPendingSavedState.invalidateAnchor();
+        }
+        requestLayout();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
+            RecyclerView.State state) {
+        if (mOrientation == VERTICAL) {
+            return 0;
+        }
+        return scrollBy(dx, recycler, state);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
+            RecyclerView.State state) {
+        if (mOrientation == HORIZONTAL) {
+            return 0;
+        }
+        return scrollBy(dy, recycler, state);
+    }
+
+    @Override
+    public int computeHorizontalScrollOffset(RecyclerView.State state) {
+        return computeScrollOffset(state);
+    }
+
+    @Override
+    public int computeVerticalScrollOffset(RecyclerView.State state) {
+        return computeScrollOffset(state);
+    }
+
+    @Override
+    public int computeHorizontalScrollExtent(RecyclerView.State state) {
+        return computeScrollExtent(state);
+    }
+
+    @Override
+    public int computeVerticalScrollExtent(RecyclerView.State state) {
+        return computeScrollExtent(state);
+    }
+
+    @Override
+    public int computeHorizontalScrollRange(RecyclerView.State state) {
+        return computeScrollRange(state);
+    }
+
+    @Override
+    public int computeVerticalScrollRange(RecyclerView.State state) {
+        return computeScrollRange(state);
+    }
+
+    private int computeScrollOffset(RecyclerView.State state) {
+        if (getChildCount() == 0) {
+            return 0;
+        }
+        ensureLayoutState();
+        return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper,
+                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
+                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
+                this, mSmoothScrollbarEnabled, mShouldReverseLayout);
+    }
+
+    private int computeScrollExtent(RecyclerView.State state) {
+        if (getChildCount() == 0) {
+            return 0;
+        }
+        ensureLayoutState();
+        return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper,
+                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
+                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
+                this,  mSmoothScrollbarEnabled);
+    }
+
+    private int computeScrollRange(RecyclerView.State state) {
+        if (getChildCount() == 0) {
+            return 0;
+        }
+        ensureLayoutState();
+        return ScrollbarHelper.computeScrollRange(state, mOrientationHelper,
+                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
+                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
+                this, mSmoothScrollbarEnabled);
+    }
+
+    /**
+     * When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed
+     * based on the number of visible pixels in the visible items. This however assumes that all
+     * list items have similar or equal widths or heights (depending on list orientation).
+     * If you use a list in which items have different dimensions, the scrollbar will change
+     * appearance as the user scrolls through the list. To avoid this issue,  you need to disable
+     * this property.
+     *
+     * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based
+     * solely on the number of items in the adapter and the position of the visible items inside
+     * the adapter. This provides a stable scrollbar as the user navigates through a list of items
+     * with varying widths / heights.
+     *
+     * @param enabled Whether or not to enable smooth scrollbar.
+     *
+     * @see #setSmoothScrollbarEnabled(boolean)
+     */
+    public void setSmoothScrollbarEnabled(boolean enabled) {
+        mSmoothScrollbarEnabled = enabled;
+    }
+
+    /**
+     * Returns the current state of the smooth scrollbar feature. It is enabled by default.
+     *
+     * @return True if smooth scrollbar is enabled, false otherwise.
+     *
+     * @see #setSmoothScrollbarEnabled(boolean)
+     */
+    public boolean isSmoothScrollbarEnabled() {
+        return mSmoothScrollbarEnabled;
+    }
+
+    private void updateLayoutState(int layoutDirection, int requiredSpace,
+            boolean canUseExistingSpace, RecyclerView.State state) {
+        // If parent provides a hint, don't measure unlimited.
+        mLayoutState.mInfinite = resolveIsInfinite();
+        mLayoutState.mExtra = getExtraLayoutSpace(state);
+        mLayoutState.mLayoutDirection = layoutDirection;
+        int scrollingOffset;
+        if (layoutDirection == LayoutState.LAYOUT_END) {
+            mLayoutState.mExtra += mOrientationHelper.getEndPadding();
+            // get the first child in the direction we are going
+            final View child = getChildClosestToEnd();
+            // the direction in which we are traversing children
+            mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
+                    : LayoutState.ITEM_DIRECTION_TAIL;
+            mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
+            mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
+            // calculate how much we can scroll without adding new children (independent of layout)
+            scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
+                    - mOrientationHelper.getEndAfterPadding();
+
+        } else {
+            final View child = getChildClosestToStart();
+            mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding();
+            mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
+                    : LayoutState.ITEM_DIRECTION_HEAD;
+            mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
+            mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
+            scrollingOffset = -mOrientationHelper.getDecoratedStart(child)
+                    + mOrientationHelper.getStartAfterPadding();
+        }
+        mLayoutState.mAvailable = requiredSpace;
+        if (canUseExistingSpace) {
+            mLayoutState.mAvailable -= scrollingOffset;
+        }
+        mLayoutState.mScrollingOffset = scrollingOffset;
+    }
+
+    boolean resolveIsInfinite() {
+        return mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED
+                && mOrientationHelper.getEnd() == 0;
+    }
+
+    void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState,
+            LayoutPrefetchRegistry layoutPrefetchRegistry) {
+        final int pos = layoutState.mCurrentPosition;
+        if (pos >= 0 && pos < state.getItemCount()) {
+            layoutPrefetchRegistry.addPosition(pos, layoutState.mScrollingOffset);
+        }
+    }
+
+    @Override
+    public void collectInitialPrefetchPositions(int adapterItemCount,
+            LayoutPrefetchRegistry layoutPrefetchRegistry) {
+        final boolean fromEnd;
+        final int anchorPos;
+        if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
+            // use restored state, since it hasn't been resolved yet
+            fromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
+            anchorPos = mPendingSavedState.mAnchorPosition;
+        } else {
+            resolveShouldLayoutReverse();
+            fromEnd = mShouldReverseLayout;
+            if (mPendingScrollPosition == NO_POSITION) {
+                anchorPos = fromEnd ? adapterItemCount - 1 : 0;
+            } else {
+                anchorPos = mPendingScrollPosition;
+            }
+        }
+
+        final int direction = fromEnd
+                ? LayoutState.ITEM_DIRECTION_HEAD
+                : LayoutState.ITEM_DIRECTION_TAIL;
+        int targetPos = anchorPos;
+        for (int i = 0; i < mInitialItemPrefetchCount; i++) {
+            if (targetPos >= 0 && targetPos < adapterItemCount) {
+                layoutPrefetchRegistry.addPosition(targetPos, 0);
+            } else {
+                break; // no more to prefetch
+            }
+            targetPos += direction;
+        }
+    }
+
+    /**
+     * Sets the number of items to prefetch in
+     * {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)}, which defines
+     * how many inner items should be prefetched when this LayoutManager's RecyclerView
+     * is nested inside another RecyclerView.
+     *
+     * <p>Set this value to the number of items this inner LayoutManager will display when it is
+     * first scrolled into the viewport. RecyclerView will attempt to prefetch that number of items
+     * so they are ready, avoiding jank as the inner RecyclerView is scrolled into the viewport.</p>
+     *
+     * <p>For example, take a vertically scrolling RecyclerView with horizontally scrolling inner
+     * RecyclerViews. The rows always have 4 items visible in them (or 5 if not aligned). Passing
+     * <code>4</code> to this method for each inner RecyclerView's LinearLayoutManager will enable
+     * RecyclerView's prefetching feature to do create/bind work for 4 views within a row early,
+     * before it is scrolled on screen, instead of just the default 2.</p>
+     *
+     * <p>Calling this method does nothing unless the LayoutManager is in a RecyclerView
+     * nested in another RecyclerView.</p>
+     *
+     * <p class="note"><strong>Note:</strong> Setting this value to be larger than the number of
+     * views that will be visible in this view can incur unnecessary bind work, and an increase to
+     * the number of Views created and in active use.</p>
+     *
+     * @param itemCount Number of items to prefetch
+     *
+     * @see #isItemPrefetchEnabled()
+     * @see #getInitialItemPrefetchCount()
+     * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
+     */
+    public void setInitialPrefetchItemCount(int itemCount) {
+        mInitialItemPrefetchCount = itemCount;
+    }
+
+    /**
+     * Gets the number of items to prefetch in
+     * {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)}, which defines
+     * how many inner items should be prefetched when this LayoutManager's RecyclerView
+     * is nested inside another RecyclerView.
+     *
+     * @see #isItemPrefetchEnabled()
+     * @see #setInitialPrefetchItemCount(int)
+     * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
+     *
+     * @return number of items to prefetch.
+     */
+    public int getInitialItemPrefetchCount() {
+        return mInitialItemPrefetchCount;
+    }
+
+    @Override
+    public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
+            LayoutPrefetchRegistry layoutPrefetchRegistry) {
+        int delta = (mOrientation == HORIZONTAL) ? dx : dy;
+        if (getChildCount() == 0 || delta == 0) {
+            // can't support this scroll, so don't bother prefetching
+            return;
+        }
+
+        final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
+        final int absDy = Math.abs(delta);
+        updateLayoutState(layoutDirection, absDy, true, state);
+        collectPrefetchPositionsForLayoutState(state, mLayoutState, layoutPrefetchRegistry);
+    }
+
+    int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
+        if (getChildCount() == 0 || dy == 0) {
+            return 0;
+        }
+        mLayoutState.mRecycle = true;
+        ensureLayoutState();
+        final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
+        final int absDy = Math.abs(dy);
+        updateLayoutState(layoutDirection, absDy, true, state);
+        final int consumed = mLayoutState.mScrollingOffset
+                + fill(recycler, mLayoutState, state, false);
+        if (consumed < 0) {
+            if (DEBUG) {
+                Log.d(TAG, "Don't have any more elements to scroll");
+            }
+            return 0;
+        }
+        final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
+        mOrientationHelper.offsetChildren(-scrolled);
+        if (DEBUG) {
+            Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
+        }
+        mLayoutState.mLastScrollDelta = scrolled;
+        return scrolled;
+    }
+
+    @Override
+    public void assertNotInLayoutOrScroll(String message) {
+        if (mPendingSavedState == null) {
+            super.assertNotInLayoutOrScroll(message);
+        }
+    }
+
+    /**
+     * Recycles children between given indices.
+     *
+     * @param startIndex inclusive
+     * @param endIndex   exclusive
+     */
+    private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
+        if (startIndex == endIndex) {
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
+        }
+        if (endIndex > startIndex) {
+            for (int i = endIndex - 1; i >= startIndex; i--) {
+                removeAndRecycleViewAt(i, recycler);
+            }
+        } else {
+            for (int i = startIndex; i > endIndex; i--) {
+                removeAndRecycleViewAt(i, recycler);
+            }
+        }
+    }
+
+    /**
+     * Recycles views that went out of bounds after scrolling towards the end of the layout.
+     * <p>
+     * Checks both layout position and visible position to guarantee that the view is not visible.
+     *
+     * @param recycler Recycler instance of {@link com.android.internal.widget.RecyclerView}
+     * @param dt       This can be used to add additional padding to the visible area. This is used
+     *                 to detect children that will go out of bounds after scrolling, without
+     *                 actually moving them.
+     */
+    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
+        if (dt < 0) {
+            if (DEBUG) {
+                Log.d(TAG, "Called recycle from start with a negative value. This might happen"
+                        + " during layout changes but may be sign of a bug");
+            }
+            return;
+        }
+        // ignore padding, ViewGroup may not clip children.
+        final int limit = dt;
+        final int childCount = getChildCount();
+        if (mShouldReverseLayout) {
+            for (int i = childCount - 1; i >= 0; i--) {
+                View child = getChildAt(i);
+                if (mOrientationHelper.getDecoratedEnd(child) > limit
+                        || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
+                    // stop here
+                    recycleChildren(recycler, childCount - 1, i);
+                    return;
+                }
+            }
+        } else {
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                if (mOrientationHelper.getDecoratedEnd(child) > limit
+                        || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
+                    // stop here
+                    recycleChildren(recycler, 0, i);
+                    return;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Recycles views that went out of bounds after scrolling towards the start of the layout.
+     * <p>
+     * Checks both layout position and visible position to guarantee that the view is not visible.
+     *
+     * @param recycler Recycler instance of {@link com.android.internal.widget.RecyclerView}
+     * @param dt       This can be used to add additional padding to the visible area. This is used
+     *                 to detect children that will go out of bounds after scrolling, without
+     *                 actually moving them.
+     */
+    private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) {
+        final int childCount = getChildCount();
+        if (dt < 0) {
+            if (DEBUG) {
+                Log.d(TAG, "Called recycle from end with a negative value. This might happen"
+                        + " during layout changes but may be sign of a bug");
+            }
+            return;
+        }
+        final int limit = mOrientationHelper.getEnd() - dt;
+        if (mShouldReverseLayout) {
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                if (mOrientationHelper.getDecoratedStart(child) < limit
+                        || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
+                    // stop here
+                    recycleChildren(recycler, 0, i);
+                    return;
+                }
+            }
+        } else {
+            for (int i = childCount - 1; i >= 0; i--) {
+                View child = getChildAt(i);
+                if (mOrientationHelper.getDecoratedStart(child) < limit
+                        || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
+                    // stop here
+                    recycleChildren(recycler, childCount - 1, i);
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * Helper method to call appropriate recycle method depending on current layout direction
+     *
+     * @param recycler    Current recycler that is attached to RecyclerView
+     * @param layoutState Current layout state. Right now, this object does not change but
+     *                    we may consider moving it out of this view so passing around as a
+     *                    parameter for now, rather than accessing {@link #mLayoutState}
+     * @see #recycleViewsFromStart(com.android.internal.widget.RecyclerView.Recycler, int)
+     * @see #recycleViewsFromEnd(com.android.internal.widget.RecyclerView.Recycler, int)
+     * @see com.android.internal.widget.LinearLayoutManager.LayoutState#mLayoutDirection
+     */
+    private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
+        if (!layoutState.mRecycle || layoutState.mInfinite) {
+            return;
+        }
+        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
+            recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
+        } else {
+            recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
+        }
+    }
+
+    /**
+     * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
+     * independent from the rest of the {@link com.android.internal.widget.LinearLayoutManager}
+     * and with little change, can be made publicly available as a helper class.
+     *
+     * @param recycler        Current recycler that is attached to RecyclerView
+     * @param layoutState     Configuration on how we should fill out the available space.
+     * @param state           Context passed by the RecyclerView to control scroll steps.
+     * @param stopOnFocusable If true, filling stops in the first focusable new child
+     * @return Number of pixels that it added. Useful for scroll functions.
+     */
+    int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
+            RecyclerView.State state, boolean stopOnFocusable) {
+        // max offset we should set is mFastScroll + available
+        final int start = layoutState.mAvailable;
+        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
+            // TODO ugly bug fix. should not happen
+            if (layoutState.mAvailable < 0) {
+                layoutState.mScrollingOffset += layoutState.mAvailable;
+            }
+            recycleByLayoutState(recycler, layoutState);
+        }
+        int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
+        LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
+        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
+            layoutChunkResult.resetInternal();
+            layoutChunk(recycler, state, layoutState, layoutChunkResult);
+            if (layoutChunkResult.mFinished) {
+                break;
+            }
+            layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
+            /**
+             * Consume the available space if:
+             * * layoutChunk did not request to be ignored
+             * * OR we are laying out scrap children
+             * * OR we are not doing pre-layout
+             */
+            if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
+                    || !state.isPreLayout()) {
+                layoutState.mAvailable -= layoutChunkResult.mConsumed;
+                // we keep a separate remaining space because mAvailable is important for recycling
+                remainingSpace -= layoutChunkResult.mConsumed;
+            }
+
+            if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
+                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
+                if (layoutState.mAvailable < 0) {
+                    layoutState.mScrollingOffset += layoutState.mAvailable;
+                }
+                recycleByLayoutState(recycler, layoutState);
+            }
+            if (stopOnFocusable && layoutChunkResult.mFocusable) {
+                break;
+            }
+        }
+        if (DEBUG) {
+            validateChildOrder();
+        }
+        return start - layoutState.mAvailable;
+    }
+
+    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
+            LayoutState layoutState, LayoutChunkResult result) {
+        View view = layoutState.next(recycler);
+        if (view == null) {
+            if (DEBUG && layoutState.mScrapList == null) {
+                throw new RuntimeException("received null view when unexpected");
+            }
+            // if we are laying out views in scrap, this may return null which means there is
+            // no more items to layout.
+            result.mFinished = true;
+            return;
+        }
+        LayoutParams params = (LayoutParams) view.getLayoutParams();
+        if (layoutState.mScrapList == null) {
+            if (mShouldReverseLayout == (layoutState.mLayoutDirection
+                    == LayoutState.LAYOUT_START)) {
+                addView(view);
+            } else {
+                addView(view, 0);
+            }
+        } else {
+            if (mShouldReverseLayout == (layoutState.mLayoutDirection
+                    == LayoutState.LAYOUT_START)) {
+                addDisappearingView(view);
+            } else {
+                addDisappearingView(view, 0);
+            }
+        }
+        measureChildWithMargins(view, 0, 0);
+        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
+        int left, top, right, bottom;
+        if (mOrientation == VERTICAL) {
+            if (isLayoutRTL()) {
+                right = getWidth() - getPaddingRight();
+                left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
+            } else {
+                left = getPaddingLeft();
+                right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
+            }
+            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
+                bottom = layoutState.mOffset;
+                top = layoutState.mOffset - result.mConsumed;
+            } else {
+                top = layoutState.mOffset;
+                bottom = layoutState.mOffset + result.mConsumed;
+            }
+        } else {
+            top = getPaddingTop();
+            bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
+
+            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
+                right = layoutState.mOffset;
+                left = layoutState.mOffset - result.mConsumed;
+            } else {
+                left = layoutState.mOffset;
+                right = layoutState.mOffset + result.mConsumed;
+            }
+        }
+        // We calculate everything with View's bounding box (which includes decor and margins)
+        // To calculate correct layout position, we subtract margins.
+        layoutDecoratedWithMargins(view, left, top, right, bottom);
+        if (DEBUG) {
+            Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
+                    + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
+                    + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));
+        }
+        // Consume the available space if the view is not removed OR changed
+        if (params.isItemRemoved() || params.isItemChanged()) {
+            result.mIgnoreConsumed = true;
+        }
+        result.mFocusable = view.isFocusable();
+    }
+
+    @Override
+    boolean shouldMeasureTwice() {
+        return getHeightMode() != View.MeasureSpec.EXACTLY
+                && getWidthMode() != View.MeasureSpec.EXACTLY
+                && hasFlexibleChildInBothOrientations();
+    }
+
+    /**
+     * Converts a focusDirection to orientation.
+     *
+     * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
+     *                       {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
+     *                       {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
+     *                       or 0 for not applicable
+     * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction
+     * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.
+     */
+    int convertFocusDirectionToLayoutDirection(int focusDirection) {
+        switch (focusDirection) {
+            case View.FOCUS_BACKWARD:
+                if (mOrientation == VERTICAL) {
+                    return LayoutState.LAYOUT_START;
+                } else if (isLayoutRTL()) {
+                    return LayoutState.LAYOUT_END;
+                } else {
+                    return LayoutState.LAYOUT_START;
+                }
+            case View.FOCUS_FORWARD:
+                if (mOrientation == VERTICAL) {
+                    return LayoutState.LAYOUT_END;
+                } else if (isLayoutRTL()) {
+                    return LayoutState.LAYOUT_START;
+                } else {
+                    return LayoutState.LAYOUT_END;
+                }
+            case View.FOCUS_UP:
+                return mOrientation == VERTICAL ? LayoutState.LAYOUT_START
+                        : LayoutState.INVALID_LAYOUT;
+            case View.FOCUS_DOWN:
+                return mOrientation == VERTICAL ? LayoutState.LAYOUT_END
+                        : LayoutState.INVALID_LAYOUT;
+            case View.FOCUS_LEFT:
+                return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START
+                        : LayoutState.INVALID_LAYOUT;
+            case View.FOCUS_RIGHT:
+                return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END
+                        : LayoutState.INVALID_LAYOUT;
+            default:
+                if (DEBUG) {
+                    Log.d(TAG, "Unknown focus request:" + focusDirection);
+                }
+                return LayoutState.INVALID_LAYOUT;
+        }
+
+    }
+
+    /**
+     * Convenience method to find the child closes to start. Caller should check it has enough
+     * children.
+     *
+     * @return The child closes to start of the layout from user's perspective.
+     */
+    private View getChildClosestToStart() {
+        return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0);
+    }
+
+    /**
+     * Convenience method to find the child closes to end. Caller should check it has enough
+     * children.
+     *
+     * @return The child closes to end of the layout from user's perspective.
+     */
+    private View getChildClosestToEnd() {
+        return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1);
+    }
+
+    /**
+     * Convenience method to find the visible child closes to start. Caller should check if it has
+     * enough children.
+     *
+     * @param completelyVisible Whether child should be completely visible or not
+     * @return The first visible child closest to start of the layout from user's perspective.
+     */
+    private View findFirstVisibleChildClosestToStart(boolean completelyVisible,
+            boolean acceptPartiallyVisible) {
+        if (mShouldReverseLayout) {
+            return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible,
+                    acceptPartiallyVisible);
+        } else {
+            return findOneVisibleChild(0, getChildCount(), completelyVisible,
+                    acceptPartiallyVisible);
+        }
+    }
+
+    /**
+     * Convenience method to find the visible child closes to end. Caller should check if it has
+     * enough children.
+     *
+     * @param completelyVisible Whether child should be completely visible or not
+     * @return The first visible child closest to end of the layout from user's perspective.
+     */
+    private View findFirstVisibleChildClosestToEnd(boolean completelyVisible,
+            boolean acceptPartiallyVisible) {
+        if (mShouldReverseLayout) {
+            return findOneVisibleChild(0, getChildCount(), completelyVisible,
+                    acceptPartiallyVisible);
+        } else {
+            return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible,
+                    acceptPartiallyVisible);
+        }
+    }
+
+
+    /**
+     * Among the children that are suitable to be considered as an anchor child, returns the one
+     * closest to the end of the layout.
+     * <p>
+     * Due to ambiguous adapter updates or children being removed, some children's positions may be
+     * invalid. This method is a best effort to find a position within adapter bounds if possible.
+     * <p>
+     * It also prioritizes children that are within the visible bounds.
+     * @return A View that can be used an an anchor View.
+     */
+    private View findReferenceChildClosestToEnd(RecyclerView.Recycler recycler,
+            RecyclerView.State state) {
+        return mShouldReverseLayout ? findFirstReferenceChild(recycler, state) :
+                findLastReferenceChild(recycler, state);
+    }
+
+    /**
+     * Among the children that are suitable to be considered as an anchor child, returns the one
+     * closest to the start of the layout.
+     * <p>
+     * Due to ambiguous adapter updates or children being removed, some children's positions may be
+     * invalid. This method is a best effort to find a position within adapter bounds if possible.
+     * <p>
+     * It also prioritizes children that are within the visible bounds.
+     *
+     * @return A View that can be used an an anchor View.
+     */
+    private View findReferenceChildClosestToStart(RecyclerView.Recycler recycler,
+            RecyclerView.State state) {
+        return mShouldReverseLayout ? findLastReferenceChild(recycler, state) :
+                findFirstReferenceChild(recycler, state);
+    }
+
+    private View findFirstReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state) {
+        return findReferenceChild(recycler, state, 0, getChildCount(), state.getItemCount());
+    }
+
+    private View findLastReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state) {
+        return findReferenceChild(recycler, state, getChildCount() - 1, -1, state.getItemCount());
+    }
+
+    // overridden by GridLayoutManager
+    View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state,
+            int start, int end, int itemCount) {
+        ensureLayoutState();
+        View invalidMatch = null;
+        View outOfBoundsMatch = null;
+        final int boundsStart = mOrientationHelper.getStartAfterPadding();
+        final int boundsEnd = mOrientationHelper.getEndAfterPadding();
+        final int diff = end > start ? 1 : -1;
+        for (int i = start; i != end; i += diff) {
+            final View view = getChildAt(i);
+            final int position = getPosition(view);
+            if (position >= 0 && position < itemCount) {
+                if (((LayoutParams) view.getLayoutParams()).isItemRemoved()) {
+                    if (invalidMatch == null) {
+                        invalidMatch = view; // removed item, least preferred
+                    }
+                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd
+                        || mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
+                    if (outOfBoundsMatch == null) {
+                        outOfBoundsMatch = view; // item is not visible, less preferred
+                    }
+                } else {
+                    return view;
+                }
+            }
+        }
+        return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;
+    }
+
+    /**
+     * Returns the adapter position of the first visible view. This position does not include
+     * adapter changes that were dispatched after the last layout pass.
+     * <p>
+     * Note that, this value is not affected by layout orientation or item order traversal.
+     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
+     * not in the layout.
+     * <p>
+     * If RecyclerView has item decorators, they will be considered in calculations as well.
+     * <p>
+     * LayoutManager may pre-cache some views that are not necessarily visible. Those views
+     * are ignored in this method.
+     *
+     * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if
+     * there aren't any visible items.
+     * @see #findFirstCompletelyVisibleItemPosition()
+     * @see #findLastVisibleItemPosition()
+     */
+    public int findFirstVisibleItemPosition() {
+        final View child = findOneVisibleChild(0, getChildCount(), false, true);
+        return child == null ? NO_POSITION : getPosition(child);
+    }
+
+    /**
+     * Returns the adapter position of the first fully visible view. This position does not include
+     * adapter changes that were dispatched after the last layout pass.
+     * <p>
+     * Note that bounds check is only performed in the current orientation. That means, if
+     * LayoutManager is horizontal, it will only check the view's left and right edges.
+     *
+     * @return The adapter position of the first fully visible item or
+     * {@link RecyclerView#NO_POSITION} if there aren't any visible items.
+     * @see #findFirstVisibleItemPosition()
+     * @see #findLastCompletelyVisibleItemPosition()
+     */
+    public int findFirstCompletelyVisibleItemPosition() {
+        final View child = findOneVisibleChild(0, getChildCount(), true, false);
+        return child == null ? NO_POSITION : getPosition(child);
+    }
+
+    /**
+     * Returns the adapter position of the last visible view. This position does not include
+     * adapter changes that were dispatched after the last layout pass.
+     * <p>
+     * Note that, this value is not affected by layout orientation or item order traversal.
+     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
+     * not in the layout.
+     * <p>
+     * If RecyclerView has item decorators, they will be considered in calculations as well.
+     * <p>
+     * LayoutManager may pre-cache some views that are not necessarily visible. Those views
+     * are ignored in this method.
+     *
+     * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if
+     * there aren't any visible items.
+     * @see #findLastCompletelyVisibleItemPosition()
+     * @see #findFirstVisibleItemPosition()
+     */
+    public int findLastVisibleItemPosition() {
+        final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true);
+        return child == null ? NO_POSITION : getPosition(child);
+    }
+
+    /**
+     * Returns the adapter position of the last fully visible view. This position does not include
+     * adapter changes that were dispatched after the last layout pass.
+     * <p>
+     * Note that bounds check is only performed in the current orientation. That means, if
+     * LayoutManager is horizontal, it will only check the view's left and right edges.
+     *
+     * @return The adapter position of the last fully visible view or
+     * {@link RecyclerView#NO_POSITION} if there aren't any visible items.
+     * @see #findLastVisibleItemPosition()
+     * @see #findFirstCompletelyVisibleItemPosition()
+     */
+    public int findLastCompletelyVisibleItemPosition() {
+        final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false);
+        return child == null ? NO_POSITION : getPosition(child);
+    }
+
+    View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible,
+            boolean acceptPartiallyVisible) {
+        ensureLayoutState();
+        final int start = mOrientationHelper.getStartAfterPadding();
+        final int end = mOrientationHelper.getEndAfterPadding();
+        final int next = toIndex > fromIndex ? 1 : -1;
+        View partiallyVisible = null;
+        for (int i = fromIndex; i != toIndex; i += next) {
+            final View child = getChildAt(i);
+            final int childStart = mOrientationHelper.getDecoratedStart(child);
+            final int childEnd = mOrientationHelper.getDecoratedEnd(child);
+            if (childStart < end && childEnd > start) {
+                if (completelyVisible) {
+                    if (childStart >= start && childEnd <= end) {
+                        return child;
+                    } else if (acceptPartiallyVisible && partiallyVisible == null) {
+                        partiallyVisible = child;
+                    }
+                } else {
+                    return child;
+                }
+            }
+        }
+        return partiallyVisible;
+    }
+
+    @Override
+    public View onFocusSearchFailed(View focused, int focusDirection,
+            RecyclerView.Recycler recycler, RecyclerView.State state) {
+        resolveShouldLayoutReverse();
+        if (getChildCount() == 0) {
+            return null;
+        }
+
+        final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection);
+        if (layoutDir == LayoutState.INVALID_LAYOUT) {
+            return null;
+        }
+        ensureLayoutState();
+        final View referenceChild;
+        if (layoutDir == LayoutState.LAYOUT_START) {
+            referenceChild = findReferenceChildClosestToStart(recycler, state);
+        } else {
+            referenceChild = findReferenceChildClosestToEnd(recycler, state);
+        }
+        if (referenceChild == null) {
+            if (DEBUG) {
+                Log.d(TAG,
+                        "Cannot find a child with a valid position to be used for focus search.");
+            }
+            return null;
+        }
+        ensureLayoutState();
+        final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace());
+        updateLayoutState(layoutDir, maxScroll, false, state);
+        mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN;
+        mLayoutState.mRecycle = false;
+        fill(recycler, mLayoutState, state, true);
+        final View nextFocus;
+        if (layoutDir == LayoutState.LAYOUT_START) {
+            nextFocus = getChildClosestToStart();
+        } else {
+            nextFocus = getChildClosestToEnd();
+        }
+        if (nextFocus == referenceChild || !nextFocus.isFocusable()) {
+            return null;
+        }
+        return nextFocus;
+    }
+
+    /**
+     * Used for debugging.
+     * Logs the internal representation of children to default logger.
+     */
+    private void logChildren() {
+        Log.d(TAG, "internal representation of views on the screen");
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            Log.d(TAG, "item " + getPosition(child) + ", coord:"
+                    + mOrientationHelper.getDecoratedStart(child));
+        }
+        Log.d(TAG, "==============");
+    }
+
+    /**
+     * Used for debugging.
+     * Validates that child views are laid out in correct order. This is important because rest of
+     * the algorithm relies on this constraint.
+     *
+     * In default layout, child 0 should be closest to screen position 0 and last child should be
+     * closest to position WIDTH or HEIGHT.
+     * In reverse layout, last child should be closes to screen position 0 and first child should
+     * be closest to position WIDTH  or HEIGHT
+     */
+    void validateChildOrder() {
+        Log.d(TAG, "validating child count " + getChildCount());
+        if (getChildCount() < 1) {
+            return;
+        }
+        int lastPos = getPosition(getChildAt(0));
+        int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0));
+        if (mShouldReverseLayout) {
+            for (int i = 1; i < getChildCount(); i++) {
+                View child = getChildAt(i);
+                int pos = getPosition(child);
+                int screenLoc = mOrientationHelper.getDecoratedStart(child);
+                if (pos < lastPos) {
+                    logChildren();
+                    throw new RuntimeException("detected invalid position. loc invalid? "
+                            + (screenLoc < lastScreenLoc));
+                }
+                if (screenLoc > lastScreenLoc) {
+                    logChildren();
+                    throw new RuntimeException("detected invalid location");
+                }
+            }
+        } else {
+            for (int i = 1; i < getChildCount(); i++) {
+                View child = getChildAt(i);
+                int pos = getPosition(child);
+                int screenLoc = mOrientationHelper.getDecoratedStart(child);
+                if (pos < lastPos) {
+                    logChildren();
+                    throw new RuntimeException("detected invalid position. loc invalid? "
+                            + (screenLoc < lastScreenLoc));
+                }
+                if (screenLoc < lastScreenLoc) {
+                    logChildren();
+                    throw new RuntimeException("detected invalid location");
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean supportsPredictiveItemAnimations() {
+        return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd;
+    }
+
+    /**
+     * @hide This method should be called by ItemTouchHelper only.
+     */
+    @Override
+    public void prepareForDrop(View view, View target, int x, int y) {
+        assertNotInLayoutOrScroll("Cannot drop a view during a scroll or layout calculation");
+        ensureLayoutState();
+        resolveShouldLayoutReverse();
+        final int myPos = getPosition(view);
+        final int targetPos = getPosition(target);
+        final int dropDirection = myPos < targetPos ? LayoutState.ITEM_DIRECTION_TAIL
+                : LayoutState.ITEM_DIRECTION_HEAD;
+        if (mShouldReverseLayout) {
+            if (dropDirection == LayoutState.ITEM_DIRECTION_TAIL) {
+                scrollToPositionWithOffset(targetPos,
+                        mOrientationHelper.getEndAfterPadding()
+                                - (mOrientationHelper.getDecoratedStart(target)
+                                        + mOrientationHelper.getDecoratedMeasurement(view)));
+            } else {
+                scrollToPositionWithOffset(targetPos,
+                        mOrientationHelper.getEndAfterPadding()
+                                - mOrientationHelper.getDecoratedEnd(target));
+            }
+        } else {
+            if (dropDirection == LayoutState.ITEM_DIRECTION_HEAD) {
+                scrollToPositionWithOffset(targetPos, mOrientationHelper.getDecoratedStart(target));
+            } else {
+                scrollToPositionWithOffset(targetPos,
+                        mOrientationHelper.getDecoratedEnd(target)
+                                - mOrientationHelper.getDecoratedMeasurement(view));
+            }
+        }
+    }
+
+    /**
+     * Helper class that keeps temporary state while {LayoutManager} is filling out the empty
+     * space.
+     */
+    static class LayoutState {
+
+        static final String TAG = "LLM#LayoutState";
+
+        static final int LAYOUT_START = -1;
+
+        static final int LAYOUT_END = 1;
+
+        static final int INVALID_LAYOUT = Integer.MIN_VALUE;
+
+        static final int ITEM_DIRECTION_HEAD = -1;
+
+        static final int ITEM_DIRECTION_TAIL = 1;
+
+        static final int SCROLLING_OFFSET_NaN = Integer.MIN_VALUE;
+
+        /**
+         * We may not want to recycle children in some cases (e.g. layout)
+         */
+        boolean mRecycle = true;
+
+        /**
+         * Pixel offset where layout should start
+         */
+        int mOffset;
+
+        /**
+         * Number of pixels that we should fill, in the layout direction.
+         */
+        int mAvailable;
+
+        /**
+         * Current position on the adapter to get the next item.
+         */
+        int mCurrentPosition;
+
+        /**
+         * Defines the direction in which the data adapter is traversed.
+         * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}
+         */
+        int mItemDirection;
+
+        /**
+         * Defines the direction in which the layout is filled.
+         * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}
+         */
+        int mLayoutDirection;
+
+        /**
+         * Used when LayoutState is constructed in a scrolling state.
+         * It should be set the amount of scrolling we can make without creating a new view.
+         * Settings this is required for efficient view recycling.
+         */
+        int mScrollingOffset;
+
+        /**
+         * Used if you want to pre-layout items that are not yet visible.
+         * The difference with {@link #mAvailable} is that, when recycling, distance laid out for
+         * {@link #mExtra} is not considered to avoid recycling visible children.
+         */
+        int mExtra = 0;
+
+        /**
+         * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value
+         * is set to true, we skip removed views since they should not be laid out in post layout
+         * step.
+         */
+        boolean mIsPreLayout = false;
+
+        /**
+         * The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)}
+         * amount.
+         */
+        int mLastScrollDelta;
+
+        /**
+         * When LLM needs to layout particular views, it sets this list in which case, LayoutState
+         * will only return views from this list and return null if it cannot find an item.
+         */
+        List<RecyclerView.ViewHolder> mScrapList = null;
+
+        /**
+         * Used when there is no limit in how many views can be laid out.
+         */
+        boolean mInfinite;
+
+        /**
+         * @return true if there are more items in the data adapter
+         */
+        boolean hasMore(RecyclerView.State state) {
+            return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
+        }
+
+        /**
+         * Gets the view for the next element that we should layout.
+         * Also updates current item index to the next item, based on {@link #mItemDirection}
+         *
+         * @return The next element that we should layout.
+         */
+        View next(RecyclerView.Recycler recycler) {
+            if (mScrapList != null) {
+                return nextViewFromScrapList();
+            }
+            final View view = recycler.getViewForPosition(mCurrentPosition);
+            mCurrentPosition += mItemDirection;
+            return view;
+        }
+
+        /**
+         * Returns the next item from the scrap list.
+         * <p>
+         * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection
+         *
+         * @return View if an item in the current position or direction exists if not null.
+         */
+        private View nextViewFromScrapList() {
+            final int size = mScrapList.size();
+            for (int i = 0; i < size; i++) {
+                final View view = mScrapList.get(i).itemView;
+                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+                if (lp.isItemRemoved()) {
+                    continue;
+                }
+                if (mCurrentPosition == lp.getViewLayoutPosition()) {
+                    assignPositionFromScrapList(view);
+                    return view;
+                }
+            }
+            return null;
+        }
+
+        public void assignPositionFromScrapList() {
+            assignPositionFromScrapList(null);
+        }
+
+        public void assignPositionFromScrapList(View ignore) {
+            final View closest = nextViewInLimitedList(ignore);
+            if (closest == null) {
+                mCurrentPosition = NO_POSITION;
+            } else {
+                mCurrentPosition = ((LayoutParams) closest.getLayoutParams())
+                        .getViewLayoutPosition();
+            }
+        }
+
+        public View nextViewInLimitedList(View ignore) {
+            int size = mScrapList.size();
+            View closest = null;
+            int closestDistance = Integer.MAX_VALUE;
+            if (DEBUG && mIsPreLayout) {
+                throw new IllegalStateException("Scrap list cannot be used in pre layout");
+            }
+            for (int i = 0; i < size; i++) {
+                View view = mScrapList.get(i).itemView;
+                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+                if (view == ignore || lp.isItemRemoved()) {
+                    continue;
+                }
+                final int distance = (lp.getViewLayoutPosition() - mCurrentPosition)
+                        * mItemDirection;
+                if (distance < 0) {
+                    continue; // item is not in current direction
+                }
+                if (distance < closestDistance) {
+                    closest = view;
+                    closestDistance = distance;
+                    if (distance == 0) {
+                        break;
+                    }
+                }
+            }
+            return closest;
+        }
+
+        void log() {
+            Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:"
+                    + mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static class SavedState implements Parcelable {
+
+        int mAnchorPosition;
+
+        int mAnchorOffset;
+
+        boolean mAnchorLayoutFromEnd;
+
+        public SavedState() {
+
+        }
+
+        SavedState(Parcel in) {
+            mAnchorPosition = in.readInt();
+            mAnchorOffset = in.readInt();
+            mAnchorLayoutFromEnd = in.readInt() == 1;
+        }
+
+        public SavedState(SavedState other) {
+            mAnchorPosition = other.mAnchorPosition;
+            mAnchorOffset = other.mAnchorOffset;
+            mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd;
+        }
+
+        boolean hasValidAnchor() {
+            return mAnchorPosition >= 0;
+        }
+
+        void invalidateAnchor() {
+            mAnchorPosition = NO_POSITION;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mAnchorPosition);
+            dest.writeInt(mAnchorOffset);
+            dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    @Override
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+
+                    @Override
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
+
+    /**
+     * Simple data class to keep Anchor information
+     */
+    class AnchorInfo {
+        int mPosition;
+        int mCoordinate;
+        boolean mLayoutFromEnd;
+        boolean mValid;
+
+        AnchorInfo() {
+            reset();
+        }
+
+        void reset() {
+            mPosition = NO_POSITION;
+            mCoordinate = INVALID_OFFSET;
+            mLayoutFromEnd = false;
+            mValid = false;
+        }
+
+        /**
+         * assigns anchor coordinate from the RecyclerView's padding depending on current
+         * layoutFromEnd value
+         */
+        void assignCoordinateFromPadding() {
+            mCoordinate = mLayoutFromEnd
+                    ? mOrientationHelper.getEndAfterPadding()
+                    : mOrientationHelper.getStartAfterPadding();
+        }
+
+        @Override
+        public String toString() {
+            return "AnchorInfo{"
+                    + "mPosition=" + mPosition
+                    + ", mCoordinate=" + mCoordinate
+                    + ", mLayoutFromEnd=" + mLayoutFromEnd
+                    + ", mValid=" + mValid
+                    + '}';
+        }
+
+        boolean isViewValidAsAnchor(View child, RecyclerView.State state) {
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            return !lp.isItemRemoved() && lp.getViewLayoutPosition() >= 0
+                    && lp.getViewLayoutPosition() < state.getItemCount();
+        }
+
+        public void assignFromViewAndKeepVisibleRect(View child) {
+            final int spaceChange = mOrientationHelper.getTotalSpaceChange();
+            if (spaceChange >= 0) {
+                assignFromView(child);
+                return;
+            }
+            mPosition = getPosition(child);
+            if (mLayoutFromEnd) {
+                final int prevLayoutEnd = mOrientationHelper.getEndAfterPadding() - spaceChange;
+                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
+                final int previousEndMargin = prevLayoutEnd - childEnd;
+                mCoordinate = mOrientationHelper.getEndAfterPadding() - previousEndMargin;
+                // ensure we did not push child's top out of bounds because of this
+                if (previousEndMargin > 0) { // we have room to shift bottom if necessary
+                    final int childSize = mOrientationHelper.getDecoratedMeasurement(child);
+                    final int estimatedChildStart = mCoordinate - childSize;
+                    final int layoutStart = mOrientationHelper.getStartAfterPadding();
+                    final int previousStartMargin = mOrientationHelper.getDecoratedStart(child)
+                            - layoutStart;
+                    final int startReference = layoutStart + Math.min(previousStartMargin, 0);
+                    final int startMargin = estimatedChildStart - startReference;
+                    if (startMargin < 0) {
+                        // offset to make top visible but not too much
+                        mCoordinate += Math.min(previousEndMargin, -startMargin);
+                    }
+                }
+            } else {
+                final int childStart = mOrientationHelper.getDecoratedStart(child);
+                final int startMargin = childStart - mOrientationHelper.getStartAfterPadding();
+                mCoordinate = childStart;
+                if (startMargin > 0) { // we have room to fix end as well
+                    final int estimatedEnd = childStart
+                            + mOrientationHelper.getDecoratedMeasurement(child);
+                    final int previousLayoutEnd = mOrientationHelper.getEndAfterPadding()
+                            - spaceChange;
+                    final int previousEndMargin = previousLayoutEnd
+                            - mOrientationHelper.getDecoratedEnd(child);
+                    final int endReference = mOrientationHelper.getEndAfterPadding()
+                            - Math.min(0, previousEndMargin);
+                    final int endMargin = endReference - estimatedEnd;
+                    if (endMargin < 0) {
+                        mCoordinate -= Math.min(startMargin, -endMargin);
+                    }
+                }
+            }
+        }
+
+        public void assignFromView(View child) {
+            if (mLayoutFromEnd) {
+                mCoordinate = mOrientationHelper.getDecoratedEnd(child)
+                        + mOrientationHelper.getTotalSpaceChange();
+            } else {
+                mCoordinate = mOrientationHelper.getDecoratedStart(child);
+            }
+
+            mPosition = getPosition(child);
+        }
+    }
+
+    protected static class LayoutChunkResult {
+        public int mConsumed;
+        public boolean mFinished;
+        public boolean mIgnoreConsumed;
+        public boolean mFocusable;
+
+        void resetInternal() {
+            mConsumed = 0;
+            mFinished = false;
+            mIgnoreConsumed = false;
+            mFocusable = false;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/LinearSmoothScroller.java b/core/java/com/android/internal/widget/LinearSmoothScroller.java
new file mode 100644
index 0000000..d024f21
--- /dev/null
+++ b/core/java/com/android/internal/widget/LinearSmoothScroller.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.PointF;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+
+/**
+ * {@link RecyclerView.SmoothScroller} implementation which uses a {@link LinearInterpolator} until
+ * the target position becomes a child of the RecyclerView and then uses a
+ * {@link DecelerateInterpolator} to slowly approach to target position.
+ * <p>
+ * If the {@link RecyclerView.LayoutManager} you are using does not implement the
+ * {@link RecyclerView.SmoothScroller.ScrollVectorProvider} interface, then you must override the
+ * {@link #computeScrollVectorForPosition(int)} method. All the LayoutManagers bundled with
+ * the support library implement this interface.
+ */
+public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
+
+    private static final String TAG = "LinearSmoothScroller";
+
+    private static final boolean DEBUG = false;
+
+    private static final float MILLISECONDS_PER_INCH = 25f;
+
+    private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
+
+    /**
+     * Align child view's left or top with parent view's left or top
+     *
+     * @see #calculateDtToFit(int, int, int, int, int)
+     * @see #calculateDxToMakeVisible(android.view.View, int)
+     * @see #calculateDyToMakeVisible(android.view.View, int)
+     */
+    public static final int SNAP_TO_START = -1;
+
+    /**
+     * Align child view's right or bottom with parent view's right or bottom
+     *
+     * @see #calculateDtToFit(int, int, int, int, int)
+     * @see #calculateDxToMakeVisible(android.view.View, int)
+     * @see #calculateDyToMakeVisible(android.view.View, int)
+     */
+    public static final int SNAP_TO_END = 1;
+
+    /**
+     * <p>Decides if the child should be snapped from start or end, depending on where it
+     * currently is in relation to its parent.</p>
+     * <p>For instance, if the view is virtually on the left of RecyclerView, using
+     * {@code SNAP_TO_ANY} is the same as using {@code SNAP_TO_START}</p>
+     *
+     * @see #calculateDtToFit(int, int, int, int, int)
+     * @see #calculateDxToMakeVisible(android.view.View, int)
+     * @see #calculateDyToMakeVisible(android.view.View, int)
+     */
+    public static final int SNAP_TO_ANY = 0;
+
+    // Trigger a scroll to a further distance than TARGET_SEEK_SCROLL_DISTANCE_PX so that if target
+    // view is not laid out until interim target position is reached, we can detect the case before
+    // scrolling slows down and reschedule another interim target scroll
+    private static final float TARGET_SEEK_EXTRA_SCROLL_RATIO = 1.2f;
+
+    protected final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
+
+    protected final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
+
+    protected PointF mTargetVector;
+
+    private final float MILLISECONDS_PER_PX;
+
+    // Temporary variables to keep track of the interim scroll target. These values do not
+    // point to a real item position, rather point to an estimated location pixels.
+    protected int mInterimTargetDx = 0, mInterimTargetDy = 0;
+
+    public LinearSmoothScroller(Context context) {
+        MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onStart() {
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
+        final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference());
+        final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
+        final int distance = (int) Math.sqrt(dx * dx + dy * dy);
+        final int time = calculateTimeForDeceleration(distance);
+        if (time > 0) {
+            action.update(-dx, -dy, time, mDecelerateInterpolator);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onSeekTargetStep(int dx, int dy, RecyclerView.State state, Action action) {
+        if (getChildCount() == 0) {
+            stop();
+            return;
+        }
+        //noinspection PointlessBooleanExpression
+        if (DEBUG && mTargetVector != null
+                && ((mTargetVector.x * dx < 0 || mTargetVector.y * dy < 0))) {
+            throw new IllegalStateException("Scroll happened in the opposite direction"
+                    + " of the target. Some calculations are wrong");
+        }
+        mInterimTargetDx = clampApplyScroll(mInterimTargetDx, dx);
+        mInterimTargetDy = clampApplyScroll(mInterimTargetDy, dy);
+
+        if (mInterimTargetDx == 0 && mInterimTargetDy == 0) {
+            updateActionForInterimTarget(action);
+        } // everything is valid, keep going
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onStop() {
+        mInterimTargetDx = mInterimTargetDy = 0;
+        mTargetVector = null;
+    }
+
+    /**
+     * Calculates the scroll speed.
+     *
+     * @param displayMetrics DisplayMetrics to be used for real dimension calculations
+     * @return The time (in ms) it should take for each pixel. For instance, if returned value is
+     * 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds.
+     */
+    protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
+        return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
+    }
+
+    /**
+     * <p>Calculates the time for deceleration so that transition from LinearInterpolator to
+     * DecelerateInterpolator looks smooth.</p>
+     *
+     * @param dx Distance to scroll
+     * @return Time for DecelerateInterpolator to smoothly traverse the distance when transitioning
+     * from LinearInterpolation
+     */
+    protected int calculateTimeForDeceleration(int dx) {
+        // we want to cover same area with the linear interpolator for the first 10% of the
+        // interpolation. After that, deceleration will take control.
+        // area under curve (1-(1-x)^2) can be calculated as (1 - x/3) * x * x
+        // which gives 0.100028 when x = .3356
+        // this is why we divide linear scrolling time with .3356
+        return  (int) Math.ceil(calculateTimeForScrolling(dx) / .3356);
+    }
+
+    /**
+     * Calculates the time it should take to scroll the given distance (in pixels)
+     *
+     * @param dx Distance in pixels that we want to scroll
+     * @return Time in milliseconds
+     * @see #calculateSpeedPerPixel(android.util.DisplayMetrics)
+     */
+    protected int calculateTimeForScrolling(int dx) {
+        // In a case where dx is very small, rounding may return 0 although dx > 0.
+        // To avoid that issue, ceil the result so that if dx > 0, we'll always return positive
+        // time.
+        return (int) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX);
+    }
+
+    /**
+     * When scrolling towards a child view, this method defines whether we should align the left
+     * or the right edge of the child with the parent RecyclerView.
+     *
+     * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
+     * @see #SNAP_TO_START
+     * @see #SNAP_TO_END
+     * @see #SNAP_TO_ANY
+     */
+    protected int getHorizontalSnapPreference() {
+        return mTargetVector == null || mTargetVector.x == 0 ? SNAP_TO_ANY :
+                mTargetVector.x > 0 ? SNAP_TO_END : SNAP_TO_START;
+    }
+
+    /**
+     * When scrolling towards a child view, this method defines whether we should align the top
+     * or the bottom edge of the child with the parent RecyclerView.
+     *
+     * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
+     * @see #SNAP_TO_START
+     * @see #SNAP_TO_END
+     * @see #SNAP_TO_ANY
+     */
+    protected int getVerticalSnapPreference() {
+        return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
+                mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
+    }
+
+    /**
+     * When the target scroll position is not a child of the RecyclerView, this method calculates
+     * a direction vector towards that child and triggers a smooth scroll.
+     *
+     * @see #computeScrollVectorForPosition(int)
+     */
+    protected void updateActionForInterimTarget(Action action) {
+        // find an interim target position
+        PointF scrollVector = computeScrollVectorForPosition(getTargetPosition());
+        if (scrollVector == null || (scrollVector.x == 0 && scrollVector.y == 0)) {
+            final int target = getTargetPosition();
+            action.jumpTo(target);
+            stop();
+            return;
+        }
+        normalize(scrollVector);
+        mTargetVector = scrollVector;
+
+        mInterimTargetDx = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.x);
+        mInterimTargetDy = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.y);
+        final int time = calculateTimeForScrolling(TARGET_SEEK_SCROLL_DISTANCE_PX);
+        // To avoid UI hiccups, trigger a smooth scroll to a distance little further than the
+        // interim target. Since we track the distance travelled in onSeekTargetStep callback, it
+        // won't actually scroll more than what we need.
+        action.update((int) (mInterimTargetDx * TARGET_SEEK_EXTRA_SCROLL_RATIO),
+                (int) (mInterimTargetDy * TARGET_SEEK_EXTRA_SCROLL_RATIO),
+                (int) (time * TARGET_SEEK_EXTRA_SCROLL_RATIO), mLinearInterpolator);
+    }
+
+    private int clampApplyScroll(int tmpDt, int dt) {
+        final int before = tmpDt;
+        tmpDt -= dt;
+        if (before * tmpDt <= 0) { // changed sign, reached 0 or was 0, reset
+            return 0;
+        }
+        return tmpDt;
+    }
+
+    /**
+     * Helper method for {@link #calculateDxToMakeVisible(android.view.View, int)} and
+     * {@link #calculateDyToMakeVisible(android.view.View, int)}
+     */
+    public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
+            snapPreference) {
+        switch (snapPreference) {
+            case SNAP_TO_START:
+                return boxStart - viewStart;
+            case SNAP_TO_END:
+                return boxEnd - viewEnd;
+            case SNAP_TO_ANY:
+                final int dtStart = boxStart - viewStart;
+                if (dtStart > 0) {
+                    return dtStart;
+                }
+                final int dtEnd = boxEnd - viewEnd;
+                if (dtEnd < 0) {
+                    return dtEnd;
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("snap preference should be one of the"
+                        + " constants defined in SmoothScroller, starting with SNAP_");
+        }
+        return 0;
+    }
+
+    /**
+     * Calculates the vertical scroll amount necessary to make the given view fully visible
+     * inside the RecyclerView.
+     *
+     * @param view           The view which we want to make fully visible
+     * @param snapPreference The edge which the view should snap to when entering the visible
+     *                       area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
+     *                       {@link #SNAP_TO_ANY}.
+     * @return The vertical scroll amount necessary to make the view visible with the given
+     * snap preference.
+     */
+    public int calculateDyToMakeVisible(View view, int snapPreference) {
+        final RecyclerView.LayoutManager layoutManager = getLayoutManager();
+        if (layoutManager == null || !layoutManager.canScrollVertically()) {
+            return 0;
+        }
+        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                view.getLayoutParams();
+        final int top = layoutManager.getDecoratedTop(view) - params.topMargin;
+        final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin;
+        final int start = layoutManager.getPaddingTop();
+        final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom();
+        return calculateDtToFit(top, bottom, start, end, snapPreference);
+    }
+
+    /**
+     * Calculates the horizontal scroll amount necessary to make the given view fully visible
+     * inside the RecyclerView.
+     *
+     * @param view           The view which we want to make fully visible
+     * @param snapPreference The edge which the view should snap to when entering the visible
+     *                       area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
+     *                       {@link #SNAP_TO_END}
+     * @return The vertical scroll amount necessary to make the view visible with the given
+     * snap preference.
+     */
+    public int calculateDxToMakeVisible(View view, int snapPreference) {
+        final RecyclerView.LayoutManager layoutManager = getLayoutManager();
+        if (layoutManager == null || !layoutManager.canScrollHorizontally()) {
+            return 0;
+        }
+        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                view.getLayoutParams();
+        final int left = layoutManager.getDecoratedLeft(view) - params.leftMargin;
+        final int right = layoutManager.getDecoratedRight(view) + params.rightMargin;
+        final int start = layoutManager.getPaddingLeft();
+        final int end = layoutManager.getWidth() - layoutManager.getPaddingRight();
+        return calculateDtToFit(left, right, start, end, snapPreference);
+    }
+
+    /**
+     * Compute the scroll vector for a given target position.
+     * <p>
+     * This method can return null if the layout manager cannot calculate a scroll vector
+     * for the given position (e.g. it has no current scroll position).
+     *
+     * @param targetPosition the position to which the scroller is scrolling
+     *
+     * @return the scroll vector for a given target position
+     */
+    @Nullable
+    public PointF computeScrollVectorForPosition(int targetPosition) {
+        RecyclerView.LayoutManager layoutManager = getLayoutManager();
+        if (layoutManager instanceof ScrollVectorProvider) {
+            return ((ScrollVectorProvider) layoutManager)
+                    .computeScrollVectorForPosition(targetPosition);
+        }
+        Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
+                + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
+        return null;
+    }
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a43f3a7..58fb145 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -43,6 +43,7 @@
 import android.util.Log;
 import android.util.SparseIntArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.google.android.collect.Lists;
 
 import libcore.util.HexEncoding;
@@ -239,7 +240,8 @@
         mHandler = looper != null ? new Handler(looper) : null;
     }
 
-    private ILockSettings getLockSettings() {
+    @VisibleForTesting
+    public ILockSettings getLockSettings() {
         if (mLockSettingsService == null) {
             ILockSettings service = ILockSettings.Stub.asInterface(
                     ServiceManager.getService("lock_settings"));
@@ -298,6 +300,10 @@
         getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
     }
 
+    public void reportPasswordLockout(int timeoutMs, int userId) {
+        getTrustManager().reportUnlockLockout(timeoutMs, userId);
+    }
+
     public int getCurrentFailedPasswordAttempts(int userId) {
         return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
     }
@@ -1310,7 +1316,7 @@
     }
 
     private boolean isDoNotAskCredentialsOnBootSet() {
-        return mDevicePolicyManager.getDoNotAskCredentialsOnBoot();
+        return getDevicePolicyManager().getDoNotAskCredentialsOnBoot();
     }
 
     private boolean shouldEncryptWithCredentials(boolean defaultValue) {
diff --git a/core/java/com/android/internal/widget/NestedScrollingChild.java b/core/java/com/android/internal/widget/NestedScrollingChild.java
new file mode 100644
index 0000000..20285b5
--- /dev/null
+++ b/core/java/com/android/internal/widget/NestedScrollingChild.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
+
+/**
+ * This interface should be implemented by {@link android.view.View View} subclasses that wish
+ * to support dispatching nested scrolling operations to a cooperating parent
+ * {@link android.view.ViewGroup ViewGroup}.
+ *
+ * <p>Classes implementing this interface should create a final instance of a
+ * {@link NestedScrollingChildHelper} as a field and delegate any View methods to the
+ * <code>NestedScrollingChildHelper</code> methods of the same signature.</p>
+ *
+ * <p>Views invoking nested scrolling functionality should always do so from the relevant
+ * {@link ViewCompat}, {@link ViewGroupCompat} or {@link ViewParentCompat} compatibility
+ * shim static methods. This ensures interoperability with nested scrolling views on Android
+ * 5.0 Lollipop and newer.</p>
+ */
+public interface NestedScrollingChild {
+    /**
+     * Enable or disable nested scrolling for this view.
+     *
+     * <p>If this property is set to true the view will be permitted to initiate nested
+     * scrolling operations with a compatible parent view in the current hierarchy. If this
+     * view does not implement nested scrolling this will have no effect. Disabling nested scrolling
+     * while a nested scroll is in progress has the effect of {@link #stopNestedScroll() stopping}
+     * the nested scroll.</p>
+     *
+     * @param enabled true to enable nested scrolling, false to disable
+     *
+     * @see #isNestedScrollingEnabled()
+     */
+    void setNestedScrollingEnabled(boolean enabled);
+
+    /**
+     * Returns true if nested scrolling is enabled for this view.
+     *
+     * <p>If nested scrolling is enabled and this View class implementation supports it,
+     * this view will act as a nested scrolling child view when applicable, forwarding data
+     * about the scroll operation in progress to a compatible and cooperating nested scrolling
+     * parent.</p>
+     *
+     * @return true if nested scrolling is enabled
+     *
+     * @see #setNestedScrollingEnabled(boolean)
+     */
+    boolean isNestedScrollingEnabled();
+
+    /**
+     * Begin a nestable scroll operation along the given axes.
+     *
+     * <p>A view starting a nested scroll promises to abide by the following contract:</p>
+     *
+     * <p>The view will call startNestedScroll upon initiating a scroll operation. In the case
+     * of a touch scroll this corresponds to the initial {@link MotionEvent#ACTION_DOWN}.
+     * In the case of touch scrolling the nested scroll will be terminated automatically in
+     * the same manner as {@link ViewParent#requestDisallowInterceptTouchEvent(boolean)}.
+     * In the event of programmatic scrolling the caller must explicitly call
+     * {@link #stopNestedScroll()} to indicate the end of the nested scroll.</p>
+     *
+     * <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found.
+     * If it returns false the caller may ignore the rest of this contract until the next scroll.
+     * Calling startNestedScroll while a nested scroll is already in progress will return true.</p>
+     *
+     * <p>At each incremental step of the scroll the caller should invoke
+     * {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll}
+     * once it has calculated the requested scrolling delta. If it returns true the nested scrolling
+     * parent at least partially consumed the scroll and the caller should adjust the amount it
+     * scrolls by.</p>
+     *
+     * <p>After applying the remainder of the scroll delta the caller should invoke
+     * {@link #dispatchNestedScroll(int, int, int, int, int[]) dispatchNestedScroll}, passing
+     * both the delta consumed and the delta unconsumed. A nested scrolling parent may treat
+     * these values differently. See
+     * {@link NestedScrollingParent#onNestedScroll(View, int, int, int, int)}.
+     * </p>
+     *
+     * @param axes Flags consisting of a combination of {@link ViewCompat#SCROLL_AXIS_HORIZONTAL}
+     *             and/or {@link ViewCompat#SCROLL_AXIS_VERTICAL}.
+     * @return true if a cooperative parent was found and nested scrolling has been enabled for
+     *         the current gesture.
+     *
+     * @see #stopNestedScroll()
+     * @see #dispatchNestedPreScroll(int, int, int[], int[])
+     * @see #dispatchNestedScroll(int, int, int, int, int[])
+     */
+    boolean startNestedScroll(int axes);
+
+    /**
+     * Stop a nested scroll in progress.
+     *
+     * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
+     *
+     * @see #startNestedScroll(int)
+     */
+    void stopNestedScroll();
+
+    /**
+     * Returns true if this view has a nested scrolling parent.
+     *
+     * <p>The presence of a nested scrolling parent indicates that this view has initiated
+     * a nested scroll and it was accepted by an ancestor view further up the view hierarchy.</p>
+     *
+     * @return whether this view has a nested scrolling parent
+     */
+    boolean hasNestedScrollingParent();
+
+    /**
+     * Dispatch one step of a nested scroll in progress.
+     *
+     * <p>Implementations of views that support nested scrolling should call this to report
+     * info about a scroll in progress to the current nested scrolling parent. If a nested scroll
+     * is not currently in progress or nested scrolling is not
+     * {@link #isNestedScrollingEnabled() enabled} for this view this method does nothing.</p>
+     *
+     * <p>Compatible View implementations should also call
+     * {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll} before
+     * consuming a component of the scroll event themselves.</p>
+     *
+     * @param dxConsumed Horizontal distance in pixels consumed by this view during this scroll step
+     * @param dyConsumed Vertical distance in pixels consumed by this view during this scroll step
+     * @param dxUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param dyUnconsumed Horizontal scroll distance in pixels not consumed by this view
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @return true if the event was dispatched, false if it could not be dispatched.
+     * @see #dispatchNestedPreScroll(int, int, int[], int[])
+     */
+    boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
+            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
+
+    /**
+     * Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
+     *
+     * <p>Nested pre-scroll events are to nested scroll events what touch intercept is to touch.
+     * <code>dispatchNestedPreScroll</code> offers an opportunity for the parent view in a nested
+     * scrolling operation to consume some or all of the scroll operation before the child view
+     * consumes it.</p>
+     *
+     * @param dx Horizontal scroll distance in pixels
+     * @param dy Vertical scroll distance in pixels
+     * @param consumed Output. If not null, consumed[0] will contain the consumed component of dx
+     *                 and consumed[1] the consumed dy.
+     * @param offsetInWindow Optional. If not null, on return this will contain the offset
+     *                       in local view coordinates of this view from before this operation
+     *                       to after it completes. View implementations may use this to adjust
+     *                       expected input coordinate tracking.
+     * @return true if the parent consumed some or all of the scroll delta
+     * @see #dispatchNestedScroll(int, int, int, int, int[])
+     */
+    boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
+
+    /**
+     * Dispatch a fling to a nested scrolling parent.
+     *
+     * <p>This method should be used to indicate that a nested scrolling child has detected
+     * suitable conditions for a fling. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling child view would normally fling but it is at the edge of
+     * its own content, it can use this method to delegate the fling to its nested scrolling
+     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
+     *
+     * @param velocityX Horizontal fling velocity in pixels per second
+     * @param velocityY Vertical fling velocity in pixels per second
+     * @param consumed true if the child consumed the fling, false otherwise
+     * @return true if the nested scrolling parent consumed or otherwise reacted to the fling
+     */
+    boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
+
+    /**
+     * Dispatch a fling to a nested scrolling parent before it is processed by this view.
+     *
+     * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
+     * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
+     * offsets an opportunity for the parent view in a nested fling to fully consume the fling
+     * before the child view consumes it. If this method returns <code>true</code>, a nested
+     * parent view consumed the fling and this view should not scroll as a result.</p>
+     *
+     * <p>For a better user experience, only one view in a nested scrolling chain should consume
+     * the fling at a time. If a parent view consumed the fling this method will return false.
+     * Custom view implementations should account for this in two ways:</p>
+     *
+     * <ul>
+     *     <li>If a custom view is paged and needs to settle to a fixed page-point, do not
+     *     call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
+     *     position regardless.</li>
+     *     <li>If a nested parent does consume the fling, this view should not scroll at all,
+     *     even to settle back to a valid idle position.</li>
+     * </ul>
+     *
+     * <p>Views should also not offer fling velocities to nested parent views along an axis
+     * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
+     * should not offer a horizontal fling velocity to its parents since scrolling along that
+     * axis is not permitted and carrying velocity along that motion does not make sense.</p>
+     *
+     * @param velocityX Horizontal fling velocity in pixels per second
+     * @param velocityY Vertical fling velocity in pixels per second
+     * @return true if a nested scrolling parent consumed the fling
+     */
+    boolean dispatchNestedPreFling(float velocityX, float velocityY);
+}
diff --git a/core/java/com/android/internal/widget/OpReorderer.java b/core/java/com/android/internal/widget/OpReorderer.java
new file mode 100644
index 0000000..babb087
--- /dev/null
+++ b/core/java/com/android/internal/widget/OpReorderer.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static com.android.internal.widget.AdapterHelper.UpdateOp.ADD;
+import static com.android.internal.widget.AdapterHelper.UpdateOp.MOVE;
+import static com.android.internal.widget.AdapterHelper.UpdateOp.REMOVE;
+import static com.android.internal.widget.AdapterHelper.UpdateOp.UPDATE;
+
+import com.android.internal.widget.AdapterHelper.UpdateOp;
+
+import java.util.List;
+
+class OpReorderer {
+
+    final Callback mCallback;
+
+    OpReorderer(Callback callback) {
+        mCallback = callback;
+    }
+
+    void reorderOps(List<UpdateOp> ops) {
+        // since move operations breaks continuity, their effects on ADD/RM are hard to handle.
+        // we push them to the end of the list so that they can be handled easily.
+        int badMove;
+        while ((badMove = getLastMoveOutOfOrder(ops)) != -1) {
+            swapMoveOp(ops, badMove, badMove + 1);
+        }
+    }
+
+    private void swapMoveOp(List<UpdateOp> list, int badMove, int next) {
+        final UpdateOp moveOp = list.get(badMove);
+        final UpdateOp nextOp = list.get(next);
+        switch (nextOp.cmd) {
+            case REMOVE:
+                swapMoveRemove(list, badMove, moveOp, next, nextOp);
+                break;
+            case ADD:
+                swapMoveAdd(list, badMove, moveOp, next, nextOp);
+                break;
+            case UPDATE:
+                swapMoveUpdate(list, badMove, moveOp, next, nextOp);
+                break;
+        }
+    }
+
+    void swapMoveRemove(List<UpdateOp> list, int movePos, UpdateOp moveOp,
+            int removePos, UpdateOp removeOp) {
+        UpdateOp extraRm = null;
+        // check if move is nulled out by remove
+        boolean revertedMove = false;
+        final boolean moveIsBackwards;
+
+        if (moveOp.positionStart < moveOp.itemCount) {
+            moveIsBackwards = false;
+            if (removeOp.positionStart == moveOp.positionStart
+                    && removeOp.itemCount == moveOp.itemCount - moveOp.positionStart) {
+                revertedMove = true;
+            }
+        } else {
+            moveIsBackwards = true;
+            if (removeOp.positionStart == moveOp.itemCount + 1
+                    && removeOp.itemCount == moveOp.positionStart - moveOp.itemCount) {
+                revertedMove = true;
+            }
+        }
+
+        // going in reverse, first revert the effect of add
+        if (moveOp.itemCount < removeOp.positionStart) {
+            removeOp.positionStart--;
+        } else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) {
+            // move is removed.
+            removeOp.itemCount--;
+            moveOp.cmd = REMOVE;
+            moveOp.itemCount = 1;
+            if (removeOp.itemCount == 0) {
+                list.remove(removePos);
+                mCallback.recycleUpdateOp(removeOp);
+            }
+            // no need to swap, it is already a remove
+            return;
+        }
+
+        // now affect of add is consumed. now apply effect of first remove
+        if (moveOp.positionStart <= removeOp.positionStart) {
+            removeOp.positionStart++;
+        } else if (moveOp.positionStart < removeOp.positionStart + removeOp.itemCount) {
+            final int remaining = removeOp.positionStart + removeOp.itemCount
+                    - moveOp.positionStart;
+            extraRm = mCallback.obtainUpdateOp(REMOVE, moveOp.positionStart + 1, remaining, null);
+            removeOp.itemCount = moveOp.positionStart - removeOp.positionStart;
+        }
+
+        // if effects of move is reverted by remove, we are done.
+        if (revertedMove) {
+            list.set(movePos, removeOp);
+            list.remove(removePos);
+            mCallback.recycleUpdateOp(moveOp);
+            return;
+        }
+
+        // now find out the new locations for move actions
+        if (moveIsBackwards) {
+            if (extraRm != null) {
+                if (moveOp.positionStart > extraRm.positionStart) {
+                    moveOp.positionStart -= extraRm.itemCount;
+                }
+                if (moveOp.itemCount > extraRm.positionStart) {
+                    moveOp.itemCount -= extraRm.itemCount;
+                }
+            }
+            if (moveOp.positionStart > removeOp.positionStart) {
+                moveOp.positionStart -= removeOp.itemCount;
+            }
+            if (moveOp.itemCount > removeOp.positionStart) {
+                moveOp.itemCount -= removeOp.itemCount;
+            }
+        } else {
+            if (extraRm != null) {
+                if (moveOp.positionStart >= extraRm.positionStart) {
+                    moveOp.positionStart -= extraRm.itemCount;
+                }
+                if (moveOp.itemCount >= extraRm.positionStart) {
+                    moveOp.itemCount -= extraRm.itemCount;
+                }
+            }
+            if (moveOp.positionStart >= removeOp.positionStart) {
+                moveOp.positionStart -= removeOp.itemCount;
+            }
+            if (moveOp.itemCount >= removeOp.positionStart) {
+                moveOp.itemCount -= removeOp.itemCount;
+            }
+        }
+
+        list.set(movePos, removeOp);
+        if (moveOp.positionStart != moveOp.itemCount) {
+            list.set(removePos, moveOp);
+        } else {
+            list.remove(removePos);
+        }
+        if (extraRm != null) {
+            list.add(movePos, extraRm);
+        }
+    }
+
+    private void swapMoveAdd(List<UpdateOp> list, int move, UpdateOp moveOp, int add,
+            UpdateOp addOp) {
+        int offset = 0;
+        // going in reverse, first revert the effect of add
+        if (moveOp.itemCount < addOp.positionStart) {
+            offset--;
+        }
+        if (moveOp.positionStart < addOp.positionStart) {
+            offset++;
+        }
+        if (addOp.positionStart <= moveOp.positionStart) {
+            moveOp.positionStart += addOp.itemCount;
+        }
+        if (addOp.positionStart <= moveOp.itemCount) {
+            moveOp.itemCount += addOp.itemCount;
+        }
+        addOp.positionStart += offset;
+        list.set(move, addOp);
+        list.set(add, moveOp);
+    }
+
+    void swapMoveUpdate(List<UpdateOp> list, int move, UpdateOp moveOp, int update,
+            UpdateOp updateOp) {
+        UpdateOp extraUp1 = null;
+        UpdateOp extraUp2 = null;
+        // going in reverse, first revert the effect of add
+        if (moveOp.itemCount < updateOp.positionStart) {
+            updateOp.positionStart--;
+        } else if (moveOp.itemCount < updateOp.positionStart + updateOp.itemCount) {
+            // moved item is updated. add an update for it
+            updateOp.itemCount--;
+            extraUp1 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart, 1, updateOp.payload);
+        }
+        // now affect of add is consumed. now apply effect of first remove
+        if (moveOp.positionStart <= updateOp.positionStart) {
+            updateOp.positionStart++;
+        } else if (moveOp.positionStart < updateOp.positionStart + updateOp.itemCount) {
+            final int remaining = updateOp.positionStart + updateOp.itemCount
+                    - moveOp.positionStart;
+            extraUp2 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart + 1, remaining,
+                    updateOp.payload);
+            updateOp.itemCount -= remaining;
+        }
+        list.set(update, moveOp);
+        if (updateOp.itemCount > 0) {
+            list.set(move, updateOp);
+        } else {
+            list.remove(move);
+            mCallback.recycleUpdateOp(updateOp);
+        }
+        if (extraUp1 != null) {
+            list.add(move, extraUp1);
+        }
+        if (extraUp2 != null) {
+            list.add(move, extraUp2);
+        }
+    }
+
+    private int getLastMoveOutOfOrder(List<UpdateOp> list) {
+        boolean foundNonMove = false;
+        for (int i = list.size() - 1; i >= 0; i--) {
+            final UpdateOp op1 = list.get(i);
+            if (op1.cmd == MOVE) {
+                if (foundNonMove) {
+                    return i;
+                }
+            } else {
+                foundNonMove = true;
+            }
+        }
+        return -1;
+    }
+
+    interface Callback {
+
+        UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount, Object payload);
+
+        void recycleUpdateOp(UpdateOp op);
+    }
+}
diff --git a/core/java/com/android/internal/widget/OrientationHelper.java b/core/java/com/android/internal/widget/OrientationHelper.java
new file mode 100644
index 0000000..1b02c88
--- /dev/null
+++ b/core/java/com/android/internal/widget/OrientationHelper.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * Helper class for LayoutManagers to abstract measurements depending on the View's orientation.
+ * <p>
+ * It is developed to easily support vertical and horizontal orientations in a LayoutManager but
+ * can also be used to abstract calls around view bounds and child measurements with margins and
+ * decorations.
+ *
+ * @see #createHorizontalHelper(RecyclerView.LayoutManager)
+ * @see #createVerticalHelper(RecyclerView.LayoutManager)
+ */
+public abstract class OrientationHelper {
+
+    private static final int INVALID_SIZE = Integer.MIN_VALUE;
+
+    protected final RecyclerView.LayoutManager mLayoutManager;
+
+    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
+
+    public static final int VERTICAL = LinearLayout.VERTICAL;
+
+    private int mLastTotalSpace = INVALID_SIZE;
+
+    final Rect mTmpRect = new Rect();
+
+    private OrientationHelper(RecyclerView.LayoutManager layoutManager) {
+        mLayoutManager = layoutManager;
+    }
+
+    /**
+     * Call this method after onLayout method is complete if state is NOT pre-layout.
+     * This method records information like layout bounds that might be useful in the next layout
+     * calculations.
+     */
+    public void onLayoutComplete() {
+        mLastTotalSpace = getTotalSpace();
+    }
+
+    /**
+     * Returns the layout space change between the previous layout pass and current layout pass.
+     * <p>
+     * Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's
+     * {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler,
+     * RecyclerView.State)} method.
+     *
+     * @return The difference between the current total space and previous layout's total space.
+     * @see #onLayoutComplete()
+     */
+    public int getTotalSpaceChange() {
+        return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace;
+    }
+
+    /**
+     * Returns the start of the view including its decoration and margin.
+     * <p>
+     * For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left
+     * decoration and 3px left margin, returned value will be 15px.
+     *
+     * @param view The view element to check
+     * @return The first pixel of the element
+     * @see #getDecoratedEnd(android.view.View)
+     */
+    public abstract int getDecoratedStart(View view);
+
+    /**
+     * Returns the end of the view including its decoration and margin.
+     * <p>
+     * For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right
+     * decoration and 3px right margin, returned value will be 205.
+     *
+     * @param view The view element to check
+     * @return The last pixel of the element
+     * @see #getDecoratedStart(android.view.View)
+     */
+    public abstract int getDecoratedEnd(View view);
+
+    /**
+     * Returns the end of the View after its matrix transformations are applied to its layout
+     * position.
+     * <p>
+     * This method is useful when trying to detect the visible edge of a View.
+     * <p>
+     * It includes the decorations but does not include the margins.
+     *
+     * @param view The view whose transformed end will be returned
+     * @return The end of the View after its decor insets and transformation matrix is applied to
+     * its position
+     *
+     * @see RecyclerView.LayoutManager#getTransformedBoundingBox(View, boolean, Rect)
+     */
+    public abstract int getTransformedEndWithDecoration(View view);
+
+    /**
+     * Returns the start of the View after its matrix transformations are applied to its layout
+     * position.
+     * <p>
+     * This method is useful when trying to detect the visible edge of a View.
+     * <p>
+     * It includes the decorations but does not include the margins.
+     *
+     * @param view The view whose transformed start will be returned
+     * @return The start of the View after its decor insets and transformation matrix is applied to
+     * its position
+     *
+     * @see RecyclerView.LayoutManager#getTransformedBoundingBox(View, boolean, Rect)
+     */
+    public abstract int getTransformedStartWithDecoration(View view);
+
+    /**
+     * Returns the space occupied by this View in the current orientation including decorations and
+     * margins.
+     *
+     * @param view The view element to check
+     * @return Total space occupied by this view
+     * @see #getDecoratedMeasurementInOther(View)
+     */
+    public abstract int getDecoratedMeasurement(View view);
+
+    /**
+     * Returns the space occupied by this View in the perpendicular orientation including
+     * decorations and margins.
+     *
+     * @param view The view element to check
+     * @return Total space occupied by this view in the perpendicular orientation to current one
+     * @see #getDecoratedMeasurement(View)
+     */
+    public abstract int getDecoratedMeasurementInOther(View view);
+
+    /**
+     * Returns the start position of the layout after the start padding is added.
+     *
+     * @return The very first pixel we can draw.
+     */
+    public abstract int getStartAfterPadding();
+
+    /**
+     * Returns the end position of the layout after the end padding is removed.
+     *
+     * @return The end boundary for this layout.
+     */
+    public abstract int getEndAfterPadding();
+
+    /**
+     * Returns the end position of the layout without taking padding into account.
+     *
+     * @return The end boundary for this layout without considering padding.
+     */
+    public abstract int getEnd();
+
+    /**
+     * Offsets all children's positions by the given amount.
+     *
+     * @param amount Value to add to each child's layout parameters
+     */
+    public abstract void offsetChildren(int amount);
+
+    /**
+     * Returns the total space to layout. This number is the difference between
+     * {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
+     *
+     * @return Total space to layout children
+     */
+    public abstract int getTotalSpace();
+
+    /**
+     * Offsets the child in this orientation.
+     *
+     * @param view   View to offset
+     * @param offset offset amount
+     */
+    public abstract void offsetChild(View view, int offset);
+
+    /**
+     * Returns the padding at the end of the layout. For horizontal helper, this is the right
+     * padding and for vertical helper, this is the bottom padding. This method does not check
+     * whether the layout is RTL or not.
+     *
+     * @return The padding at the end of the layout.
+     */
+    public abstract int getEndPadding();
+
+    /**
+     * Returns the MeasureSpec mode for the current orientation from the LayoutManager.
+     *
+     * @return The current measure spec mode.
+     *
+     * @see View.MeasureSpec
+     * @see RecyclerView.LayoutManager#getWidthMode()
+     * @see RecyclerView.LayoutManager#getHeightMode()
+     */
+    public abstract int getMode();
+
+    /**
+     * Returns the MeasureSpec mode for the perpendicular orientation from the LayoutManager.
+     *
+     * @return The current measure spec mode.
+     *
+     * @see View.MeasureSpec
+     * @see RecyclerView.LayoutManager#getWidthMode()
+     * @see RecyclerView.LayoutManager#getHeightMode()
+     */
+    public abstract int getModeInOther();
+
+    /**
+     * Creates an OrientationHelper for the given LayoutManager and orientation.
+     *
+     * @param layoutManager LayoutManager to attach to
+     * @param orientation   Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
+     * @return A new OrientationHelper
+     */
+    public static OrientationHelper createOrientationHelper(
+            RecyclerView.LayoutManager layoutManager, int orientation) {
+        switch (orientation) {
+            case HORIZONTAL:
+                return createHorizontalHelper(layoutManager);
+            case VERTICAL:
+                return createVerticalHelper(layoutManager);
+        }
+        throw new IllegalArgumentException("invalid orientation");
+    }
+
+    /**
+     * Creates a horizontal OrientationHelper for the given LayoutManager.
+     *
+     * @param layoutManager The LayoutManager to attach to.
+     * @return A new OrientationHelper
+     */
+    public static OrientationHelper createHorizontalHelper(
+            RecyclerView.LayoutManager layoutManager) {
+        return new OrientationHelper(layoutManager) {
+            @Override
+            public int getEndAfterPadding() {
+                return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();
+            }
+
+            @Override
+            public int getEnd() {
+                return mLayoutManager.getWidth();
+            }
+
+            @Override
+            public void offsetChildren(int amount) {
+                mLayoutManager.offsetChildrenHorizontal(amount);
+            }
+
+            @Override
+            public int getStartAfterPadding() {
+                return mLayoutManager.getPaddingLeft();
+            }
+
+            @Override
+            public int getDecoratedMeasurement(View view) {
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                        view.getLayoutParams();
+                return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+                        + params.rightMargin;
+            }
+
+            @Override
+            public int getDecoratedMeasurementInOther(View view) {
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                        view.getLayoutParams();
+                return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+                        + params.bottomMargin;
+            }
+
+            @Override
+            public int getDecoratedEnd(View view) {
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                        view.getLayoutParams();
+                return mLayoutManager.getDecoratedRight(view) + params.rightMargin;
+            }
+
+            @Override
+            public int getDecoratedStart(View view) {
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                        view.getLayoutParams();
+                return mLayoutManager.getDecoratedLeft(view) - params.leftMargin;
+            }
+
+            @Override
+            public int getTransformedEndWithDecoration(View view) {
+                mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
+                return mTmpRect.right;
+            }
+
+            @Override
+            public int getTransformedStartWithDecoration(View view) {
+                mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
+                return mTmpRect.left;
+            }
+
+            @Override
+            public int getTotalSpace() {
+                return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
+                        - mLayoutManager.getPaddingRight();
+            }
+
+            @Override
+            public void offsetChild(View view, int offset) {
+                view.offsetLeftAndRight(offset);
+            }
+
+            @Override
+            public int getEndPadding() {
+                return mLayoutManager.getPaddingRight();
+            }
+
+            @Override
+            public int getMode() {
+                return mLayoutManager.getWidthMode();
+            }
+
+            @Override
+            public int getModeInOther() {
+                return mLayoutManager.getHeightMode();
+            }
+        };
+    }
+
+    /**
+     * Creates a vertical OrientationHelper for the given LayoutManager.
+     *
+     * @param layoutManager The LayoutManager to attach to.
+     * @return A new OrientationHelper
+     */
+    public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
+        return new OrientationHelper(layoutManager) {
+            @Override
+            public int getEndAfterPadding() {
+                return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom();
+            }
+
+            @Override
+            public int getEnd() {
+                return mLayoutManager.getHeight();
+            }
+
+            @Override
+            public void offsetChildren(int amount) {
+                mLayoutManager.offsetChildrenVertical(amount);
+            }
+
+            @Override
+            public int getStartAfterPadding() {
+                return mLayoutManager.getPaddingTop();
+            }
+
+            @Override
+            public int getDecoratedMeasurement(View view) {
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                        view.getLayoutParams();
+                return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+                        + params.bottomMargin;
+            }
+
+            @Override
+            public int getDecoratedMeasurementInOther(View view) {
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                        view.getLayoutParams();
+                return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+                        + params.rightMargin;
+            }
+
+            @Override
+            public int getDecoratedEnd(View view) {
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                        view.getLayoutParams();
+                return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin;
+            }
+
+            @Override
+            public int getDecoratedStart(View view) {
+                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
+                        view.getLayoutParams();
+                return mLayoutManager.getDecoratedTop(view) - params.topMargin;
+            }
+
+            @Override
+            public int getTransformedEndWithDecoration(View view) {
+                mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
+                return mTmpRect.bottom;
+            }
+
+            @Override
+            public int getTransformedStartWithDecoration(View view) {
+                mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
+                return mTmpRect.top;
+            }
+
+            @Override
+            public int getTotalSpace() {
+                return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
+                        - mLayoutManager.getPaddingBottom();
+            }
+
+            @Override
+            public void offsetChild(View view, int offset) {
+                view.offsetTopAndBottom(offset);
+            }
+
+            @Override
+            public int getEndPadding() {
+                return mLayoutManager.getPaddingBottom();
+            }
+
+            @Override
+            public int getMode() {
+                return mLayoutManager.getHeightMode();
+            }
+
+            @Override
+            public int getModeInOther() {
+                return mLayoutManager.getWidthMode();
+            }
+        };
+    }
+}
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
new file mode 100644
index 0000000..0cf3164
--- /dev/null
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -0,0 +1,12255 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.Observable;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.AbsSavedState;
+import android.view.Display;
+import android.view.FocusFinder;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.Interpolator;
+import android.widget.EdgeEffect;
+import android.widget.OverScroller;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A flexible view for providing a limited window into a large data set.
+ *
+ * <h3>Glossary of terms:</h3>
+ *
+ * <ul>
+ *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
+ *     that represent items in a data set.</li>
+ *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
+ *     <li><em>Index:</em> The index of an attached child view as used in a call to
+ *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
+ *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
+ *     to a <em>position</em> within the adapter.</li>
+ *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
+ *     position may be placed in a cache for later reuse to display the same type of data again
+ *     later. This can drastically improve performance by skipping initial layout inflation
+ *     or construction.</li>
+ *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
+ *     state during layout. Scrap views may be reused without becoming fully detached
+ *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
+ *     by the adapter if the view was considered <em>dirty</em>.</li>
+ *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
+ *     being displayed.</li>
+ * </ul>
+ *
+ * <h4>Positions in RecyclerView:</h4>
+ * <p>
+ * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
+ * {@link LayoutManager} to be able to detect data set changes in batches during a layout
+ * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
+ * It also helps with performance because all view bindings happen at the same time and unnecessary
+ * bindings are avoided.
+ * <p>
+ * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
+ * <ul>
+ *     <li>layout position: Position of an item in the latest layout calculation. This is the
+ *     position from the LayoutManager's perspective.</li>
+ *     <li>adapter position: Position of an item in the adapter. This is the position from
+ *     the Adapter's perspective.</li>
+ * </ul>
+ * <p>
+ * These two positions are the same except the time between dispatching <code>adapter.notify*
+ * </code> events and calculating the updated layout.
+ * <p>
+ * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
+ * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
+ * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
+ * last layout calculation. You can rely on these positions to be consistent with what user is
+ * currently seeing on the screen. For example, if you have a list of items on the screen and user
+ * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
+ * is seeing.
+ * <p>
+ * The other set of position related methods are in the form of
+ * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
+ * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
+ * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
+ * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
+ * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
+ * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
+ * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
+ * <code>null</code> results from these methods.
+ * <p>
+ * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
+ * writing an {@link Adapter}, you probably want to use adapter positions.
+ *
+ * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
+ */
+public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
+
+    static final String TAG = "RecyclerView";
+
+    static final boolean DEBUG = false;
+
+    private static final int[]  NESTED_SCROLLING_ATTRS = { android.R.attr.nestedScrollingEnabled };
+
+    private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
+
+    /**
+     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
+     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
+     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
+     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
+     * this criteria.
+     */
+    static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
+            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
+    /**
+     * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
+     * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
+     * 0 when mode is unspecified.
+     */
+    static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
+
+    static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
+
+    /**
+     * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
+     * RenderThread but before the next frame begins. We schedule prefetch work in this window.
+     */
+    private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
+
+    /**
+     * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
+     * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
+     */
+    private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
+
+    /**
+     * on API 15-, a focused child can still be considered a focused child of RV even after
+     * it's being removed or its focusable flag is set to false. This is because when this focused
+     * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
+     * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
+     * to request focus on a new child, which will clear the focus on the old (detached) child as a
+     * side-effect.
+     */
+    private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
+
+    static final boolean DISPATCH_TEMP_DETACH = false;
+    public static final int HORIZONTAL = 0;
+    public static final int VERTICAL = 1;
+
+    public static final int NO_POSITION = -1;
+    public static final long NO_ID = -1;
+    public static final int INVALID_TYPE = -1;
+
+    /**
+     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
+     * that the RecyclerView should use the standard touch slop for smooth,
+     * continuous scrolling.
+     */
+    public static final int TOUCH_SLOP_DEFAULT = 0;
+
+    /**
+     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
+     * that the RecyclerView should use the standard touch slop for scrolling
+     * widgets that snap to a page or other coarse-grained barrier.
+     */
+    public static final int TOUCH_SLOP_PAGING = 1;
+
+    static final int MAX_SCROLL_DURATION = 2000;
+
+    /**
+     * RecyclerView is calculating a scroll.
+     * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
+     * it. Try to avoid using EditText, focusable views or handle them with care.
+     */
+    static final String TRACE_SCROLL_TAG = "RV Scroll";
+
+    /**
+     * OnLayout has been called by the View system.
+     * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
+     * update themselves directly. This will cause a full re-layout but when it happens via the
+     * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
+     */
+    private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
+
+    /**
+     * NotifyDataSetChanged or equal has been called.
+     * If this is taking a long time, try sending granular notify adapter changes instead of just
+     * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
+     * might help.
+     */
+    private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
+
+    /**
+     * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
+     * If this is taking a long time, you may have dispatched too many Adapter updates causing too
+     * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
+     * methods.
+     */
+    private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
+
+    /**
+     * RecyclerView is rebinding a View.
+     * If this is taking a lot of time, consider optimizing your layout or make sure you are not
+     * doing extra operations in onBindViewHolder call.
+     */
+    static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
+
+    /**
+     * RecyclerView is attempting to pre-populate off screen views.
+     */
+    static final String TRACE_PREFETCH_TAG = "RV Prefetch";
+
+    /**
+     * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
+     * RecyclerView.
+     */
+    static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
+
+    /**
+     * RecyclerView is creating a new View.
+     * If too many of these present in Systrace:
+     * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
+     * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
+     * > Adapter#onFailedToRecycleView(ViewHolder)})
+     *
+     * - There might be too many item view types.
+     * > Try merging them
+     *
+     * - There might be too many itemChange animations and not enough space in RecyclerPool.
+     * >Try increasing your pool size and item cache size.
+     */
+    static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
+    private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
+            new Class[]{Context.class, AttributeSet.class, int.class, int.class};
+
+    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
+
+    final Recycler mRecycler = new Recycler();
+
+    private SavedState mPendingSavedState;
+
+    /**
+     * Handles adapter updates
+     */
+    AdapterHelper mAdapterHelper;
+
+    /**
+     * Handles abstraction between LayoutManager children and RecyclerView children
+     */
+    ChildHelper mChildHelper;
+
+    /**
+     * Keeps data about views to be used for animations
+     */
+    final ViewInfoStore mViewInfoStore = new ViewInfoStore();
+
+    /**
+     * Prior to L, there is no way to query this variable which is why we override the setter and
+     * track it here.
+     */
+    boolean mClipToPadding;
+
+    /**
+     * Note: this Runnable is only ever posted if:
+     * 1) We've been through first layout
+     * 2) We know we have a fixed size (mHasFixedSize)
+     * 3) We're attached
+     */
+    final Runnable mUpdateChildViewsRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (!mFirstLayoutComplete || isLayoutRequested()) {
+                // a layout request will happen, we should not do layout here.
+                return;
+            }
+            if (!mIsAttached) {
+                requestLayout();
+                // if we are not attached yet, mark us as requiring layout and skip
+                return;
+            }
+            if (mLayoutFrozen) {
+                mLayoutRequestEaten = true;
+                return; //we'll process updates when ice age ends.
+            }
+            consumePendingUpdateOperations();
+        }
+    };
+
+    final Rect mTempRect = new Rect();
+    private final Rect mTempRect2 = new Rect();
+    final RectF mTempRectF = new RectF();
+    Adapter mAdapter;
+    @VisibleForTesting LayoutManager mLayout;
+    RecyclerListener mRecyclerListener;
+    final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
+    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
+            new ArrayList<>();
+    private OnItemTouchListener mActiveOnItemTouchListener;
+    boolean mIsAttached;
+    boolean mHasFixedSize;
+    @VisibleForTesting boolean mFirstLayoutComplete;
+
+    // Counting lock to control whether we should ignore requestLayout calls from children or not.
+    private int mEatRequestLayout = 0;
+
+    boolean mLayoutRequestEaten;
+    boolean mLayoutFrozen;
+    private boolean mIgnoreMotionEventTillDown;
+
+    // binary OR of change events that were eaten during a layout or scroll.
+    private int mEatenAccessibilityChangeFlags;
+    boolean mAdapterUpdateDuringMeasure;
+
+    private final AccessibilityManager mAccessibilityManager;
+    private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
+
+    /**
+     * Set to true when an adapter data set changed notification is received.
+     * In that case, we cannot run any animations since we don't know what happened until layout.
+     *
+     * Attached items are invalid until next layout, at which point layout will animate/replace
+     * items as necessary, building up content from the (effectively) new adapter from scratch.
+     *
+     * Cached items must be discarded when setting this to true, so that the cache may be freely
+     * used by prefetching until the next layout occurs.
+     *
+     * @see #setDataSetChangedAfterLayout()
+     */
+    boolean mDataSetHasChangedAfterLayout = false;
+
+    /**
+     * This variable is incremented during a dispatchLayout and/or scroll.
+     * Some methods should not be called during these periods (e.g. adapter data change).
+     * Doing so will create hard to find bugs so we better check it and throw an exception.
+     *
+     * @see #assertInLayoutOrScroll(String)
+     * @see #assertNotInLayoutOrScroll(String)
+     */
+    private int mLayoutOrScrollCounter = 0;
+
+    /**
+     * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
+     * (for API compatibility).
+     * <p>
+     * It is a bad practice for a developer to update the data in a scroll callback since it is
+     * potentially called during a layout.
+     */
+    private int mDispatchScrollCounter = 0;
+
+    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
+
+    ItemAnimator mItemAnimator = new DefaultItemAnimator();
+
+    private static final int INVALID_POINTER = -1;
+
+    /**
+     * The RecyclerView is not currently scrolling.
+     * @see #getScrollState()
+     */
+    public static final int SCROLL_STATE_IDLE = 0;
+
+    /**
+     * The RecyclerView is currently being dragged by outside input such as user touch input.
+     * @see #getScrollState()
+     */
+    public static final int SCROLL_STATE_DRAGGING = 1;
+
+    /**
+     * The RecyclerView is currently animating to a final position while not under
+     * outside control.
+     * @see #getScrollState()
+     */
+    public static final int SCROLL_STATE_SETTLING = 2;
+
+    static final long FOREVER_NS = Long.MAX_VALUE;
+
+    // Touch/scrolling handling
+
+    private int mScrollState = SCROLL_STATE_IDLE;
+    private int mScrollPointerId = INVALID_POINTER;
+    private VelocityTracker mVelocityTracker;
+    private int mInitialTouchX;
+    private int mInitialTouchY;
+    private int mLastTouchX;
+    private int mLastTouchY;
+    private int mTouchSlop;
+    private OnFlingListener mOnFlingListener;
+    private final int mMinFlingVelocity;
+    private final int mMaxFlingVelocity;
+    // This value is used when handling generic motion events.
+    private float mScrollFactor = Float.MIN_VALUE;
+    private boolean mPreserveFocusAfterLayout = true;
+
+    final ViewFlinger mViewFlinger = new ViewFlinger();
+
+    GapWorker mGapWorker;
+    GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
+            ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
+
+    final State mState = new State();
+
+    private OnScrollListener mScrollListener;
+    private List<OnScrollListener> mScrollListeners;
+
+    // For use in item animations
+    boolean mItemsAddedOrRemoved = false;
+    boolean mItemsChanged = false;
+    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
+            new ItemAnimatorRestoreListener();
+    boolean mPostedAnimatorRunner = false;
+    RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
+    private ChildDrawingOrderCallback mChildDrawingOrderCallback;
+
+    // simple array to keep min and max child position during a layout calculation
+    // preserved not to create a new one in each layout pass
+    private final int[] mMinMaxLayoutPositions = new int[2];
+
+    private final int[] mScrollOffset = new int[2];
+    private final int[] mScrollConsumed = new int[2];
+    private final int[] mNestedOffsets = new int[2];
+
+    /**
+     * These are views that had their a11y importance changed during a layout. We defer these events
+     * until the end of the layout because a11y service may make sync calls back to the RV while
+     * the View's state is undefined.
+     */
+    @VisibleForTesting
+    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList();
+
+    private Runnable mItemAnimatorRunner = new Runnable() {
+        @Override
+        public void run() {
+            if (mItemAnimator != null) {
+                mItemAnimator.runPendingAnimations();
+            }
+            mPostedAnimatorRunner = false;
+        }
+    };
+
+    static final Interpolator sQuinticInterpolator = new Interpolator() {
+        @Override
+        public float getInterpolation(float t) {
+            t -= 1.0f;
+            return t * t * t * t * t + 1.0f;
+        }
+    };
+
+    /**
+     * The callback to convert view info diffs into animations.
+     */
+    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
+            new ViewInfoStore.ProcessCallback() {
+        @Override
+        public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
+                @Nullable ItemHolderInfo postInfo) {
+            mRecycler.unscrapView(viewHolder);
+            animateDisappearance(viewHolder, info, postInfo);
+        }
+        @Override
+        public void processAppeared(ViewHolder viewHolder,
+                ItemHolderInfo preInfo, ItemHolderInfo info) {
+            animateAppearance(viewHolder, preInfo, info);
+        }
+
+        @Override
+        public void processPersistent(ViewHolder viewHolder,
+                @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
+            viewHolder.setIsRecyclable(false);
+            if (mDataSetHasChangedAfterLayout) {
+                // since it was rebound, use change instead as we'll be mapping them from
+                // stable ids. If stable ids were false, we would not be running any
+                // animations
+                if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
+                    postAnimationRunner();
+                }
+            } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
+                postAnimationRunner();
+            }
+        }
+        @Override
+        public void unused(ViewHolder viewHolder) {
+            mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
+        }
+    };
+
+    public RecyclerView(Context context) {
+        this(context, null);
+    }
+
+    public RecyclerView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        if (attrs != null) {
+            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
+            mClipToPadding = a.getBoolean(0, true);
+            a.recycle();
+        } else {
+            mClipToPadding = true;
+        }
+        setScrollContainer(true);
+        setFocusableInTouchMode(true);
+
+        final ViewConfiguration vc = ViewConfiguration.get(context);
+        mTouchSlop = vc.getScaledTouchSlop();
+        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
+        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
+        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
+
+        mItemAnimator.setListener(mItemAnimatorListener);
+        initAdapterManager();
+        initChildrenHelper();
+        // If not explicitly specified this view is important for accessibility.
+        if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
+        mAccessibilityManager = (AccessibilityManager) getContext()
+                .getSystemService(Context.ACCESSIBILITY_SERVICE);
+        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
+        // Create the layoutManager if specified.
+
+        boolean nestedScrollingEnabled = true;
+
+        if (attrs != null) {
+            int defStyleRes = 0;
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
+                    defStyle, defStyleRes);
+            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
+            int descendantFocusability = a.getInt(
+                    R.styleable.RecyclerView_descendantFocusability, -1);
+            if (descendantFocusability == -1) {
+                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+            }
+            a.recycle();
+            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
+
+            if (Build.VERSION.SDK_INT >= 21) {
+                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
+                        defStyle, defStyleRes);
+                nestedScrollingEnabled = a.getBoolean(0, true);
+                a.recycle();
+            }
+        } else {
+            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+        }
+
+        // Re-set whether nested scrolling is enabled so that it is set on all API levels
+        setNestedScrollingEnabled(nestedScrollingEnabled);
+    }
+
+    /**
+     * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
+     * @return An instance of AccessibilityDelegateCompat used by RecyclerView
+     */
+    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
+        return mAccessibilityDelegate;
+    }
+
+    /**
+     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
+     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
+     */
+    public void setAccessibilityDelegateCompat(
+            RecyclerViewAccessibilityDelegate accessibilityDelegate) {
+        mAccessibilityDelegate = accessibilityDelegate;
+        setAccessibilityDelegate(mAccessibilityDelegate);
+    }
+
+    /**
+     * Instantiate and set a LayoutManager, if specified in the attributes.
+     */
+    private void createLayoutManager(Context context, String className, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        if (className != null) {
+            className = className.trim();
+            if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
+                className = getFullClassName(context, className);
+                try {
+                    ClassLoader classLoader;
+                    if (isInEditMode()) {
+                        // Stupid layoutlib cannot handle simple class loaders.
+                        classLoader = this.getClass().getClassLoader();
+                    } else {
+                        classLoader = context.getClassLoader();
+                    }
+                    Class<? extends LayoutManager> layoutManagerClass =
+                            classLoader.loadClass(className).asSubclass(LayoutManager.class);
+                    Constructor<? extends LayoutManager> constructor;
+                    Object[] constructorArgs = null;
+                    try {
+                        constructor = layoutManagerClass
+                                .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
+                        constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
+                    } catch (NoSuchMethodException e) {
+                        try {
+                            constructor = layoutManagerClass.getConstructor();
+                        } catch (NoSuchMethodException e1) {
+                            e1.initCause(e);
+                            throw new IllegalStateException(attrs.getPositionDescription()
+                                    + ": Error creating LayoutManager " + className, e1);
+                        }
+                    }
+                    constructor.setAccessible(true);
+                    setLayoutManager(constructor.newInstance(constructorArgs));
+                } catch (ClassNotFoundException e) {
+                    throw new IllegalStateException(attrs.getPositionDescription()
+                            + ": Unable to find LayoutManager " + className, e);
+                } catch (InvocationTargetException e) {
+                    throw new IllegalStateException(attrs.getPositionDescription()
+                            + ": Could not instantiate the LayoutManager: " + className, e);
+                } catch (InstantiationException e) {
+                    throw new IllegalStateException(attrs.getPositionDescription()
+                            + ": Could not instantiate the LayoutManager: " + className, e);
+                } catch (IllegalAccessException e) {
+                    throw new IllegalStateException(attrs.getPositionDescription()
+                            + ": Cannot access non-public constructor " + className, e);
+                } catch (ClassCastException e) {
+                    throw new IllegalStateException(attrs.getPositionDescription()
+                            + ": Class is not a LayoutManager " + className, e);
+                }
+            }
+        }
+    }
+
+    private String getFullClassName(Context context, String className) {
+        if (className.charAt(0) == '.') {
+            return context.getPackageName() + className;
+        }
+        if (className.contains(".")) {
+            return className;
+        }
+        return RecyclerView.class.getPackage().getName() + '.' + className;
+    }
+
+    private void initChildrenHelper() {
+        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
+            @Override
+            public int getChildCount() {
+                return RecyclerView.this.getChildCount();
+            }
+
+            @Override
+            public void addView(View child, int index) {
+                RecyclerView.this.addView(child, index);
+                dispatchChildAttached(child);
+            }
+
+            @Override
+            public int indexOfChild(View view) {
+                return RecyclerView.this.indexOfChild(view);
+            }
+
+            @Override
+            public void removeViewAt(int index) {
+                final View child = RecyclerView.this.getChildAt(index);
+                if (child != null) {
+                    dispatchChildDetached(child);
+                }
+                RecyclerView.this.removeViewAt(index);
+            }
+
+            @Override
+            public View getChildAt(int offset) {
+                return RecyclerView.this.getChildAt(offset);
+            }
+
+            @Override
+            public void removeAllViews() {
+                final int count = getChildCount();
+                for (int i = 0; i < count; i++) {
+                    dispatchChildDetached(getChildAt(i));
+                }
+                RecyclerView.this.removeAllViews();
+            }
+
+            @Override
+            public ViewHolder getChildViewHolder(View view) {
+                return getChildViewHolderInt(view);
+            }
+
+            @Override
+            public void attachViewToParent(View child, int index,
+                    ViewGroup.LayoutParams layoutParams) {
+                final ViewHolder vh = getChildViewHolderInt(child);
+                if (vh != null) {
+                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
+                        throw new IllegalArgumentException("Called attach on a child which is not"
+                                + " detached: " + vh);
+                    }
+                    if (DEBUG) {
+                        Log.d(TAG, "reAttach " + vh);
+                    }
+                    vh.clearTmpDetachFlag();
+                }
+                RecyclerView.this.attachViewToParent(child, index, layoutParams);
+            }
+
+            @Override
+            public void detachViewFromParent(int offset) {
+                final View view = getChildAt(offset);
+                if (view != null) {
+                    final ViewHolder vh = getChildViewHolderInt(view);
+                    if (vh != null) {
+                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
+                            throw new IllegalArgumentException("called detach on an already"
+                                    + " detached child " + vh);
+                        }
+                        if (DEBUG) {
+                            Log.d(TAG, "tmpDetach " + vh);
+                        }
+                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
+                    }
+                }
+                RecyclerView.this.detachViewFromParent(offset);
+            }
+
+            @Override
+            public void onEnteredHiddenState(View child) {
+                final ViewHolder vh = getChildViewHolderInt(child);
+                if (vh != null) {
+                    vh.onEnteredHiddenState(RecyclerView.this);
+                }
+            }
+
+            @Override
+            public void onLeftHiddenState(View child) {
+                final ViewHolder vh = getChildViewHolderInt(child);
+                if (vh != null) {
+                    vh.onLeftHiddenState(RecyclerView.this);
+                }
+            }
+        });
+    }
+
+    void initAdapterManager() {
+        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
+            @Override
+            public ViewHolder findViewHolder(int position) {
+                final ViewHolder vh = findViewHolderForPosition(position, true);
+                if (vh == null) {
+                    return null;
+                }
+                // ensure it is not hidden because for adapter helper, the only thing matter is that
+                // LM thinks view is a child.
+                if (mChildHelper.isHidden(vh.itemView)) {
+                    if (DEBUG) {
+                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
+                    }
+                    return null;
+                }
+                return vh;
+            }
+
+            @Override
+            public void offsetPositionsForRemovingInvisible(int start, int count) {
+                offsetPositionRecordsForRemove(start, count, true);
+                mItemsAddedOrRemoved = true;
+                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
+            }
+
+            @Override
+            public void offsetPositionsForRemovingLaidOutOrNewView(
+                    int positionStart, int itemCount) {
+                offsetPositionRecordsForRemove(positionStart, itemCount, false);
+                mItemsAddedOrRemoved = true;
+            }
+
+            @Override
+            public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
+                viewRangeUpdate(positionStart, itemCount, payload);
+                mItemsChanged = true;
+            }
+
+            @Override
+            public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
+                dispatchUpdate(op);
+            }
+
+            void dispatchUpdate(AdapterHelper.UpdateOp op) {
+                switch (op.cmd) {
+                    case AdapterHelper.UpdateOp.ADD:
+                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
+                        break;
+                    case AdapterHelper.UpdateOp.REMOVE:
+                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
+                        break;
+                    case AdapterHelper.UpdateOp.UPDATE:
+                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
+                                op.payload);
+                        break;
+                    case AdapterHelper.UpdateOp.MOVE:
+                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
+                        break;
+                }
+            }
+
+            @Override
+            public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
+                dispatchUpdate(op);
+            }
+
+            @Override
+            public void offsetPositionsForAdd(int positionStart, int itemCount) {
+                offsetPositionRecordsForInsert(positionStart, itemCount);
+                mItemsAddedOrRemoved = true;
+            }
+
+            @Override
+            public void offsetPositionsForMove(int from, int to) {
+                offsetPositionRecordsForMove(from, to);
+                // should we create mItemsMoved ?
+                mItemsAddedOrRemoved = true;
+            }
+        });
+    }
+
+    /**
+     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
+     * size is not affected by the adapter contents. RecyclerView can still change its size based
+     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
+     * size of its children or contents of its adapter (except the number of items in the adapter).
+     * <p>
+     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
+     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
+     *
+     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
+     */
+    public void setHasFixedSize(boolean hasFixedSize) {
+        mHasFixedSize = hasFixedSize;
+    }
+
+    /**
+     * @return true if the app has specified that changes in adapter content cannot change
+     * the size of the RecyclerView itself.
+     */
+    public boolean hasFixedSize() {
+        return mHasFixedSize;
+    }
+
+    @Override
+    public void setClipToPadding(boolean clipToPadding) {
+        if (clipToPadding != mClipToPadding) {
+            invalidateGlows();
+        }
+        mClipToPadding = clipToPadding;
+        super.setClipToPadding(clipToPadding);
+        if (mFirstLayoutComplete) {
+            requestLayout();
+        }
+    }
+
+    /**
+     * Returns whether this RecyclerView will clip its children to its padding, and resize (but
+     * not clip) any EdgeEffect to the padded region, if padding is present.
+     * <p>
+     * By default, children are clipped to the padding of their parent
+     * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
+     *
+     * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
+     *         clip) any EdgeEffect to the padded region, false otherwise.
+     *
+     * @attr name android:clipToPadding
+     */
+    @Override
+    public boolean getClipToPadding() {
+        return mClipToPadding;
+    }
+
+    /**
+     * Configure the scrolling touch slop for a specific use case.
+     *
+     * Set up the RecyclerView's scrolling motion threshold based on common usages.
+     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
+     *
+     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
+     *                     the intended usage of this RecyclerView
+     */
+    public void setScrollingTouchSlop(int slopConstant) {
+        final ViewConfiguration vc = ViewConfiguration.get(getContext());
+        switch (slopConstant) {
+            default:
+                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
+                        + slopConstant + "; using default value");
+                // fall-through
+            case TOUCH_SLOP_DEFAULT:
+                mTouchSlop = vc.getScaledTouchSlop();
+                break;
+
+            case TOUCH_SLOP_PAGING:
+                mTouchSlop = vc.getScaledPagingTouchSlop();
+                break;
+        }
+    }
+
+    /**
+     * Swaps the current adapter with the provided one. It is similar to
+     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
+     * {@link ViewHolder} and does not clear the RecycledViewPool.
+     * <p>
+     * Note that it still calls onAdapterChanged callbacks.
+     *
+     * @param adapter The new adapter to set, or null to set no adapter.
+     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
+     *                                      Views. If adapters have stable ids and/or you want to
+     *                                      animate the disappearing views, you may prefer to set
+     *                                      this to false.
+     * @see #setAdapter(Adapter)
+     */
+    public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
+        // bail out if layout is frozen
+        setLayoutFrozen(false);
+        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
+        setDataSetChangedAfterLayout();
+        requestLayout();
+    }
+    /**
+     * Set a new adapter to provide child views on demand.
+     * <p>
+     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
+     * only one adapter, it will be cleared.
+     *
+     * @param adapter The new adapter to set, or null to set no adapter.
+     * @see #swapAdapter(Adapter, boolean)
+     */
+    public void setAdapter(Adapter adapter) {
+        // bail out if layout is frozen
+        setLayoutFrozen(false);
+        setAdapterInternal(adapter, false, true);
+        requestLayout();
+    }
+
+    /**
+     * Removes and recycles all views - both those currently attached, and those in the Recycler.
+     */
+    void removeAndRecycleViews() {
+        // end all running animations
+        if (mItemAnimator != null) {
+            mItemAnimator.endAnimations();
+        }
+        // Since animations are ended, mLayout.children should be equal to
+        // recyclerView.children. This may not be true if item animator's end does not work as
+        // expected. (e.g. not release children instantly). It is safer to use mLayout's child
+        // count.
+        if (mLayout != null) {
+            mLayout.removeAndRecycleAllViews(mRecycler);
+            mLayout.removeAndRecycleScrapInt(mRecycler);
+        }
+        // we should clear it here before adapters are swapped to ensure correct callbacks.
+        mRecycler.clear();
+    }
+
+    /**
+     * Replaces the current adapter with the new one and triggers listeners.
+     * @param adapter The new adapter
+     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
+     *                               item types with the current adapter (helps us avoid cache
+     *                               invalidation).
+     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
+     *                               compatibleWithPrevious is false, this parameter is ignored.
+     */
+    private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
+            boolean removeAndRecycleViews) {
+        if (mAdapter != null) {
+            mAdapter.unregisterAdapterDataObserver(mObserver);
+            mAdapter.onDetachedFromRecyclerView(this);
+        }
+        if (!compatibleWithPrevious || removeAndRecycleViews) {
+            removeAndRecycleViews();
+        }
+        mAdapterHelper.reset();
+        final Adapter oldAdapter = mAdapter;
+        mAdapter = adapter;
+        if (adapter != null) {
+            adapter.registerAdapterDataObserver(mObserver);
+            adapter.onAttachedToRecyclerView(this);
+        }
+        if (mLayout != null) {
+            mLayout.onAdapterChanged(oldAdapter, mAdapter);
+        }
+        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
+        mState.mStructureChanged = true;
+        markKnownViewsInvalid();
+    }
+
+    /**
+     * Retrieves the previously set adapter or null if no adapter is set.
+     *
+     * @return The previously set adapter
+     * @see #setAdapter(Adapter)
+     */
+    public Adapter getAdapter() {
+        return mAdapter;
+    }
+
+    /**
+     * Register a listener that will be notified whenever a child view is recycled.
+     *
+     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
+     * that a child view is no longer needed. If an application associates expensive
+     * or heavyweight data with item views, this may be a good place to release
+     * or free those resources.</p>
+     *
+     * @param listener Listener to register, or null to clear
+     */
+    public void setRecyclerListener(RecyclerListener listener) {
+        mRecyclerListener = listener;
+    }
+
+    /**
+     * <p>Return the offset of the RecyclerView's text baseline from the its top
+     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
+     * this method returns -1.</p>
+     *
+     * @return the offset of the baseline within the RecyclerView's bounds or -1
+     *         if baseline alignment is not supported
+     */
+    @Override
+    public int getBaseline() {
+        if (mLayout != null) {
+            return mLayout.getBaseline();
+        } else {
+            return super.getBaseline();
+        }
+    }
+
+    /**
+     * Register a listener that will be notified whenever a child view is attached to or detached
+     * from RecyclerView.
+     *
+     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
+     * that a child view is no longer needed. If an application associates expensive
+     * or heavyweight data with item views, this may be a good place to release
+     * or free those resources.</p>
+     *
+     * @param listener Listener to register
+     */
+    public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
+        if (mOnChildAttachStateListeners == null) {
+            mOnChildAttachStateListeners = new ArrayList<>();
+        }
+        mOnChildAttachStateListeners.add(listener);
+    }
+
+    /**
+     * Removes the provided listener from child attached state listeners list.
+     *
+     * @param listener Listener to unregister
+     */
+    public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
+        if (mOnChildAttachStateListeners == null) {
+            return;
+        }
+        mOnChildAttachStateListeners.remove(listener);
+    }
+
+    /**
+     * Removes all listeners that were added via
+     * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
+     */
+    public void clearOnChildAttachStateChangeListeners() {
+        if (mOnChildAttachStateListeners != null) {
+            mOnChildAttachStateListeners.clear();
+        }
+    }
+
+    /**
+     * Set the {@link LayoutManager} that this RecyclerView will use.
+     *
+     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
+     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
+     * layout arrangements for child views. These arrangements are controlled by the
+     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
+     *
+     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
+     *
+     * @param layout LayoutManager to use
+     */
+    public void setLayoutManager(LayoutManager layout) {
+        if (layout == mLayout) {
+            return;
+        }
+        stopScroll();
+        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
+        // chance that LayoutManagers will re-use views.
+        if (mLayout != null) {
+            // end all running animations
+            if (mItemAnimator != null) {
+                mItemAnimator.endAnimations();
+            }
+            mLayout.removeAndRecycleAllViews(mRecycler);
+            mLayout.removeAndRecycleScrapInt(mRecycler);
+            mRecycler.clear();
+
+            if (mIsAttached) {
+                mLayout.dispatchDetachedFromWindow(this, mRecycler);
+            }
+            mLayout.setRecyclerView(null);
+            mLayout = null;
+        } else {
+            mRecycler.clear();
+        }
+        // this is just a defensive measure for faulty item animators.
+        mChildHelper.removeAllViewsUnfiltered();
+        mLayout = layout;
+        if (layout != null) {
+            if (layout.mRecyclerView != null) {
+                throw new IllegalArgumentException("LayoutManager " + layout
+                        + " is already attached to a RecyclerView: " + layout.mRecyclerView);
+            }
+            mLayout.setRecyclerView(this);
+            if (mIsAttached) {
+                mLayout.dispatchAttachedToWindow(this);
+            }
+        }
+        mRecycler.updateViewCacheSize();
+        requestLayout();
+    }
+
+    /**
+     * Set a {@link OnFlingListener} for this {@link RecyclerView}.
+     * <p>
+     * If the {@link OnFlingListener} is set then it will receive
+     * calls to {@link #fling(int,int)} and will be able to intercept them.
+     *
+     * @param onFlingListener The {@link OnFlingListener} instance.
+     */
+    public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
+        mOnFlingListener = onFlingListener;
+    }
+
+    /**
+     * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
+     *
+     * @return The {@link OnFlingListener} instance currently set (can be null).
+     */
+    @Nullable
+    public OnFlingListener getOnFlingListener() {
+        return mOnFlingListener;
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        SavedState state = new SavedState(super.onSaveInstanceState());
+        if (mPendingSavedState != null) {
+            state.copyFrom(mPendingSavedState);
+        } else if (mLayout != null) {
+            state.mLayoutState = mLayout.onSaveInstanceState();
+        } else {
+            state.mLayoutState = null;
+        }
+
+        return state;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (!(state instanceof SavedState)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+
+        mPendingSavedState = (SavedState) state;
+        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
+        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
+            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
+        }
+    }
+
+    /**
+     * Override to prevent freezing of any views created by the adapter.
+     */
+    @Override
+    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
+        dispatchFreezeSelfOnly(container);
+    }
+
+    /**
+     * Override to prevent thawing of any views created by the adapter.
+     */
+    @Override
+    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+        dispatchThawSelfOnly(container);
+    }
+
+    /**
+     * Adds a view to the animatingViews list.
+     * mAnimatingViews holds the child views that are currently being kept around
+     * purely for the purpose of being animated out of view. They are drawn as a regular
+     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
+     * as they are managed separately from the regular child views.
+     * @param viewHolder The ViewHolder to be removed
+     */
+    private void addAnimatingView(ViewHolder viewHolder) {
+        final View view = viewHolder.itemView;
+        final boolean alreadyParented = view.getParent() == this;
+        mRecycler.unscrapView(getChildViewHolder(view));
+        if (viewHolder.isTmpDetached()) {
+            // re-attach
+            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
+        } else if (!alreadyParented) {
+            mChildHelper.addView(view, true);
+        } else {
+            mChildHelper.hide(view);
+        }
+    }
+
+    /**
+     * Removes a view from the animatingViews list.
+     * @param view The view to be removed
+     * @see #addAnimatingView(RecyclerView.ViewHolder)
+     * @return true if an animating view is removed
+     */
+    boolean removeAnimatingView(View view) {
+        eatRequestLayout();
+        final boolean removed = mChildHelper.removeViewIfHidden(view);
+        if (removed) {
+            final ViewHolder viewHolder = getChildViewHolderInt(view);
+            mRecycler.unscrapView(viewHolder);
+            mRecycler.recycleViewHolderInternal(viewHolder);
+            if (DEBUG) {
+                Log.d(TAG, "after removing animated view: " + view + ", " + this);
+            }
+        }
+        // only clear request eaten flag if we removed the view.
+        resumeRequestLayout(!removed);
+        return removed;
+    }
+
+    /**
+     * Return the {@link LayoutManager} currently responsible for
+     * layout policy for this RecyclerView.
+     *
+     * @return The currently bound LayoutManager
+     */
+    public LayoutManager getLayoutManager() {
+        return mLayout;
+    }
+
+    /**
+     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
+     * if no pool is set for this view a new one will be created. See
+     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
+     *
+     * @return The pool used to store recycled item views for reuse.
+     * @see #setRecycledViewPool(RecycledViewPool)
+     */
+    public RecycledViewPool getRecycledViewPool() {
+        return mRecycler.getRecycledViewPool();
+    }
+
+    /**
+     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
+     * This can be useful if you have multiple RecyclerViews with adapters that use the same
+     * view types, for example if you have several data sets with the same kinds of item views
+     * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
+     *
+     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
+     */
+    public void setRecycledViewPool(RecycledViewPool pool) {
+        mRecycler.setRecycledViewPool(pool);
+    }
+
+    /**
+     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
+     *
+     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
+     *
+     * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
+     */
+    public void setViewCacheExtension(ViewCacheExtension extension) {
+        mRecycler.setViewCacheExtension(extension);
+    }
+
+    /**
+     * Set the number of offscreen views to retain before adding them to the potentially shared
+     * {@link #getRecycledViewPool() recycled view pool}.
+     *
+     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
+     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
+     * to rebind them.</p>
+     *
+     * @param size Number of views to cache offscreen before returning them to the general
+     *             recycled view pool
+     */
+    public void setItemViewCacheSize(int size) {
+        mRecycler.setViewCacheSize(size);
+    }
+
+    /**
+     * Return the current scrolling state of the RecyclerView.
+     *
+     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
+     * {@link #SCROLL_STATE_SETTLING}
+     */
+    public int getScrollState() {
+        return mScrollState;
+    }
+
+    void setScrollState(int state) {
+        if (state == mScrollState) {
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
+                    new Exception());
+        }
+        mScrollState = state;
+        if (state != SCROLL_STATE_SETTLING) {
+            stopScrollersInternal();
+        }
+        dispatchOnScrollStateChanged(state);
+    }
+
+    /**
+     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
+     * affect both measurement and drawing of individual item views.
+     *
+     * <p>Item decorations are ordered. Decorations placed earlier in the list will
+     * be run/queried/drawn first for their effects on item views. Padding added to views
+     * will be nested; a padding added by an earlier decoration will mean further
+     * item decorations in the list will be asked to draw/pad within the previous decoration's
+     * given area.</p>
+     *
+     * @param decor Decoration to add
+     * @param index Position in the decoration chain to insert this decoration at. If this value
+     *              is negative the decoration will be added at the end.
+     */
+    public void addItemDecoration(ItemDecoration decor, int index) {
+        if (mLayout != null) {
+            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
+                    + " layout");
+        }
+        if (mItemDecorations.isEmpty()) {
+            setWillNotDraw(false);
+        }
+        if (index < 0) {
+            mItemDecorations.add(decor);
+        } else {
+            mItemDecorations.add(index, decor);
+        }
+        markItemDecorInsetsDirty();
+        requestLayout();
+    }
+
+    /**
+     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
+     * affect both measurement and drawing of individual item views.
+     *
+     * <p>Item decorations are ordered. Decorations placed earlier in the list will
+     * be run/queried/drawn first for their effects on item views. Padding added to views
+     * will be nested; a padding added by an earlier decoration will mean further
+     * item decorations in the list will be asked to draw/pad within the previous decoration's
+     * given area.</p>
+     *
+     * @param decor Decoration to add
+     */
+    public void addItemDecoration(ItemDecoration decor) {
+        addItemDecoration(decor, -1);
+    }
+
+    /**
+     * Remove an {@link ItemDecoration} from this RecyclerView.
+     *
+     * <p>The given decoration will no longer impact the measurement and drawing of
+     * item views.</p>
+     *
+     * @param decor Decoration to remove
+     * @see #addItemDecoration(ItemDecoration)
+     */
+    public void removeItemDecoration(ItemDecoration decor) {
+        if (mLayout != null) {
+            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
+                    + " layout");
+        }
+        mItemDecorations.remove(decor);
+        if (mItemDecorations.isEmpty()) {
+            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
+        }
+        markItemDecorInsetsDirty();
+        requestLayout();
+    }
+
+    /**
+     * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
+     * <p>
+     * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
+     * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
+     * true if childDrawingOrderCallback is not null, false otherwise.
+     * <p>
+     * Note that child drawing order may be overridden by View's elevation.
+     *
+     * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
+     *                                  system.
+     */
+    public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
+        if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
+            return;
+        }
+        mChildDrawingOrderCallback = childDrawingOrderCallback;
+        setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
+    }
+
+    /**
+     * Set a listener that will be notified of any changes in scroll state or position.
+     *
+     * @param listener Listener to set or null to clear
+     *
+     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
+     *             {@link #removeOnScrollListener(OnScrollListener)}
+     */
+    @Deprecated
+    public void setOnScrollListener(OnScrollListener listener) {
+        mScrollListener = listener;
+    }
+
+    /**
+     * Add a listener that will be notified of any changes in scroll state or position.
+     *
+     * <p>Components that add a listener should take care to remove it when finished.
+     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
+     * to remove all attached listeners.</p>
+     *
+     * @param listener listener to set or null to clear
+     */
+    public void addOnScrollListener(OnScrollListener listener) {
+        if (mScrollListeners == null) {
+            mScrollListeners = new ArrayList<>();
+        }
+        mScrollListeners.add(listener);
+    }
+
+    /**
+     * Remove a listener that was notified of any changes in scroll state or position.
+     *
+     * @param listener listener to set or null to clear
+     */
+    public void removeOnScrollListener(OnScrollListener listener) {
+        if (mScrollListeners != null) {
+            mScrollListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Remove all secondary listener that were notified of any changes in scroll state or position.
+     */
+    public void clearOnScrollListeners() {
+        if (mScrollListeners != null) {
+            mScrollListeners.clear();
+        }
+    }
+
+    /**
+     * Convenience method to scroll to a certain position.
+     *
+     * RecyclerView does not implement scrolling logic, rather forwards the call to
+     * {@link com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
+     * @param position Scroll to this adapter position
+     * @see com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)
+     */
+    public void scrollToPosition(int position) {
+        if (mLayoutFrozen) {
+            return;
+        }
+        stopScroll();
+        if (mLayout == null) {
+            Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
+            return;
+        }
+        mLayout.scrollToPosition(position);
+        awakenScrollBars();
+    }
+
+    void jumpToPositionForSmoothScroller(int position) {
+        if (mLayout == null) {
+            return;
+        }
+        mLayout.scrollToPosition(position);
+        awakenScrollBars();
+    }
+
+    /**
+     * Starts a smooth scroll to an adapter position.
+     * <p>
+     * To support smooth scrolling, you must override
+     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
+     * {@link SmoothScroller}.
+     * <p>
+     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
+     * provide a custom smooth scroll logic, override
+     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
+     * LayoutManager.
+     *
+     * @param position The adapter position to scroll to
+     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
+     */
+    public void smoothScrollToPosition(int position) {
+        if (mLayoutFrozen) {
+            return;
+        }
+        if (mLayout == null) {
+            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
+            return;
+        }
+        mLayout.smoothScrollToPosition(this, mState, position);
+    }
+
+    @Override
+    public void scrollTo(int x, int y) {
+        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
+                + "Use scrollToPosition instead");
+    }
+
+    @Override
+    public void scrollBy(int x, int y) {
+        if (mLayout == null) {
+            Log.e(TAG, "Cannot scroll without a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
+            return;
+        }
+        if (mLayoutFrozen) {
+            return;
+        }
+        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
+        final boolean canScrollVertical = mLayout.canScrollVertically();
+        if (canScrollHorizontal || canScrollVertical) {
+            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
+        }
+    }
+
+    /**
+     * Helper method reflect data changes to the state.
+     * <p>
+     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
+     * but data actually changed.
+     * <p>
+     * This method consumes all deferred changes to avoid that case.
+     */
+    void consumePendingUpdateOperations() {
+        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
+            Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
+            dispatchLayout();
+            Trace.endSection();
+            return;
+        }
+        if (!mAdapterHelper.hasPendingUpdates()) {
+            return;
+        }
+
+        // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
+        // of the visible items is affected and if not, just ignore the change.
+        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
+                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
+                        | AdapterHelper.UpdateOp.MOVE)) {
+            Trace.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
+            eatRequestLayout();
+            onEnterLayoutOrScroll();
+            mAdapterHelper.preProcess();
+            if (!mLayoutRequestEaten) {
+                if (hasUpdatedView()) {
+                    dispatchLayout();
+                } else {
+                    // no need to layout, clean state
+                    mAdapterHelper.consumePostponedUpdates();
+                }
+            }
+            resumeRequestLayout(true);
+            onExitLayoutOrScroll();
+            Trace.endSection();
+        } else if (mAdapterHelper.hasPendingUpdates()) {
+            Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
+            dispatchLayout();
+            Trace.endSection();
+        }
+    }
+
+    /**
+     * @return True if an existing view holder needs to be updated
+     */
+    private boolean hasUpdatedView() {
+        final int childCount = mChildHelper.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
+            if (holder == null || holder.shouldIgnore()) {
+                continue;
+            }
+            if (holder.isUpdated()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Does not perform bounds checking. Used by internal methods that have already validated input.
+     * <p>
+     * It also reports any unused scroll request to the related EdgeEffect.
+     *
+     * @param x The amount of horizontal scroll request
+     * @param y The amount of vertical scroll request
+     * @param ev The originating MotionEvent, or null if not from a touch event.
+     *
+     * @return Whether any scroll was consumed in either direction.
+     */
+    boolean scrollByInternal(int x, int y, MotionEvent ev) {
+        int unconsumedX = 0, unconsumedY = 0;
+        int consumedX = 0, consumedY = 0;
+
+        consumePendingUpdateOperations();
+        if (mAdapter != null) {
+            eatRequestLayout();
+            onEnterLayoutOrScroll();
+            Trace.beginSection(TRACE_SCROLL_TAG);
+            if (x != 0) {
+                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
+                unconsumedX = x - consumedX;
+            }
+            if (y != 0) {
+                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
+                unconsumedY = y - consumedY;
+            }
+            Trace.endSection();
+            repositionShadowingViews();
+            onExitLayoutOrScroll();
+            resumeRequestLayout(false);
+        }
+        if (!mItemDecorations.isEmpty()) {
+            invalidate();
+        }
+
+        if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
+            // Update the last touch co-ords, taking any scroll offset into account
+            mLastTouchX -= mScrollOffset[0];
+            mLastTouchY -= mScrollOffset[1];
+            if (ev != null) {
+                ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
+            }
+            mNestedOffsets[0] += mScrollOffset[0];
+            mNestedOffsets[1] += mScrollOffset[1];
+        } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
+            if (ev != null) {
+                pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
+            }
+            considerReleasingGlowsOnScroll(x, y);
+        }
+        if (consumedX != 0 || consumedY != 0) {
+            dispatchOnScrolled(consumedX, consumedY);
+        }
+        if (!awakenScrollBars()) {
+            invalidate();
+        }
+        return consumedX != 0 || consumedY != 0;
+    }
+
+    /**
+     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
+     * range. This value is used to compute the length of the thumb within the scrollbar's track.
+     * </p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the units used by
+     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
+     *
+     * <p>Default implementation returns 0.</p>
+     *
+     * <p>If you want to support scroll bars, override
+     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
+     * LayoutManager. </p>
+     *
+     * @return The horizontal offset of the scrollbar's thumb
+     * @see com.android.internal.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
+     * (RecyclerView.State)
+     */
+    @Override
+    public int computeHorizontalScrollOffset() {
+        if (mLayout == null) {
+            return 0;
+        }
+        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
+    }
+
+    /**
+     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
+     * horizontal range. This value is used to compute the length of the thumb within the
+     * scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the units used by
+     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
+     *
+     * <p>Default implementation returns 0.</p>
+     *
+     * <p>If you want to support scroll bars, override
+     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
+     * LayoutManager.</p>
+     *
+     * @return The horizontal extent of the scrollbar's thumb
+     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
+     */
+    @Override
+    public int computeHorizontalScrollExtent() {
+        if (mLayout == null) {
+            return 0;
+        }
+        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
+    }
+
+    /**
+     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the units used by
+     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
+     *
+     * <p>Default implementation returns 0.</p>
+     *
+     * <p>If you want to support scroll bars, override
+     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
+     * LayoutManager.</p>
+     *
+     * @return The total horizontal range represented by the vertical scrollbar
+     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
+     */
+    @Override
+    public int computeHorizontalScrollRange() {
+        if (mLayout == null) {
+            return 0;
+        }
+        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
+    }
+
+    /**
+     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
+     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the units used by
+     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
+     *
+     * <p>Default implementation returns 0.</p>
+     *
+     * <p>If you want to support scroll bars, override
+     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
+     * LayoutManager.</p>
+     *
+     * @return The vertical offset of the scrollbar's thumb
+     * @see com.android.internal.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
+     * (RecyclerView.State)
+     */
+    @Override
+    public int computeVerticalScrollOffset() {
+        if (mLayout == null) {
+            return 0;
+        }
+        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
+    }
+
+    /**
+     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
+     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the units used by
+     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
+     *
+     * <p>Default implementation returns 0.</p>
+     *
+     * <p>If you want to support scroll bars, override
+     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
+     * LayoutManager.</p>
+     *
+     * @return The vertical extent of the scrollbar's thumb
+     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
+     */
+    @Override
+    public int computeVerticalScrollExtent() {
+        if (mLayout == null) {
+            return 0;
+        }
+        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
+    }
+
+    /**
+     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the units used by
+     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
+     *
+     * <p>Default implementation returns 0.</p>
+     *
+     * <p>If you want to support scroll bars, override
+     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
+     * LayoutManager.</p>
+     *
+     * @return The total vertical range represented by the vertical scrollbar
+     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
+     */
+    @Override
+    public int computeVerticalScrollRange() {
+        if (mLayout == null) {
+            return 0;
+        }
+        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
+    }
+
+
+    void eatRequestLayout() {
+        mEatRequestLayout++;
+        if (mEatRequestLayout == 1 && !mLayoutFrozen) {
+            mLayoutRequestEaten = false;
+        }
+    }
+
+    void resumeRequestLayout(boolean performLayoutChildren) {
+        if (mEatRequestLayout < 1) {
+            //noinspection PointlessBooleanExpression
+            if (DEBUG) {
+                throw new IllegalStateException("invalid eat request layout count");
+            }
+            mEatRequestLayout = 1;
+        }
+        if (!performLayoutChildren) {
+            // Reset the layout request eaten counter.
+            // This is necessary since eatRequest calls can be nested in which case the other
+            // call will override the inner one.
+            // for instance:
+            // eat layout for process adapter updates
+            //   eat layout for dispatchLayout
+            //     a bunch of req layout calls arrive
+
+            mLayoutRequestEaten = false;
+        }
+        if (mEatRequestLayout == 1) {
+            // when layout is frozen we should delay dispatchLayout()
+            if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
+                    && mLayout != null && mAdapter != null) {
+                dispatchLayout();
+            }
+            if (!mLayoutFrozen) {
+                mLayoutRequestEaten = false;
+            }
+        }
+        mEatRequestLayout--;
+    }
+
+    /**
+     * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
+     * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
+     * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
+     * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
+     * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
+     * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
+     * called.
+     *
+     * <p>
+     * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
+     * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
+     * RecyclerView, State, int)}.
+     * <p>
+     * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
+     * stop frozen.
+     * <p>
+     * Note: Running ItemAnimator is not stopped automatically,  it's caller's
+     * responsibility to call ItemAnimator.end().
+     *
+     * @param frozen   true to freeze layout and scroll, false to re-enable.
+     */
+    public void setLayoutFrozen(boolean frozen) {
+        if (frozen != mLayoutFrozen) {
+            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
+            if (!frozen) {
+                mLayoutFrozen = false;
+                if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
+                    requestLayout();
+                }
+                mLayoutRequestEaten = false;
+            } else {
+                final long now = SystemClock.uptimeMillis();
+                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
+                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+                onTouchEvent(cancelEvent);
+                mLayoutFrozen = true;
+                mIgnoreMotionEventTillDown = true;
+                stopScroll();
+            }
+        }
+    }
+
+    /**
+     * Returns true if layout and scroll are frozen.
+     *
+     * @return true if layout and scroll are frozen
+     * @see #setLayoutFrozen(boolean)
+     */
+    public boolean isLayoutFrozen() {
+        return mLayoutFrozen;
+    }
+
+    /**
+     * Animate a scroll by the given amount of pixels along either axis.
+     *
+     * @param dx Pixels to scroll horizontally
+     * @param dy Pixels to scroll vertically
+     */
+    public void smoothScrollBy(int dx, int dy) {
+        smoothScrollBy(dx, dy, null);
+    }
+
+    /**
+     * Animate a scroll by the given amount of pixels along either axis.
+     *
+     * @param dx Pixels to scroll horizontally
+     * @param dy Pixels to scroll vertically
+     * @param interpolator {@link Interpolator} to be used for scrolling. If it is
+     *                     {@code null}, RecyclerView is going to use the default interpolator.
+     */
+    public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
+        if (mLayout == null) {
+            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
+            return;
+        }
+        if (mLayoutFrozen) {
+            return;
+        }
+        if (!mLayout.canScrollHorizontally()) {
+            dx = 0;
+        }
+        if (!mLayout.canScrollVertically()) {
+            dy = 0;
+        }
+        if (dx != 0 || dy != 0) {
+            mViewFlinger.smoothScrollBy(dx, dy, interpolator);
+        }
+    }
+
+    /**
+     * Begin a standard fling with an initial velocity along each axis in pixels per second.
+     * If the velocity given is below the system-defined minimum this method will return false
+     * and no fling will occur.
+     *
+     * @param velocityX Initial horizontal velocity in pixels per second
+     * @param velocityY Initial vertical velocity in pixels per second
+     * @return true if the fling was started, false if the velocity was too low to fling or
+     * LayoutManager does not support scrolling in the axis fling is issued.
+     *
+     * @see LayoutManager#canScrollVertically()
+     * @see LayoutManager#canScrollHorizontally()
+     */
+    public boolean fling(int velocityX, int velocityY) {
+        if (mLayout == null) {
+            Log.e(TAG, "Cannot fling without a LayoutManager set. "
+                    + "Call setLayoutManager with a non-null argument.");
+            return false;
+        }
+        if (mLayoutFrozen) {
+            return false;
+        }
+
+        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
+        final boolean canScrollVertical = mLayout.canScrollVertically();
+
+        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
+            velocityX = 0;
+        }
+        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
+            velocityY = 0;
+        }
+        if (velocityX == 0 && velocityY == 0) {
+            // If we don't have any velocity, return false
+            return false;
+        }
+
+        if (!dispatchNestedPreFling(velocityX, velocityY)) {
+            final boolean canScroll = canScrollHorizontal || canScrollVertical;
+            dispatchNestedFling(velocityX, velocityY, canScroll);
+
+            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
+                return true;
+            }
+
+            if (canScroll) {
+                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
+                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
+                mViewFlinger.fling(velocityX, velocityY);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Stop any current scroll in progress, such as one started by
+     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
+     */
+    public void stopScroll() {
+        setScrollState(SCROLL_STATE_IDLE);
+        stopScrollersInternal();
+    }
+
+    /**
+     * Similar to {@link #stopScroll()} but does not set the state.
+     */
+    private void stopScrollersInternal() {
+        mViewFlinger.stop();
+        if (mLayout != null) {
+            mLayout.stopSmoothScroller();
+        }
+    }
+
+    /**
+     * Returns the minimum velocity to start a fling.
+     *
+     * @return The minimum velocity to start a fling
+     */
+    public int getMinFlingVelocity() {
+        return mMinFlingVelocity;
+    }
+
+
+    /**
+     * Returns the maximum fling velocity used by this RecyclerView.
+     *
+     * @return The maximum fling velocity used by this RecyclerView.
+     */
+    public int getMaxFlingVelocity() {
+        return mMaxFlingVelocity;
+    }
+
+    /**
+     * Apply a pull to relevant overscroll glow effects
+     */
+    private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
+        boolean invalidate = false;
+        if (overscrollX < 0) {
+            ensureLeftGlow();
+            mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight());
+            invalidate = true;
+        } else if (overscrollX > 0) {
+            ensureRightGlow();
+            mRightGlow.onPull(overscrollX / getWidth(), y / getHeight());
+            invalidate = true;
+        }
+
+        if (overscrollY < 0) {
+            ensureTopGlow();
+            mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth());
+            invalidate = true;
+        } else if (overscrollY > 0) {
+            ensureBottomGlow();
+            mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth());
+            invalidate = true;
+        }
+
+        if (invalidate || overscrollX != 0 || overscrollY != 0) {
+            postInvalidateOnAnimation();
+        }
+    }
+
+    private void releaseGlows() {
+        boolean needsInvalidate = false;
+        if (mLeftGlow != null) {
+            mLeftGlow.onRelease();
+            needsInvalidate = true;
+        }
+        if (mTopGlow != null) {
+            mTopGlow.onRelease();
+            needsInvalidate = true;
+        }
+        if (mRightGlow != null) {
+            mRightGlow.onRelease();
+            needsInvalidate = true;
+        }
+        if (mBottomGlow != null) {
+            mBottomGlow.onRelease();
+            needsInvalidate = true;
+        }
+        if (needsInvalidate) {
+            postInvalidateOnAnimation();
+        }
+    }
+
+    void considerReleasingGlowsOnScroll(int dx, int dy) {
+        boolean needsInvalidate = false;
+        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
+            mLeftGlow.onRelease();
+            needsInvalidate = true;
+        }
+        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
+            mRightGlow.onRelease();
+            needsInvalidate = true;
+        }
+        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
+            mTopGlow.onRelease();
+            needsInvalidate = true;
+        }
+        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
+            mBottomGlow.onRelease();
+            needsInvalidate = true;
+        }
+        if (needsInvalidate) {
+            postInvalidateOnAnimation();
+        }
+    }
+
+    void absorbGlows(int velocityX, int velocityY) {
+        if (velocityX < 0) {
+            ensureLeftGlow();
+            mLeftGlow.onAbsorb(-velocityX);
+        } else if (velocityX > 0) {
+            ensureRightGlow();
+            mRightGlow.onAbsorb(velocityX);
+        }
+
+        if (velocityY < 0) {
+            ensureTopGlow();
+            mTopGlow.onAbsorb(-velocityY);
+        } else if (velocityY > 0) {
+            ensureBottomGlow();
+            mBottomGlow.onAbsorb(velocityY);
+        }
+
+        if (velocityX != 0 || velocityY != 0) {
+            postInvalidateOnAnimation();
+        }
+    }
+
+    void ensureLeftGlow() {
+        if (mLeftGlow != null) {
+            return;
+        }
+        mLeftGlow = new EdgeEffect(getContext());
+        if (mClipToPadding) {
+            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
+                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
+        } else {
+            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
+        }
+    }
+
+    void ensureRightGlow() {
+        if (mRightGlow != null) {
+            return;
+        }
+        mRightGlow = new EdgeEffect(getContext());
+        if (mClipToPadding) {
+            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
+                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
+        } else {
+            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
+        }
+    }
+
+    void ensureTopGlow() {
+        if (mTopGlow != null) {
+            return;
+        }
+        mTopGlow = new EdgeEffect(getContext());
+        if (mClipToPadding) {
+            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
+                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
+        } else {
+            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
+        }
+
+    }
+
+    void ensureBottomGlow() {
+        if (mBottomGlow != null) {
+            return;
+        }
+        mBottomGlow = new EdgeEffect(getContext());
+        if (mClipToPadding) {
+            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
+                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
+        } else {
+            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
+        }
+    }
+
+    void invalidateGlows() {
+        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
+    }
+
+    /**
+     * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
+     * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
+     * that differs from other ViewGroups.
+     * <p>
+     * It first does a focus search within the RecyclerView. If this search finds a View that is in
+     * the focus direction with respect to the currently focused View, RecyclerView returns that
+     * child as the next focus target. When it cannot find such child, it calls
+     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
+     * in the focus search direction. If LayoutManager adds a View that matches the
+     * focus search criteria, it will be returned as the focus search result. Otherwise,
+     * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
+     * <p>
+     * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
+     * is not in the focus direction is still valid focus target which may not be the desired
+     * behavior if the Adapter has more children in the focus direction. To handle this case,
+     * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
+     * focus search in that direction. If there are no Views to gain focus, it will call
+     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
+     * focus search with the original (relative) direction. This allows RecyclerView to provide
+     * better candidates to the focus search while still allowing the view system to take focus from
+     * the RecyclerView and give it to a more suitable child if such child exists.
+     *
+     * @param focused The view that currently has focus
+     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
+     * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
+     * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
+     *
+     * @return A new View that can be the next focus after the focused View
+     */
+    @Override
+    public View focusSearch(View focused, int direction) {
+        View result = mLayout.onInterceptFocusSearch(focused, direction);
+        if (result != null) {
+            return result;
+        }
+        final boolean canRunFocusFailure = mAdapter != null && mLayout != null
+                && !isComputingLayout() && !mLayoutFrozen;
+
+        final FocusFinder ff = FocusFinder.getInstance();
+        if (canRunFocusFailure
+                && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
+            // convert direction to absolute direction and see if we have a view there and if not
+            // tell LayoutManager to add if it can.
+            boolean needsFocusFailureLayout = false;
+            if (mLayout.canScrollVertically()) {
+                final int absDir =
+                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
+                final View found = ff.findNextFocus(this, focused, absDir);
+                needsFocusFailureLayout = found == null;
+                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
+                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
+                    direction = absDir;
+                }
+            }
+            if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
+                boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
+                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
+                final View found = ff.findNextFocus(this, focused, absDir);
+                needsFocusFailureLayout = found == null;
+                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
+                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
+                    direction = absDir;
+                }
+            }
+            if (needsFocusFailureLayout) {
+                consumePendingUpdateOperations();
+                final View focusedItemView = findContainingItemView(focused);
+                if (focusedItemView == null) {
+                    // panic, focused view is not a child anymore, cannot call super.
+                    return null;
+                }
+                eatRequestLayout();
+                mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
+                resumeRequestLayout(false);
+            }
+            result = ff.findNextFocus(this, focused, direction);
+        } else {
+            result = ff.findNextFocus(this, focused, direction);
+            if (result == null && canRunFocusFailure) {
+                consumePendingUpdateOperations();
+                final View focusedItemView = findContainingItemView(focused);
+                if (focusedItemView == null) {
+                    // panic, focused view is not a child anymore, cannot call super.
+                    return null;
+                }
+                eatRequestLayout();
+                result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
+                resumeRequestLayout(false);
+            }
+        }
+        return isPreferredNextFocus(focused, result, direction)
+                ? result : super.focusSearch(focused, direction);
+    }
+
+    /**
+     * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
+     * assign it as the next focus View instead of letting view hierarchy decide.
+     * A good candidate means a View that is aligned in the focus direction wrt the focused View
+     * and is not the RecyclerView itself.
+     * When this method returns false, RecyclerView will let the parent make the decision so the
+     * same View may still get the focus as a result of that search.
+     */
+    private boolean isPreferredNextFocus(View focused, View next, int direction) {
+        if (next == null || next == this) {
+            return false;
+        }
+        if (focused == null) {
+            return true;
+        }
+
+        if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
+            final boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
+                    ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
+            if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
+                return true;
+            }
+            if (direction == View.FOCUS_FORWARD) {
+                return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
+            } else {
+                return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
+            }
+        } else {
+            return isPreferredNextFocusAbsolute(focused, next, direction);
+        }
+
+    }
+
+    /**
+     * Logic taken from FocusSearch#isCandidate
+     */
+    private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
+        mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
+        mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
+        offsetDescendantRectToMyCoords(focused, mTempRect);
+        offsetDescendantRectToMyCoords(next, mTempRect2);
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return (mTempRect.right > mTempRect2.right
+                        || mTempRect.left >= mTempRect2.right)
+                        && mTempRect.left > mTempRect2.left;
+            case View.FOCUS_RIGHT:
+                return (mTempRect.left < mTempRect2.left
+                        || mTempRect.right <= mTempRect2.left)
+                        && mTempRect.right < mTempRect2.right;
+            case View.FOCUS_UP:
+                return (mTempRect.bottom > mTempRect2.bottom
+                        || mTempRect.top >= mTempRect2.bottom)
+                        && mTempRect.top > mTempRect2.top;
+            case View.FOCUS_DOWN:
+                return (mTempRect.top < mTempRect2.top
+                        || mTempRect.bottom <= mTempRect2.top)
+                        && mTempRect.bottom < mTempRect2.bottom;
+        }
+        throw new IllegalArgumentException("direction must be absolute. received:" + direction);
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
+            mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
+
+            // get item decor offsets w/o refreshing. If they are invalid, there will be another
+            // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
+            // View in viewport.
+            final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
+            if (focusedLayoutParams instanceof LayoutParams) {
+                // if focused child has item decors, use them. Otherwise, ignore.
+                final LayoutParams lp = (LayoutParams) focusedLayoutParams;
+                if (!lp.mInsetsDirty) {
+                    final Rect insets = lp.mDecorInsets;
+                    mTempRect.left -= insets.left;
+                    mTempRect.right += insets.right;
+                    mTempRect.top -= insets.top;
+                    mTempRect.bottom += insets.bottom;
+                }
+            }
+
+            offsetDescendantRectToMyCoords(focused, mTempRect);
+            offsetRectIntoDescendantCoords(child, mTempRect);
+            requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
+        }
+        super.requestChildFocus(child, focused);
+    }
+
+    @Override
+    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
+        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
+    }
+
+    @Override
+    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+        if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
+            super.addFocusables(views, direction, focusableMode);
+        }
+    }
+
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+        if (isComputingLayout()) {
+            // if we are in the middle of a layout calculation, don't let any child take focus.
+            // RV will handle it after layout calculation is finished.
+            return false;
+        }
+        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mLayoutOrScrollCounter = 0;
+        mIsAttached = true;
+        mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
+        if (mLayout != null) {
+            mLayout.dispatchAttachedToWindow(this);
+        }
+        mPostedAnimatorRunner = false;
+
+        if (ALLOW_THREAD_GAP_WORK) {
+            // Register with gap worker
+            mGapWorker = GapWorker.sGapWorker.get();
+            if (mGapWorker == null) {
+                mGapWorker = new GapWorker();
+
+                // break 60 fps assumption if data from display appears valid
+                // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
+                Display display = getDisplay();
+                float refreshRate = 60.0f;
+                if (!isInEditMode() && display != null) {
+                    float displayRefreshRate = display.getRefreshRate();
+                    if (displayRefreshRate >= 30.0f) {
+                        refreshRate = displayRefreshRate;
+                    }
+                }
+                mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
+                GapWorker.sGapWorker.set(mGapWorker);
+            }
+            mGapWorker.add(this);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mItemAnimator != null) {
+            mItemAnimator.endAnimations();
+        }
+        stopScroll();
+        mIsAttached = false;
+        if (mLayout != null) {
+            mLayout.dispatchDetachedFromWindow(this, mRecycler);
+        }
+        mPendingAccessibilityImportanceChange.clear();
+        removeCallbacks(mItemAnimatorRunner);
+        mViewInfoStore.onDetach();
+
+        if (ALLOW_THREAD_GAP_WORK) {
+            // Unregister with gap worker
+            mGapWorker.remove(this);
+            mGapWorker = null;
+        }
+    }
+
+    /**
+     * Returns true if RecyclerView is attached to window.
+     */
+    // @override
+    public boolean isAttachedToWindow() {
+        return mIsAttached;
+    }
+
+    /**
+     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
+     * {@link IllegalStateException} if it <b>is not</b>.
+     *
+     * @param message The message for the exception. Can be null.
+     * @see #assertNotInLayoutOrScroll(String)
+     */
+    void assertInLayoutOrScroll(String message) {
+        if (!isComputingLayout()) {
+            if (message == null) {
+                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
+                        + "computing a layout or scrolling");
+            }
+            throw new IllegalStateException(message);
+
+        }
+    }
+
+    /**
+     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
+     * {@link IllegalStateException} if it <b>is</b>.
+     *
+     * @param message The message for the exception. Can be null.
+     * @see #assertInLayoutOrScroll(String)
+     */
+    void assertNotInLayoutOrScroll(String message) {
+        if (isComputingLayout()) {
+            if (message == null) {
+                throw new IllegalStateException("Cannot call this method while RecyclerView is "
+                        + "computing a layout or scrolling");
+            }
+            throw new IllegalStateException(message);
+        }
+        if (mDispatchScrollCounter > 0) {
+            Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might be run"
+                    + " during a measure & layout pass where you cannot change the RecyclerView"
+                    + " data. Any method call that might change the structure of the RecyclerView"
+                    + " or the adapter contents should be postponed to the next frame.",
+                    new IllegalStateException(""));
+        }
+    }
+
+    /**
+     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
+     * to child views or this view's standard scrolling behavior.
+     *
+     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
+     * returns true from
+     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
+     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
+     * for each incoming MotionEvent until the end of the gesture.</p>
+     *
+     * @param listener Listener to add
+     * @see SimpleOnItemTouchListener
+     */
+    public void addOnItemTouchListener(OnItemTouchListener listener) {
+        mOnItemTouchListeners.add(listener);
+    }
+
+    /**
+     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
+     *
+     * @param listener Listener to remove
+     */
+    public void removeOnItemTouchListener(OnItemTouchListener listener) {
+        mOnItemTouchListeners.remove(listener);
+        if (mActiveOnItemTouchListener == listener) {
+            mActiveOnItemTouchListener = null;
+        }
+    }
+
+    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
+        final int action = e.getAction();
+        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
+            mActiveOnItemTouchListener = null;
+        }
+
+        final int listenerCount = mOnItemTouchListeners.size();
+        for (int i = 0; i < listenerCount; i++) {
+            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
+            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
+                mActiveOnItemTouchListener = listener;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean dispatchOnItemTouch(MotionEvent e) {
+        final int action = e.getAction();
+        if (mActiveOnItemTouchListener != null) {
+            if (action == MotionEvent.ACTION_DOWN) {
+                // Stale state from a previous gesture, we're starting a new one. Clear it.
+                mActiveOnItemTouchListener = null;
+            } else {
+                mActiveOnItemTouchListener.onTouchEvent(this, e);
+                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+                    // Clean up for the next gesture.
+                    mActiveOnItemTouchListener = null;
+                }
+                return true;
+            }
+        }
+
+        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
+        // as called from onInterceptTouchEvent; skip it.
+        if (action != MotionEvent.ACTION_DOWN) {
+            final int listenerCount = mOnItemTouchListeners.size();
+            for (int i = 0; i < listenerCount; i++) {
+                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
+                if (listener.onInterceptTouchEvent(this, e)) {
+                    mActiveOnItemTouchListener = listener;
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent e) {
+        if (mLayoutFrozen) {
+            // When layout is frozen,  RV does not intercept the motion event.
+            // A child view e.g. a button may still get the click.
+            return false;
+        }
+        if (dispatchOnItemTouchIntercept(e)) {
+            cancelTouch();
+            return true;
+        }
+
+        if (mLayout == null) {
+            return false;
+        }
+
+        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
+        final boolean canScrollVertically = mLayout.canScrollVertically();
+
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(e);
+
+        final int action = e.getActionMasked();
+        final int actionIndex = e.getActionIndex();
+
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                if (mIgnoreMotionEventTillDown) {
+                    mIgnoreMotionEventTillDown = false;
+                }
+                mScrollPointerId = e.getPointerId(0);
+                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
+                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
+
+                if (mScrollState == SCROLL_STATE_SETTLING) {
+                    getParent().requestDisallowInterceptTouchEvent(true);
+                    setScrollState(SCROLL_STATE_DRAGGING);
+                }
+
+                // Clear the nested offsets
+                mNestedOffsets[0] = mNestedOffsets[1] = 0;
+
+                int nestedScrollAxis = View.SCROLL_AXIS_NONE;
+                if (canScrollHorizontally) {
+                    nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
+                }
+                if (canScrollVertically) {
+                    nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
+                }
+                startNestedScroll(nestedScrollAxis);
+                break;
+
+            case MotionEvent.ACTION_POINTER_DOWN:
+                mScrollPointerId = e.getPointerId(actionIndex);
+                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
+                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
+                break;
+
+            case MotionEvent.ACTION_MOVE: {
+                final int index = e.findPointerIndex(mScrollPointerId);
+                if (index < 0) {
+                    Log.e(TAG, "Error processing scroll; pointer index for id "
+                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
+                    return false;
+                }
+
+                final int x = (int) (e.getX(index) + 0.5f);
+                final int y = (int) (e.getY(index) + 0.5f);
+                if (mScrollState != SCROLL_STATE_DRAGGING) {
+                    final int dx = x - mInitialTouchX;
+                    final int dy = y - mInitialTouchY;
+                    boolean startScroll = false;
+                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
+                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
+                        startScroll = true;
+                    }
+                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
+                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
+                        startScroll = true;
+                    }
+                    if (startScroll) {
+                        setScrollState(SCROLL_STATE_DRAGGING);
+                    }
+                }
+            } break;
+
+            case MotionEvent.ACTION_POINTER_UP: {
+                onPointerUp(e);
+            } break;
+
+            case MotionEvent.ACTION_UP: {
+                mVelocityTracker.clear();
+                stopNestedScroll();
+            } break;
+
+            case MotionEvent.ACTION_CANCEL: {
+                cancelTouch();
+            }
+        }
+        return mScrollState == SCROLL_STATE_DRAGGING;
+    }
+
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        final int listenerCount = mOnItemTouchListeners.size();
+        for (int i = 0; i < listenerCount; i++) {
+            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
+            listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
+        }
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent e) {
+        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
+            return false;
+        }
+        if (dispatchOnItemTouch(e)) {
+            cancelTouch();
+            return true;
+        }
+
+        if (mLayout == null) {
+            return false;
+        }
+
+        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
+        final boolean canScrollVertically = mLayout.canScrollVertically();
+
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        boolean eventAddedToVelocityTracker = false;
+
+        final MotionEvent vtev = MotionEvent.obtain(e);
+        final int action = e.getActionMasked();
+        final int actionIndex = e.getActionIndex();
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            mNestedOffsets[0] = mNestedOffsets[1] = 0;
+        }
+        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
+
+        switch (action) {
+            case MotionEvent.ACTION_DOWN: {
+                mScrollPointerId = e.getPointerId(0);
+                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
+                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
+
+                int nestedScrollAxis = View.SCROLL_AXIS_NONE;
+                if (canScrollHorizontally) {
+                    nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
+                }
+                if (canScrollVertically) {
+                    nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
+                }
+                startNestedScroll(nestedScrollAxis);
+            } break;
+
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                mScrollPointerId = e.getPointerId(actionIndex);
+                mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
+                mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
+            } break;
+
+            case MotionEvent.ACTION_MOVE: {
+                final int index = e.findPointerIndex(mScrollPointerId);
+                if (index < 0) {
+                    Log.e(TAG, "Error processing scroll; pointer index for id "
+                            + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
+                    return false;
+                }
+
+                final int x = (int) (e.getX(index) + 0.5f);
+                final int y = (int) (e.getY(index) + 0.5f);
+                int dx = mLastTouchX - x;
+                int dy = mLastTouchY - y;
+
+                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
+                    dx -= mScrollConsumed[0];
+                    dy -= mScrollConsumed[1];
+                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
+                    // Updated the nested offsets
+                    mNestedOffsets[0] += mScrollOffset[0];
+                    mNestedOffsets[1] += mScrollOffset[1];
+                }
+
+                if (mScrollState != SCROLL_STATE_DRAGGING) {
+                    boolean startScroll = false;
+                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
+                        if (dx > 0) {
+                            dx -= mTouchSlop;
+                        } else {
+                            dx += mTouchSlop;
+                        }
+                        startScroll = true;
+                    }
+                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
+                        if (dy > 0) {
+                            dy -= mTouchSlop;
+                        } else {
+                            dy += mTouchSlop;
+                        }
+                        startScroll = true;
+                    }
+                    if (startScroll) {
+                        setScrollState(SCROLL_STATE_DRAGGING);
+                    }
+                }
+
+                if (mScrollState == SCROLL_STATE_DRAGGING) {
+                    mLastTouchX = x - mScrollOffset[0];
+                    mLastTouchY = y - mScrollOffset[1];
+
+                    if (scrollByInternal(
+                            canScrollHorizontally ? dx : 0,
+                            canScrollVertically ? dy : 0,
+                            vtev)) {
+                        getParent().requestDisallowInterceptTouchEvent(true);
+                    }
+                    if (mGapWorker != null && (dx != 0 || dy != 0)) {
+                        mGapWorker.postFromTraversal(this, dx, dy);
+                    }
+                }
+            } break;
+
+            case MotionEvent.ACTION_POINTER_UP: {
+                onPointerUp(e);
+            } break;
+
+            case MotionEvent.ACTION_UP: {
+                mVelocityTracker.addMovement(vtev);
+                eventAddedToVelocityTracker = true;
+                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
+                final float xvel = canScrollHorizontally
+                        ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
+                final float yvel = canScrollVertically
+                        ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
+                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
+                    setScrollState(SCROLL_STATE_IDLE);
+                }
+                resetTouch();
+            } break;
+
+            case MotionEvent.ACTION_CANCEL: {
+                cancelTouch();
+            } break;
+        }
+
+        if (!eventAddedToVelocityTracker) {
+            mVelocityTracker.addMovement(vtev);
+        }
+        vtev.recycle();
+
+        return true;
+    }
+
+    private void resetTouch() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.clear();
+        }
+        stopNestedScroll();
+        releaseGlows();
+    }
+
+    private void cancelTouch() {
+        resetTouch();
+        setScrollState(SCROLL_STATE_IDLE);
+    }
+
+    private void onPointerUp(MotionEvent e) {
+        final int actionIndex = e.getActionIndex();
+        if (e.getPointerId(actionIndex) == mScrollPointerId) {
+            // Pick a new pointer to pick up the slack.
+            final int newIndex = actionIndex == 0 ? 1 : 0;
+            mScrollPointerId = e.getPointerId(newIndex);
+            mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
+            mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
+        }
+    }
+
+    // @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        if (mLayout == null) {
+            return false;
+        }
+        if (mLayoutFrozen) {
+            return false;
+        }
+        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+            if (event.getAction() == MotionEvent.ACTION_SCROLL) {
+                final float vScroll, hScroll;
+                if (mLayout.canScrollVertically()) {
+                    // Inverse the sign of the vertical scroll to align the scroll orientation
+                    // with AbsListView.
+                    vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+                } else {
+                    vScroll = 0f;
+                }
+                if (mLayout.canScrollHorizontally()) {
+                    hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+                } else {
+                    hScroll = 0f;
+                }
+
+                if (vScroll != 0 || hScroll != 0) {
+                    final float scrollFactor = getScrollFactor();
+                    scrollByInternal((int) (hScroll * scrollFactor),
+                            (int) (vScroll * scrollFactor), event);
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Ported from View.getVerticalScrollFactor.
+     */
+    private float getScrollFactor() {
+        if (mScrollFactor == Float.MIN_VALUE) {
+            TypedValue outValue = new TypedValue();
+            if (getContext().getTheme().resolveAttribute(
+                    android.R.attr.listPreferredItemHeight, outValue, true)) {
+                mScrollFactor = outValue.getDimension(
+                        getContext().getResources().getDisplayMetrics());
+            } else {
+                return 0; //listPreferredItemHeight is not defined, no generic scrolling
+            }
+        }
+        return mScrollFactor;
+    }
+
+    @Override
+    protected void onMeasure(int widthSpec, int heightSpec) {
+        if (mLayout == null) {
+            defaultOnMeasure(widthSpec, heightSpec);
+            return;
+        }
+        if (mLayout.mAutoMeasure) {
+            final int widthMode = MeasureSpec.getMode(widthSpec);
+            final int heightMode = MeasureSpec.getMode(heightSpec);
+            final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
+                    && heightMode == MeasureSpec.EXACTLY;
+            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
+            if (skipMeasure || mAdapter == null) {
+                return;
+            }
+            if (mState.mLayoutStep == State.STEP_START) {
+                dispatchLayoutStep1();
+            }
+            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
+            // consistency
+            mLayout.setMeasureSpecs(widthSpec, heightSpec);
+            mState.mIsMeasuring = true;
+            dispatchLayoutStep2();
+
+            // now we can get the width and height from the children.
+            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
+
+            // if RecyclerView has non-exact width and height and if there is at least one child
+            // which also has non-exact width & height, we have to re-measure.
+            if (mLayout.shouldMeasureTwice()) {
+                mLayout.setMeasureSpecs(
+                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
+                mState.mIsMeasuring = true;
+                dispatchLayoutStep2();
+                // now we can get the width and height from the children.
+                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
+            }
+        } else {
+            if (mHasFixedSize) {
+                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
+                return;
+            }
+            // custom onMeasure
+            if (mAdapterUpdateDuringMeasure) {
+                eatRequestLayout();
+                onEnterLayoutOrScroll();
+                processAdapterUpdatesAndSetAnimationFlags();
+                onExitLayoutOrScroll();
+
+                if (mState.mRunPredictiveAnimations) {
+                    mState.mInPreLayout = true;
+                } else {
+                    // consume remaining updates to provide a consistent state with the layout pass.
+                    mAdapterHelper.consumeUpdatesInOnePass();
+                    mState.mInPreLayout = false;
+                }
+                mAdapterUpdateDuringMeasure = false;
+                resumeRequestLayout(false);
+            }
+
+            if (mAdapter != null) {
+                mState.mItemCount = mAdapter.getItemCount();
+            } else {
+                mState.mItemCount = 0;
+            }
+            eatRequestLayout();
+            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
+            resumeRequestLayout(false);
+            mState.mInPreLayout = false; // clear
+        }
+    }
+
+    /**
+     * Used when onMeasure is called before layout manager is set
+     */
+    void defaultOnMeasure(int widthSpec, int heightSpec) {
+        // calling LayoutManager here is not pretty but that API is already public and it is better
+        // than creating another method since this is internal.
+        final int width = LayoutManager.chooseSize(widthSpec,
+                getPaddingLeft() + getPaddingRight(),
+                getMinimumWidth());
+        final int height = LayoutManager.chooseSize(heightSpec,
+                getPaddingTop() + getPaddingBottom(),
+                getMinimumHeight());
+
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        if (w != oldw || h != oldh) {
+            invalidateGlows();
+            // layout's w/h are updated during measure/layout steps.
+        }
+    }
+
+    /**
+     * Sets the {@link ItemAnimator} that will handle animations involving changes
+     * to the items in this RecyclerView. By default, RecyclerView instantiates and
+     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
+     * enabled for the RecyclerView depends on the ItemAnimator and whether
+     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
+     * supports item animations}.
+     *
+     * @param animator The ItemAnimator being set. If null, no animations will occur
+     * when changes occur to the items in this RecyclerView.
+     */
+    public void setItemAnimator(ItemAnimator animator) {
+        if (mItemAnimator != null) {
+            mItemAnimator.endAnimations();
+            mItemAnimator.setListener(null);
+        }
+        mItemAnimator = animator;
+        if (mItemAnimator != null) {
+            mItemAnimator.setListener(mItemAnimatorListener);
+        }
+    }
+
+    void onEnterLayoutOrScroll() {
+        mLayoutOrScrollCounter++;
+    }
+
+    void onExitLayoutOrScroll() {
+        mLayoutOrScrollCounter--;
+        if (mLayoutOrScrollCounter < 1) {
+            if (DEBUG && mLayoutOrScrollCounter < 0) {
+                throw new IllegalStateException("layout or scroll counter cannot go below zero."
+                        + "Some calls are not matching");
+            }
+            mLayoutOrScrollCounter = 0;
+            dispatchContentChangedIfNecessary();
+            dispatchPendingImportantForAccessibilityChanges();
+        }
+    }
+
+    boolean isAccessibilityEnabled() {
+        return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
+    }
+
+    private void dispatchContentChangedIfNecessary() {
+        final int flags = mEatenAccessibilityChangeFlags;
+        mEatenAccessibilityChangeFlags = 0;
+        if (flags != 0 && isAccessibilityEnabled()) {
+            final AccessibilityEvent event = AccessibilityEvent.obtain();
+            event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+            event.setContentChangeTypes(flags);
+            sendAccessibilityEventUnchecked(event);
+        }
+    }
+
+    /**
+     * Returns whether RecyclerView is currently computing a layout.
+     * <p>
+     * If this method returns true, it means that RecyclerView is in a lockdown state and any
+     * attempt to update adapter contents will result in an exception because adapter contents
+     * cannot be changed while RecyclerView is trying to compute the layout.
+     * <p>
+     * It is very unlikely that your code will be running during this state as it is
+     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
+     * in response to system events (touch, accessibility etc).
+     * <p>
+     * This case may happen if you have some custom logic to change adapter contents in
+     * response to a View callback (e.g. focus change callback) which might be triggered during a
+     * layout calculation. In these cases, you should just postpone the change using a Handler or a
+     * similar mechanism.
+     *
+     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
+     *         otherwise
+     */
+    public boolean isComputingLayout() {
+        return mLayoutOrScrollCounter > 0;
+    }
+
+    /**
+     * Returns true if an accessibility event should not be dispatched now. This happens when an
+     * accessibility request arrives while RecyclerView does not have a stable state which is very
+     * hard to handle for a LayoutManager. Instead, this method records necessary information about
+     * the event and dispatches a window change event after the critical section is finished.
+     *
+     * @return True if the accessibility event should be postponed.
+     */
+    boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
+        if (isComputingLayout()) {
+            int type = 0;
+            if (event != null) {
+                type = event.getContentChangeTypes();
+            }
+            if (type == 0) {
+                type = AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+            }
+            mEatenAccessibilityChangeFlags |= type;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
+        if (shouldDeferAccessibilityEvent(event)) {
+            return;
+        }
+        super.sendAccessibilityEventUnchecked(event);
+    }
+
+    /**
+     * Gets the current ItemAnimator for this RecyclerView. A null return value
+     * indicates that there is no animator and that item changes will happen without
+     * any animations. By default, RecyclerView instantiates and
+     * uses an instance of {@link DefaultItemAnimator}.
+     *
+     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
+     * when changes occur to the items in this RecyclerView.
+     */
+    public ItemAnimator getItemAnimator() {
+        return mItemAnimator;
+    }
+
+    /**
+     * Post a runnable to the next frame to run pending item animations. Only the first such
+     * request will be posted, governed by the mPostedAnimatorRunner flag.
+     */
+    void postAnimationRunner() {
+        if (!mPostedAnimatorRunner && mIsAttached) {
+            postOnAnimation(mItemAnimatorRunner);
+            mPostedAnimatorRunner = true;
+        }
+    }
+
+    private boolean predictiveItemAnimationsEnabled() {
+        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
+    }
+
+    /**
+     * Consumes adapter updates and calculates which type of animations we want to run.
+     * Called in onMeasure and dispatchLayout.
+     * <p>
+     * This method may process only the pre-layout state of updates or all of them.
+     */
+    private void processAdapterUpdatesAndSetAnimationFlags() {
+        if (mDataSetHasChangedAfterLayout) {
+            // Processing these items have no value since data set changed unexpectedly.
+            // Instead, we just reset it.
+            mAdapterHelper.reset();
+            mLayout.onItemsChanged(this);
+        }
+        // simple animations are a subset of advanced animations (which will cause a
+        // pre-layout step)
+        // If layout supports predictive animations, pre-process to decide if we want to run them
+        if (predictiveItemAnimationsEnabled()) {
+            mAdapterHelper.preProcess();
+        } else {
+            mAdapterHelper.consumeUpdatesInOnePass();
+        }
+        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
+        mState.mRunSimpleAnimations = mFirstLayoutComplete
+                && mItemAnimator != null
+                && (mDataSetHasChangedAfterLayout
+                        || animationTypeSupported
+                        || mLayout.mRequestedSimpleAnimations)
+                && (!mDataSetHasChangedAfterLayout
+                        || mAdapter.hasStableIds());
+        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
+                && animationTypeSupported
+                && !mDataSetHasChangedAfterLayout
+                && predictiveItemAnimationsEnabled();
+    }
+
+    /**
+     * Wrapper around layoutChildren() that handles animating changes caused by layout.
+     * Animations work on the assumption that there are five different kinds of items
+     * in play:
+     * PERSISTENT: items are visible before and after layout
+     * REMOVED: items were visible before layout and were removed by the app
+     * ADDED: items did not exist before layout and were added by the app
+     * DISAPPEARING: items exist in the data set before/after, but changed from
+     * visible to non-visible in the process of layout (they were moved off
+     * screen as a side-effect of other changes)
+     * APPEARING: items exist in the data set before/after, but changed from
+     * non-visible to visible in the process of layout (they were moved on
+     * screen as a side-effect of other changes)
+     * The overall approach figures out what items exist before/after layout and
+     * infers one of the five above states for each of the items. Then the animations
+     * are set up accordingly:
+     * PERSISTENT views are animated via
+     * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
+     * DISAPPEARING views are animated via
+     * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
+     * APPEARING views are animated via
+     * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
+     * and changed views are animated via
+     * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
+     */
+    void dispatchLayout() {
+        if (mAdapter == null) {
+            Log.e(TAG, "No adapter attached; skipping layout");
+            // leave the state in START
+            return;
+        }
+        if (mLayout == null) {
+            Log.e(TAG, "No layout manager attached; skipping layout");
+            // leave the state in START
+            return;
+        }
+        mState.mIsMeasuring = false;
+        if (mState.mLayoutStep == State.STEP_START) {
+            dispatchLayoutStep1();
+            mLayout.setExactMeasureSpecsFrom(this);
+            dispatchLayoutStep2();
+        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
+                || mLayout.getHeight() != getHeight()) {
+            // First 2 steps are done in onMeasure but looks like we have to run again due to
+            // changed size.
+            mLayout.setExactMeasureSpecsFrom(this);
+            dispatchLayoutStep2();
+        } else {
+            // always make sure we sync them (to ensure mode is exact)
+            mLayout.setExactMeasureSpecsFrom(this);
+        }
+        dispatchLayoutStep3();
+    }
+
+    private void saveFocusInfo() {
+        View child = null;
+        if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
+            child = getFocusedChild();
+        }
+
+        final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
+        if (focusedVh == null) {
+            resetFocusInfo();
+        } else {
+            mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
+            // mFocusedItemPosition should hold the current adapter position of the previously
+            // focused item. If the item is removed, we store the previous adapter position of the
+            // removed item.
+            mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
+                    : (focusedVh.isRemoved() ? focusedVh.mOldPosition
+                            : focusedVh.getAdapterPosition());
+            mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
+        }
+    }
+
+    private void resetFocusInfo() {
+        mState.mFocusedItemId = NO_ID;
+        mState.mFocusedItemPosition = NO_POSITION;
+        mState.mFocusedSubChildId = View.NO_ID;
+    }
+
+    /**
+     * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
+     * previously focused item. It first traverses the adapter forward to find a focusable candidate
+     * and if no such candidate is found, it reverses the focus search direction for the items
+     * before the mFocusedItemPosition'th index;
+     * @return The best candidate to request focus on, or null if no such candidate exists. Null
+     * indicates all the existing adapter items are unfocusable.
+     */
+    @Nullable
+    private View findNextViewToFocus() {
+        int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
+                : 0;
+        ViewHolder nextFocus;
+        final int itemCount = mState.getItemCount();
+        for (int i = startFocusSearchIndex; i < itemCount; i++) {
+            nextFocus = findViewHolderForAdapterPosition(i);
+            if (nextFocus == null) {
+                break;
+            }
+            if (nextFocus.itemView.hasFocusable()) {
+                return nextFocus.itemView;
+            }
+        }
+        final int limit = Math.min(itemCount, startFocusSearchIndex);
+        for (int i = limit - 1; i >= 0; i--) {
+            nextFocus = findViewHolderForAdapterPosition(i);
+            if (nextFocus == null) {
+                return null;
+            }
+            if (nextFocus.itemView.hasFocusable()) {
+                return nextFocus.itemView;
+            }
+        }
+        return null;
+    }
+
+    private void recoverFocusFromState() {
+        if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
+                || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
+                || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
+            // No-op if either of these cases happens:
+            // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
+            // before its children and is focused (i.e. it already stole the focus away from its
+            // descendants).
+            return;
+        }
+        // only recover focus if RV itself has the focus or the focused view is hidden
+        if (!isFocused()) {
+            final View focusedChild = getFocusedChild();
+            if (IGNORE_DETACHED_FOCUSED_CHILD
+                    && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
+                // Special handling of API 15-. A focused child can be invalid because mFocus is not
+                // cleared when the child is detached (mParent = null),
+                // This happens because clearFocus on API 15- does not invalidate mFocus of its
+                // parent when this child is detached.
+                // For API 16+, this is not an issue because requestFocus takes care of clearing the
+                // prior detached focused child. For API 15- the problem happens in 2 cases because
+                // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
+                // for the current focused item which calls clearChild or 2. when the prior focused
+                // child is removed, removeDetachedView called in layout step 3 which calls
+                // clearChild. We should ignore this invalid focused child in all our calculations
+                // for the next view to receive focus, and apply the focus recovery logic instead.
+                if (mChildHelper.getChildCount() == 0) {
+                    // No children left. Request focus on the RV itself since one of its children
+                    // was holding focus previously.
+                    requestFocus();
+                    return;
+                }
+            } else if (!mChildHelper.isHidden(focusedChild)) {
+                // If the currently focused child is hidden, apply the focus recovery logic.
+                // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
+                return;
+            }
+        }
+        ViewHolder focusTarget = null;
+        // RV first attempts to locate the previously focused item to request focus on using
+        // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
+        // find the next best candidate to request focus on based on mFocusedItemPosition.
+        if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
+            focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
+        }
+        View viewToFocus = null;
+        if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
+                || !focusTarget.itemView.hasFocusable()) {
+            if (mChildHelper.getChildCount() > 0) {
+                // At this point, RV has focus and either of these conditions are true:
+                // 1. There's no previously focused item either because RV received focused before
+                // layout, or the previously focused item was removed, or RV doesn't have stable IDs
+                // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
+                // focusable. In either of these cases, we make sure that RV still passes down the
+                // focus to one of its focusable children using a best-effort algorithm.
+                viewToFocus = findNextViewToFocus();
+            }
+        } else {
+            // looks like the focused item has been replaced with another view that represents the
+            // same item in the adapter. Request focus on that.
+            viewToFocus = focusTarget.itemView;
+        }
+
+        if (viewToFocus != null) {
+            if (mState.mFocusedSubChildId != NO_ID) {
+                View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
+                if (child != null && child.isFocusable()) {
+                    viewToFocus = child;
+                }
+            }
+            viewToFocus.requestFocus();
+        }
+    }
+
+    private int getDeepestFocusedViewWithId(View view) {
+        int lastKnownId = view.getId();
+        while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
+            view = ((ViewGroup) view).getFocusedChild();
+            final int id = view.getId();
+            if (id != View.NO_ID) {
+                lastKnownId = view.getId();
+            }
+        }
+        return lastKnownId;
+    }
+
+    /**
+     * The first step of a layout where we;
+     * - process adapter updates
+     * - decide which animation should run
+     * - save information about current views
+     * - If necessary, run predictive layout and save its information
+     */
+    private void dispatchLayoutStep1() {
+        mState.assertLayoutStep(State.STEP_START);
+        mState.mIsMeasuring = false;
+        eatRequestLayout();
+        mViewInfoStore.clear();
+        onEnterLayoutOrScroll();
+        processAdapterUpdatesAndSetAnimationFlags();
+        saveFocusInfo();
+        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
+        mItemsAddedOrRemoved = mItemsChanged = false;
+        mState.mInPreLayout = mState.mRunPredictiveAnimations;
+        mState.mItemCount = mAdapter.getItemCount();
+        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
+
+        if (mState.mRunSimpleAnimations) {
+            // Step 0: Find out where all non-removed items are, pre-layout
+            int count = mChildHelper.getChildCount();
+            for (int i = 0; i < count; ++i) {
+                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
+                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
+                    continue;
+                }
+                final ItemHolderInfo animationInfo = mItemAnimator
+                        .recordPreLayoutInformation(mState, holder,
+                                ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
+                                holder.getUnmodifiedPayloads());
+                mViewInfoStore.addToPreLayout(holder, animationInfo);
+                if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
+                        && !holder.shouldIgnore() && !holder.isInvalid()) {
+                    long key = getChangedHolderKey(holder);
+                    // This is NOT the only place where a ViewHolder is added to old change holders
+                    // list. There is another case where:
+                    //    * A VH is currently hidden but not deleted
+                    //    * The hidden item is changed in the adapter
+                    //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
+                    // When this case is detected, RV will un-hide that view and add to the old
+                    // change holders list.
+                    mViewInfoStore.addToOldChangeHolders(key, holder);
+                }
+            }
+        }
+        if (mState.mRunPredictiveAnimations) {
+            // Step 1: run prelayout: This will use the old positions of items. The layout manager
+            // is expected to layout everything, even removed items (though not to add removed
+            // items back to the container). This gives the pre-layout position of APPEARING views
+            // which come into existence as part of the real layout.
+
+            // Save old positions so that LayoutManager can run its mapping logic.
+            saveOldPositions();
+            final boolean didStructureChange = mState.mStructureChanged;
+            mState.mStructureChanged = false;
+            // temporarily disable flag because we are asking for previous layout
+            mLayout.onLayoutChildren(mRecycler, mState);
+            mState.mStructureChanged = didStructureChange;
+
+            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
+                final View child = mChildHelper.getChildAt(i);
+                final ViewHolder viewHolder = getChildViewHolderInt(child);
+                if (viewHolder.shouldIgnore()) {
+                    continue;
+                }
+                if (!mViewInfoStore.isInPreLayout(viewHolder)) {
+                    int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
+                    boolean wasHidden = viewHolder
+                            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
+                    if (!wasHidden) {
+                        flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
+                    }
+                    final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
+                            mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
+                    if (wasHidden) {
+                        recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
+                    } else {
+                        mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
+                    }
+                }
+            }
+            // we don't process disappearing list because they may re-appear in post layout pass.
+            clearOldPositions();
+        } else {
+            clearOldPositions();
+        }
+        onExitLayoutOrScroll();
+        resumeRequestLayout(false);
+        mState.mLayoutStep = State.STEP_LAYOUT;
+    }
+
+    /**
+     * The second layout step where we do the actual layout of the views for the final state.
+     * This step might be run multiple times if necessary (e.g. measure).
+     */
+    private void dispatchLayoutStep2() {
+        eatRequestLayout();
+        onEnterLayoutOrScroll();
+        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
+        mAdapterHelper.consumeUpdatesInOnePass();
+        mState.mItemCount = mAdapter.getItemCount();
+        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
+
+        // Step 2: Run layout
+        mState.mInPreLayout = false;
+        mLayout.onLayoutChildren(mRecycler, mState);
+
+        mState.mStructureChanged = false;
+        mPendingSavedState = null;
+
+        // onLayoutChildren may have caused client code to disable item animations; re-check
+        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
+        mState.mLayoutStep = State.STEP_ANIMATIONS;
+        onExitLayoutOrScroll();
+        resumeRequestLayout(false);
+    }
+
+    /**
+     * The final step of the layout where we save the information about views for animations,
+     * trigger animations and do any necessary cleanup.
+     */
+    private void dispatchLayoutStep3() {
+        mState.assertLayoutStep(State.STEP_ANIMATIONS);
+        eatRequestLayout();
+        onEnterLayoutOrScroll();
+        mState.mLayoutStep = State.STEP_START;
+        if (mState.mRunSimpleAnimations) {
+            // Step 3: Find out where things are now, and process change animations.
+            // traverse list in reverse because we may call animateChange in the loop which may
+            // remove the target view holder.
+            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
+                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
+                if (holder.shouldIgnore()) {
+                    continue;
+                }
+                long key = getChangedHolderKey(holder);
+                final ItemHolderInfo animationInfo = mItemAnimator
+                        .recordPostLayoutInformation(mState, holder);
+                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
+                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
+                    // run a change animation
+
+                    // If an Item is CHANGED but the updated version is disappearing, it creates
+                    // a conflicting case.
+                    // Since a view that is marked as disappearing is likely to be going out of
+                    // bounds, we run a change animation. Both views will be cleaned automatically
+                    // once their animations finish.
+                    // On the other hand, if it is the same view holder instance, we run a
+                    // disappearing animation instead because we are not going to rebind the updated
+                    // VH unless it is enforced by the layout manager.
+                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(
+                            oldChangeViewHolder);
+                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
+                    if (oldDisappearing && oldChangeViewHolder == holder) {
+                        // run disappear animation instead of change
+                        mViewInfoStore.addToPostLayout(holder, animationInfo);
+                    } else {
+                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
+                                oldChangeViewHolder);
+                        // we add and remove so that any post info is merged.
+                        mViewInfoStore.addToPostLayout(holder, animationInfo);
+                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
+                        if (preInfo == null) {
+                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
+                        } else {
+                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
+                                    oldDisappearing, newDisappearing);
+                        }
+                    }
+                } else {
+                    mViewInfoStore.addToPostLayout(holder, animationInfo);
+                }
+            }
+
+            // Step 4: Process view info lists and trigger animations
+            mViewInfoStore.process(mViewInfoProcessCallback);
+        }
+
+        mLayout.removeAndRecycleScrapInt(mRecycler);
+        mState.mPreviousLayoutItemCount = mState.mItemCount;
+        mDataSetHasChangedAfterLayout = false;
+        mState.mRunSimpleAnimations = false;
+
+        mState.mRunPredictiveAnimations = false;
+        mLayout.mRequestedSimpleAnimations = false;
+        if (mRecycler.mChangedScrap != null) {
+            mRecycler.mChangedScrap.clear();
+        }
+        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
+            // Initial prefetch has expanded cache, so reset until next prefetch.
+            // This prevents initial prefetches from expanding the cache permanently.
+            mLayout.mPrefetchMaxCountObserved = 0;
+            mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
+            mRecycler.updateViewCacheSize();
+        }
+
+        mLayout.onLayoutCompleted(mState);
+        onExitLayoutOrScroll();
+        resumeRequestLayout(false);
+        mViewInfoStore.clear();
+        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
+            dispatchOnScrolled(0, 0);
+        }
+        recoverFocusFromState();
+        resetFocusInfo();
+    }
+
+    /**
+     * This handles the case where there is an unexpected VH missing in the pre-layout map.
+     * <p>
+     * We might be able to detect the error in the application which will help the developer to
+     * resolve the issue.
+     * <p>
+     * If it is not an expected error, we at least print an error to notify the developer and ignore
+     * the animation.
+     *
+     * https://code.google.com/p/android/issues/detail?id=193958
+     *
+     * @param key The change key
+     * @param holder Current ViewHolder
+     * @param oldChangeViewHolder Changed ViewHolder
+     */
+    private void handleMissingPreInfoForChangeError(long key,
+            ViewHolder holder, ViewHolder oldChangeViewHolder) {
+        // check if two VH have the same key, if so, print that as an error
+        final int childCount = mChildHelper.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View view = mChildHelper.getChildAt(i);
+            ViewHolder other = getChildViewHolderInt(view);
+            if (other == holder) {
+                continue;
+            }
+            final long otherKey = getChangedHolderKey(other);
+            if (otherKey == key) {
+                if (mAdapter != null && mAdapter.hasStableIds()) {
+                    throw new IllegalStateException("Two different ViewHolders have the same stable"
+                            + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
+                            + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
+                } else {
+                    throw new IllegalStateException("Two different ViewHolders have the same change"
+                            + " ID. This might happen due to inconsistent Adapter update events or"
+                            + " if the LayoutManager lays out the same View multiple times."
+                            + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
+                }
+            }
+        }
+        // Very unlikely to happen but if it does, notify the developer.
+        Log.e(TAG, "Problem while matching changed view holders with the new"
+                + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
+                + " cannot be found but it is necessary for " + holder);
+    }
+
+    /**
+     * Records the animation information for a view holder that was bounced from hidden list. It
+     * also clears the bounce back flag.
+     */
+    void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
+            ItemHolderInfo animationInfo) {
+        // looks like this view bounced back from hidden list!
+        viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
+        if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
+                && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
+            long key = getChangedHolderKey(viewHolder);
+            mViewInfoStore.addToOldChangeHolders(key, viewHolder);
+        }
+        mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
+    }
+
+    private void findMinMaxChildLayoutPositions(int[] into) {
+        final int count = mChildHelper.getChildCount();
+        if (count == 0) {
+            into[0] = NO_POSITION;
+            into[1] = NO_POSITION;
+            return;
+        }
+        int minPositionPreLayout = Integer.MAX_VALUE;
+        int maxPositionPreLayout = Integer.MIN_VALUE;
+        for (int i = 0; i < count; ++i) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
+            if (holder.shouldIgnore()) {
+                continue;
+            }
+            final int pos = holder.getLayoutPosition();
+            if (pos < minPositionPreLayout) {
+                minPositionPreLayout = pos;
+            }
+            if (pos > maxPositionPreLayout) {
+                maxPositionPreLayout = pos;
+            }
+        }
+        into[0] = minPositionPreLayout;
+        into[1] = maxPositionPreLayout;
+    }
+
+    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
+        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
+        return mMinMaxLayoutPositions[0] != minPositionPreLayout
+                || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
+    }
+
+    @Override
+    protected void removeDetachedView(View child, boolean animate) {
+        ViewHolder vh = getChildViewHolderInt(child);
+        if (vh != null) {
+            if (vh.isTmpDetached()) {
+                vh.clearTmpDetachFlag();
+            } else if (!vh.shouldIgnore()) {
+                throw new IllegalArgumentException("Called removeDetachedView with a view which"
+                        + " is not flagged as tmp detached." + vh);
+            }
+        }
+        dispatchChildDetached(child);
+        super.removeDetachedView(child, animate);
+    }
+
+    /**
+     * Returns a unique key to be used while handling change animations.
+     * It might be child's position or stable id depending on the adapter type.
+     */
+    long getChangedHolderKey(ViewHolder holder) {
+        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
+    }
+
+    void animateAppearance(@NonNull ViewHolder itemHolder,
+            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
+        itemHolder.setIsRecyclable(false);
+        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
+            postAnimationRunner();
+        }
+    }
+
+    void animateDisappearance(@NonNull ViewHolder holder,
+            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
+        addAnimatingView(holder);
+        holder.setIsRecyclable(false);
+        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
+            postAnimationRunner();
+        }
+    }
+
+    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
+            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
+            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
+        oldHolder.setIsRecyclable(false);
+        if (oldHolderDisappearing) {
+            addAnimatingView(oldHolder);
+        }
+        if (oldHolder != newHolder) {
+            if (newHolderDisappearing) {
+                addAnimatingView(newHolder);
+            }
+            oldHolder.mShadowedHolder = newHolder;
+            // old holder should disappear after animation ends
+            addAnimatingView(oldHolder);
+            mRecycler.unscrapView(oldHolder);
+            newHolder.setIsRecyclable(false);
+            newHolder.mShadowingHolder = oldHolder;
+        }
+        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
+            postAnimationRunner();
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        Trace.beginSection(TRACE_ON_LAYOUT_TAG);
+        dispatchLayout();
+        Trace.endSection();
+        mFirstLayoutComplete = true;
+    }
+
+    @Override
+    public void requestLayout() {
+        if (mEatRequestLayout == 0 && !mLayoutFrozen) {
+            super.requestLayout();
+        } else {
+            mLayoutRequestEaten = true;
+        }
+    }
+
+    void markItemDecorInsetsDirty() {
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = mChildHelper.getUnfilteredChildAt(i);
+            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
+        }
+        mRecycler.markItemDecorInsetsDirty();
+    }
+
+    @Override
+    public void draw(Canvas c) {
+        super.draw(c);
+
+        final int count = mItemDecorations.size();
+        for (int i = 0; i < count; i++) {
+            mItemDecorations.get(i).onDrawOver(c, this, mState);
+        }
+        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
+        // need find children closest to edges. Not sure if it is worth the effort.
+        boolean needsInvalidate = false;
+        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
+            final int restore = c.save();
+            final int padding = mClipToPadding ? getPaddingBottom() : 0;
+            c.rotate(270);
+            c.translate(-getHeight() + padding, 0);
+            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
+            c.restoreToCount(restore);
+        }
+        if (mTopGlow != null && !mTopGlow.isFinished()) {
+            final int restore = c.save();
+            if (mClipToPadding) {
+                c.translate(getPaddingLeft(), getPaddingTop());
+            }
+            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
+            c.restoreToCount(restore);
+        }
+        if (mRightGlow != null && !mRightGlow.isFinished()) {
+            final int restore = c.save();
+            final int width = getWidth();
+            final int padding = mClipToPadding ? getPaddingTop() : 0;
+            c.rotate(90);
+            c.translate(-padding, -width);
+            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
+            c.restoreToCount(restore);
+        }
+        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
+            final int restore = c.save();
+            c.rotate(180);
+            if (mClipToPadding) {
+                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
+            } else {
+                c.translate(-getWidth(), -getHeight());
+            }
+            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
+            c.restoreToCount(restore);
+        }
+
+        // If some views are animating, ItemDecorators are likely to move/change with them.
+        // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
+        // display lists are not invalidated.
+        if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
+                && mItemAnimator.isRunning()) {
+            needsInvalidate = true;
+        }
+
+        if (needsInvalidate) {
+            postInvalidateOnAnimation();
+        }
+    }
+
+    @Override
+    public void onDraw(Canvas c) {
+        super.onDraw(c);
+
+        final int count = mItemDecorations.size();
+        for (int i = 0; i < count; i++) {
+            mItemDecorations.get(i).onDraw(c, this, mState);
+        }
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+        if (mLayout == null) {
+            throw new IllegalStateException("RecyclerView has no LayoutManager");
+        }
+        return mLayout.generateDefaultLayoutParams();
+    }
+
+    @Override
+    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+        if (mLayout == null) {
+            throw new IllegalStateException("RecyclerView has no LayoutManager");
+        }
+        return mLayout.generateLayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        if (mLayout == null) {
+            throw new IllegalStateException("RecyclerView has no LayoutManager");
+        }
+        return mLayout.generateLayoutParams(p);
+    }
+
+    /**
+     * Returns true if RecyclerView is currently running some animations.
+     * <p>
+     * If you want to be notified when animations are finished, use
+     * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
+     *
+     * @return True if there are some item animations currently running or waiting to be started.
+     */
+    public boolean isAnimating() {
+        return mItemAnimator != null && mItemAnimator.isRunning();
+    }
+
+    void saveOldPositions() {
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
+                throw new IllegalStateException("view holder cannot have position -1 unless it"
+                        + " is removed");
+            }
+            if (!holder.shouldIgnore()) {
+                holder.saveOldPosition();
+            }
+        }
+    }
+
+    void clearOldPositions() {
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (!holder.shouldIgnore()) {
+                holder.clearOldPosition();
+            }
+        }
+        mRecycler.clearOldPositions();
+    }
+
+    void offsetPositionRecordsForMove(int from, int to) {
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        final int start, end, inBetweenOffset;
+        if (from < to) {
+            start = from;
+            end = to;
+            inBetweenOffset = -1;
+        } else {
+            start = to;
+            end = from;
+            inBetweenOffset = 1;
+        }
+
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
+                continue;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
+                        + holder);
+            }
+            if (holder.mPosition == from) {
+                holder.offsetPosition(to - from, false);
+            } else {
+                holder.offsetPosition(inBetweenOffset, false);
+            }
+
+            mState.mStructureChanged = true;
+        }
+        mRecycler.offsetPositionRecordsForMove(from, to);
+        requestLayout();
+    }
+
+    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
+                if (DEBUG) {
+                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
+                            + holder + " now at position " + (holder.mPosition + itemCount));
+                }
+                holder.offsetPosition(itemCount, false);
+                mState.mStructureChanged = true;
+            }
+        }
+        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
+        requestLayout();
+    }
+
+    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
+            boolean applyToPreLayout) {
+        final int positionEnd = positionStart + itemCount;
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (holder != null && !holder.shouldIgnore()) {
+                if (holder.mPosition >= positionEnd) {
+                    if (DEBUG) {
+                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
+                                + " holder " + holder + " now at position "
+                                + (holder.mPosition - itemCount));
+                    }
+                    holder.offsetPosition(-itemCount, applyToPreLayout);
+                    mState.mStructureChanged = true;
+                } else if (holder.mPosition >= positionStart) {
+                    if (DEBUG) {
+                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
+                                + " holder " + holder + " now REMOVED");
+                    }
+                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
+                            applyToPreLayout);
+                    mState.mStructureChanged = true;
+                }
+            }
+        }
+        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
+        requestLayout();
+    }
+
+    /**
+     * Rebind existing views for the given range, or create as needed.
+     *
+     * @param positionStart Adapter position to start at
+     * @param itemCount Number of views that must explicitly be rebound
+     */
+    void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        final int positionEnd = positionStart + itemCount;
+
+        for (int i = 0; i < childCount; i++) {
+            final View child = mChildHelper.getUnfilteredChildAt(i);
+            final ViewHolder holder = getChildViewHolderInt(child);
+            if (holder == null || holder.shouldIgnore()) {
+                continue;
+            }
+            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
+                // We re-bind these view holders after pre-processing is complete so that
+                // ViewHolders have their final positions assigned.
+                holder.addFlags(ViewHolder.FLAG_UPDATE);
+                holder.addChangePayload(payload);
+                // lp cannot be null since we get ViewHolder from it.
+                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
+            }
+        }
+        mRecycler.viewRangeUpdate(positionStart, itemCount);
+    }
+
+    boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
+        return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
+                viewHolder.getUnmodifiedPayloads());
+    }
+
+
+    /**
+     * Call this method to signal that *all* adapter content has changed (generally, because of
+     * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
+     * be discarded or animated. Note that this work is deferred because RecyclerView requires a
+     * layout to resolve non-incremental changes to the data set.
+     *
+     * Attached items are labeled as position unknown, and may no longer be cached.
+     *
+     * It is still possible for items to be prefetched while mDataSetHasChangedAfterLayout == true,
+     * so calling this method *must* be associated with marking the cache invalid, so that the
+     * only valid items that remain in the cache, once layout occurs, are prefetched items.
+     */
+    void setDataSetChangedAfterLayout() {
+        if (mDataSetHasChangedAfterLayout) {
+            return;
+        }
+        mDataSetHasChangedAfterLayout = true;
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (holder != null && !holder.shouldIgnore()) {
+                holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
+            }
+        }
+        mRecycler.setAdapterPositionsAsUnknown();
+
+        // immediately mark all views as invalid, so prefetched views can be
+        // differentiated from views bound to previous data set - both in children, and cache
+        markKnownViewsInvalid();
+    }
+
+    /**
+     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
+     * data change event.
+     */
+    void markKnownViewsInvalid() {
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (holder != null && !holder.shouldIgnore()) {
+                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
+            }
+        }
+        markItemDecorInsetsDirty();
+        mRecycler.markKnownViewsInvalid();
+    }
+
+    /**
+     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
+     * will trigger a {@link #requestLayout()} call.
+     */
+    public void invalidateItemDecorations() {
+        if (mItemDecorations.size() == 0) {
+            return;
+        }
+        if (mLayout != null) {
+            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
+                    + " or layout");
+        }
+        markItemDecorInsetsDirty();
+        requestLayout();
+    }
+
+    /**
+     * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
+     * focus even if the View representing the Item is replaced during a layout calculation.
+     * <p>
+     * By default, this value is {@code true}.
+     *
+     * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
+     * focus.
+     *
+     * @see #setPreserveFocusAfterLayout(boolean)
+     */
+    public boolean getPreserveFocusAfterLayout() {
+        return mPreserveFocusAfterLayout;
+    }
+
+    /**
+     * Set whether the RecyclerView should try to keep the same Item focused after a layout
+     * calculation or not.
+     * <p>
+     * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
+     * views may lose focus during a layout calculation as their state changes or they are replaced
+     * with another view due to type change or animation. In these cases, RecyclerView can request
+     * focus on the new view automatically.
+     *
+     * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
+     *                                 layout calculations. Defaults to true.
+     *
+     * @see #getPreserveFocusAfterLayout()
+     */
+    public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
+        mPreserveFocusAfterLayout = preserveFocusAfterLayout;
+    }
+
+    /**
+     * Retrieve the {@link ViewHolder} for the given child view.
+     *
+     * @param child Child of this RecyclerView to query for its ViewHolder
+     * @return The child view's ViewHolder
+     */
+    public ViewHolder getChildViewHolder(View child) {
+        final ViewParent parent = child.getParent();
+        if (parent != null && parent != this) {
+            throw new IllegalArgumentException("View " + child + " is not a direct child of "
+                    + this);
+        }
+        return getChildViewHolderInt(child);
+    }
+
+    /**
+     * Traverses the ancestors of the given view and returns the item view that contains it and
+     * also a direct child of the RecyclerView. This returned view can be used to get the
+     * ViewHolder by calling {@link #getChildViewHolder(View)}.
+     *
+     * @param view The view that is a descendant of the RecyclerView.
+     *
+     * @return The direct child of the RecyclerView which contains the given view or null if the
+     * provided view is not a descendant of this RecyclerView.
+     *
+     * @see #getChildViewHolder(View)
+     * @see #findContainingViewHolder(View)
+     */
+    @Nullable
+    public View findContainingItemView(View view) {
+        ViewParent parent = view.getParent();
+        while (parent != null && parent != this && parent instanceof View) {
+            view = (View) parent;
+            parent = view.getParent();
+        }
+        return parent == this ? view : null;
+    }
+
+    /**
+     * Returns the ViewHolder that contains the given view.
+     *
+     * @param view The view that is a descendant of the RecyclerView.
+     *
+     * @return The ViewHolder that contains the given view or null if the provided view is not a
+     * descendant of this RecyclerView.
+     */
+    @Nullable
+    public ViewHolder findContainingViewHolder(View view) {
+        View itemView = findContainingItemView(view);
+        return itemView == null ? null : getChildViewHolder(itemView);
+    }
+
+
+    static ViewHolder getChildViewHolderInt(View child) {
+        if (child == null) {
+            return null;
+        }
+        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
+    }
+
+    /**
+     * @deprecated use {@link #getChildAdapterPosition(View)} or
+     * {@link #getChildLayoutPosition(View)}.
+     */
+    @Deprecated
+    public int getChildPosition(View child) {
+        return getChildAdapterPosition(child);
+    }
+
+    /**
+     * Return the adapter position that the given child view corresponds to.
+     *
+     * @param child Child View to query
+     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
+     */
+    public int getChildAdapterPosition(View child) {
+        final ViewHolder holder = getChildViewHolderInt(child);
+        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
+    }
+
+    /**
+     * Return the adapter position of the given child view as of the latest completed layout pass.
+     * <p>
+     * This position may not be equal to Item's adapter position if there are pending changes
+     * in the adapter which have not been reflected to the layout yet.
+     *
+     * @param child Child View to query
+     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
+     * the View is representing a removed item.
+     */
+    public int getChildLayoutPosition(View child) {
+        final ViewHolder holder = getChildViewHolderInt(child);
+        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
+    }
+
+    /**
+     * Return the stable item id that the given child view corresponds to.
+     *
+     * @param child Child View to query
+     * @return Item id corresponding to the given view or {@link #NO_ID}
+     */
+    public long getChildItemId(View child) {
+        if (mAdapter == null || !mAdapter.hasStableIds()) {
+            return NO_ID;
+        }
+        final ViewHolder holder = getChildViewHolderInt(child);
+        return holder != null ? holder.getItemId() : NO_ID;
+    }
+
+    /**
+     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
+     * {@link #findViewHolderForAdapterPosition(int)}
+     */
+    @Deprecated
+    public ViewHolder findViewHolderForPosition(int position) {
+        return findViewHolderForPosition(position, false);
+    }
+
+    /**
+     * Return the ViewHolder for the item in the given position of the data set as of the latest
+     * layout pass.
+     * <p>
+     * This method checks only the children of RecyclerView. If the item at the given
+     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
+     * <p>
+     * Note that when Adapter contents change, ViewHolder positions are not updated until the
+     * next layout calculation. If there are pending adapter updates, the return value of this
+     * method may not match your adapter contents. You can use
+     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
+     * <p>
+     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
+     * with the same layout position representing the same Item. In this case, the updated
+     * ViewHolder will be returned.
+     *
+     * @param position The position of the item in the data set of the adapter
+     * @return The ViewHolder at <code>position</code> or null if there is no such item
+     */
+    public ViewHolder findViewHolderForLayoutPosition(int position) {
+        return findViewHolderForPosition(position, false);
+    }
+
+    /**
+     * Return the ViewHolder for the item in the given position of the data set. Unlike
+     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
+     * adapter changes that may not be reflected to the layout yet. On the other hand, if
+     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
+     * calculated yet, this method will return <code>null</code> since the new positions of views
+     * are unknown until the layout is calculated.
+     * <p>
+     * This method checks only the children of RecyclerView. If the item at the given
+     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
+     * <p>
+     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
+     * representing the same Item. In this case, the updated ViewHolder will be returned.
+     *
+     * @param position The position of the item in the data set of the adapter
+     * @return The ViewHolder at <code>position</code> or null if there is no such item
+     */
+    public ViewHolder findViewHolderForAdapterPosition(int position) {
+        if (mDataSetHasChangedAfterLayout) {
+            return null;
+        }
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        // hidden VHs are not preferred but if that is the only one we find, we rather return it
+        ViewHolder hidden = null;
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (holder != null && !holder.isRemoved()
+                    && getAdapterPositionFor(holder) == position) {
+                if (mChildHelper.isHidden(holder.itemView)) {
+                    hidden = holder;
+                } else {
+                    return holder;
+                }
+            }
+        }
+        return hidden;
+    }
+
+    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        ViewHolder hidden = null;
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (holder != null && !holder.isRemoved()) {
+                if (checkNewPosition) {
+                    if (holder.mPosition != position) {
+                        continue;
+                    }
+                } else if (holder.getLayoutPosition() != position) {
+                    continue;
+                }
+                if (mChildHelper.isHidden(holder.itemView)) {
+                    hidden = holder;
+                } else {
+                    return holder;
+                }
+            }
+        }
+        // This method should not query cached views. It creates a problem during adapter updates
+        // when we are dealing with already laid out views. Also, for the public method, it is more
+        // reasonable to return null if position is not laid out.
+        return hidden;
+    }
+
+    /**
+     * Return the ViewHolder for the item with the given id. The RecyclerView must
+     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
+     * return a non-null value.
+     * <p>
+     * This method checks only the children of RecyclerView. If the item with the given
+     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
+     *
+     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
+     * same id. In this case, the updated ViewHolder will be returned.
+     *
+     * @param id The id for the requested item
+     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
+     */
+    public ViewHolder findViewHolderForItemId(long id) {
+        if (mAdapter == null || !mAdapter.hasStableIds()) {
+            return null;
+        }
+        final int childCount = mChildHelper.getUnfilteredChildCount();
+        ViewHolder hidden = null;
+        for (int i = 0; i < childCount; i++) {
+            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
+            if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
+                if (mChildHelper.isHidden(holder.itemView)) {
+                    hidden = holder;
+                } else {
+                    return holder;
+                }
+            }
+        }
+        return hidden;
+    }
+
+    /**
+     * Find the topmost view under the given point.
+     *
+     * @param x Horizontal position in pixels to search
+     * @param y Vertical position in pixels to search
+     * @return The child view under (x, y) or null if no matching child is found
+     */
+    public View findChildViewUnder(float x, float y) {
+        final int count = mChildHelper.getChildCount();
+        for (int i = count - 1; i >= 0; i--) {
+            final View child = mChildHelper.getChildAt(i);
+            final float translationX = child.getTranslationX();
+            final float translationY = child.getTranslationY();
+            if (x >= child.getLeft() + translationX
+                    && x <= child.getRight() + translationX
+                    && y >= child.getTop() + translationY
+                    && y <= child.getBottom() + translationY) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        return super.drawChild(canvas, child, drawingTime);
+    }
+
+    /**
+     * Offset the bounds of all child views by <code>dy</code> pixels.
+     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
+     *
+     * @param dy Vertical pixel offset to apply to the bounds of all child views
+     */
+    public void offsetChildrenVertical(int dy) {
+        final int childCount = mChildHelper.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
+        }
+    }
+
+    /**
+     * Called when an item view is attached to this RecyclerView.
+     *
+     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
+     * of child views as they become attached. This will be called before a
+     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
+     * changes.</p>
+     *
+     * @param child Child view that is now attached to this RecyclerView and its associated window
+     */
+    public void onChildAttachedToWindow(View child) {
+    }
+
+    /**
+     * Called when an item view is detached from this RecyclerView.
+     *
+     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
+     * of child views as they become detached. This will be called as a
+     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
+     *
+     * @param child Child view that is now detached from this RecyclerView and its associated window
+     */
+    public void onChildDetachedFromWindow(View child) {
+    }
+
+    /**
+     * Offset the bounds of all child views by <code>dx</code> pixels.
+     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
+     *
+     * @param dx Horizontal pixel offset to apply to the bounds of all child views
+     */
+    public void offsetChildrenHorizontal(int dx) {
+        final int childCount = mChildHelper.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
+        }
+    }
+
+    /**
+     * Returns the bounds of the view including its decoration and margins.
+     *
+     * @param view The view element to check
+     * @param outBounds A rect that will receive the bounds of the element including its
+     *                  decoration and margins.
+     */
+    public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
+        getDecoratedBoundsWithMarginsInt(view, outBounds);
+    }
+
+    static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
+        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+        final Rect insets = lp.mDecorInsets;
+        outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
+                view.getTop() - insets.top - lp.topMargin,
+                view.getRight() + insets.right + lp.rightMargin,
+                view.getBottom() + insets.bottom + lp.bottomMargin);
+    }
+
+    Rect getItemDecorInsetsForChild(View child) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        if (!lp.mInsetsDirty) {
+            return lp.mDecorInsets;
+        }
+
+        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
+            // changed/invalid items should not be updated until they are rebound.
+            return lp.mDecorInsets;
+        }
+        final Rect insets = lp.mDecorInsets;
+        insets.set(0, 0, 0, 0);
+        final int decorCount = mItemDecorations.size();
+        for (int i = 0; i < decorCount; i++) {
+            mTempRect.set(0, 0, 0, 0);
+            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
+            insets.left += mTempRect.left;
+            insets.top += mTempRect.top;
+            insets.right += mTempRect.right;
+            insets.bottom += mTempRect.bottom;
+        }
+        lp.mInsetsDirty = false;
+        return insets;
+    }
+
+    /**
+     * Called when the scroll position of this RecyclerView changes. Subclasses should use
+     * this method to respond to scrolling within the adapter's data set instead of an explicit
+     * listener.
+     *
+     * <p>This method will always be invoked before listeners. If a subclass needs to perform
+     * any additional upkeep or bookkeeping after scrolling but before listeners run,
+     * this is a good place to do so.</p>
+     *
+     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
+     * the distance scrolled in either direction within the adapter's data set instead of absolute
+     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
+     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
+     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
+     * do not correspond to the data set scroll position. However, some subclasses may choose
+     * to use these fields as special offsets.</p>
+     *
+     * @param dx horizontal distance scrolled in pixels
+     * @param dy vertical distance scrolled in pixels
+     */
+    public void onScrolled(int dx, int dy) {
+        // Do nothing
+    }
+
+    void dispatchOnScrolled(int hresult, int vresult) {
+        mDispatchScrollCounter++;
+        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
+        // but some general-purpose code may choose to respond to changes this way.
+        final int scrollX = getScrollX();
+        final int scrollY = getScrollY();
+        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
+
+        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
+        onScrolled(hresult, vresult);
+
+        // Invoke listeners last. Subclassed view methods always handle the event first.
+        // All internal state is consistent by the time listeners are invoked.
+        if (mScrollListener != null) {
+            mScrollListener.onScrolled(this, hresult, vresult);
+        }
+        if (mScrollListeners != null) {
+            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
+                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
+            }
+        }
+        mDispatchScrollCounter--;
+    }
+
+    /**
+     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
+     * method to respond to state changes instead of an explicit listener.
+     *
+     * <p>This method will always be invoked before listeners, but after the LayoutManager
+     * responds to the scroll state change.</p>
+     *
+     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
+     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
+     */
+    public void onScrollStateChanged(int state) {
+        // Do nothing
+    }
+
+    void dispatchOnScrollStateChanged(int state) {
+        // Let the LayoutManager go first; this allows it to bring any properties into
+        // a consistent state before the RecyclerView subclass responds.
+        if (mLayout != null) {
+            mLayout.onScrollStateChanged(state);
+        }
+
+        // Let the RecyclerView subclass handle this event next; any LayoutManager property
+        // changes will be reflected by this time.
+        onScrollStateChanged(state);
+
+        // Listeners go last. All other internal state is consistent by this point.
+        if (mScrollListener != null) {
+            mScrollListener.onScrollStateChanged(this, state);
+        }
+        if (mScrollListeners != null) {
+            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
+                mScrollListeners.get(i).onScrollStateChanged(this, state);
+            }
+        }
+    }
+
+    /**
+     * Returns whether there are pending adapter updates which are not yet applied to the layout.
+     * <p>
+     * If this method returns <code>true</code>, it means that what user is currently seeing may not
+     * reflect them adapter contents (depending on what has changed).
+     * You may use this information to defer or cancel some operations.
+     * <p>
+     * This method returns true if RecyclerView has not yet calculated the first layout after it is
+     * attached to the Window or the Adapter has been replaced.
+     *
+     * @return True if there are some adapter updates which are not yet reflected to layout or false
+     * if layout is up to date.
+     */
+    public boolean hasPendingAdapterUpdates() {
+        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
+                || mAdapterHelper.hasPendingUpdates();
+    }
+
+    class ViewFlinger implements Runnable {
+        private int mLastFlingX;
+        private int mLastFlingY;
+        private OverScroller mScroller;
+        Interpolator mInterpolator = sQuinticInterpolator;
+
+
+        // When set to true, postOnAnimation callbacks are delayed until the run method completes
+        private boolean mEatRunOnAnimationRequest = false;
+
+        // Tracks if postAnimationCallback should be re-attached when it is done
+        private boolean mReSchedulePostAnimationCallback = false;
+
+        ViewFlinger() {
+            mScroller = new OverScroller(getContext(), sQuinticInterpolator);
+        }
+
+        @Override
+        public void run() {
+            if (mLayout == null) {
+                stop();
+                return; // no layout, cannot scroll.
+            }
+            disableRunOnAnimationRequests();
+            consumePendingUpdateOperations();
+            // keep a local reference so that if it is changed during onAnimation method, it won't
+            // cause unexpected behaviors
+            final OverScroller scroller = mScroller;
+            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
+            if (scroller.computeScrollOffset()) {
+                final int x = scroller.getCurrX();
+                final int y = scroller.getCurrY();
+                final int dx = x - mLastFlingX;
+                final int dy = y - mLastFlingY;
+                int hresult = 0;
+                int vresult = 0;
+                mLastFlingX = x;
+                mLastFlingY = y;
+                int overscrollX = 0, overscrollY = 0;
+                if (mAdapter != null) {
+                    eatRequestLayout();
+                    onEnterLayoutOrScroll();
+                    Trace.beginSection(TRACE_SCROLL_TAG);
+                    if (dx != 0) {
+                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
+                        overscrollX = dx - hresult;
+                    }
+                    if (dy != 0) {
+                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
+                        overscrollY = dy - vresult;
+                    }
+                    Trace.endSection();
+                    repositionShadowingViews();
+
+                    onExitLayoutOrScroll();
+                    resumeRequestLayout(false);
+
+                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
+                            && smoothScroller.isRunning()) {
+                        final int adapterSize = mState.getItemCount();
+                        if (adapterSize == 0) {
+                            smoothScroller.stop();
+                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
+                            smoothScroller.setTargetPosition(adapterSize - 1);
+                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
+                        } else {
+                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
+                        }
+                    }
+                }
+                if (!mItemDecorations.isEmpty()) {
+                    invalidate();
+                }
+                if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
+                    considerReleasingGlowsOnScroll(dx, dy);
+                }
+                if (overscrollX != 0 || overscrollY != 0) {
+                    final int vel = (int) scroller.getCurrVelocity();
+
+                    int velX = 0;
+                    if (overscrollX != x) {
+                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
+                    }
+
+                    int velY = 0;
+                    if (overscrollY != y) {
+                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
+                    }
+
+                    if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
+                        absorbGlows(velX, velY);
+                    }
+                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
+                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
+                        scroller.abortAnimation();
+                    }
+                }
+                if (hresult != 0 || vresult != 0) {
+                    dispatchOnScrolled(hresult, vresult);
+                }
+
+                if (!awakenScrollBars()) {
+                    invalidate();
+                }
+
+                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
+                        && vresult == dy;
+                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
+                        && hresult == dx;
+                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
+                        || fullyConsumedVertical;
+
+                if (scroller.isFinished() || !fullyConsumedAny) {
+                    setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
+                    if (ALLOW_THREAD_GAP_WORK) {
+                        mPrefetchRegistry.clearPrefetchPositions();
+                    }
+                } else {
+                    postOnAnimation();
+                    if (mGapWorker != null) {
+                        mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
+                    }
+                }
+            }
+            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
+            if (smoothScroller != null) {
+                if (smoothScroller.isPendingInitialRun()) {
+                    smoothScroller.onAnimation(0, 0);
+                }
+                if (!mReSchedulePostAnimationCallback) {
+                    smoothScroller.stop(); //stop if it does not trigger any scroll
+                }
+            }
+            enableRunOnAnimationRequests();
+        }
+
+        private void disableRunOnAnimationRequests() {
+            mReSchedulePostAnimationCallback = false;
+            mEatRunOnAnimationRequest = true;
+        }
+
+        private void enableRunOnAnimationRequests() {
+            mEatRunOnAnimationRequest = false;
+            if (mReSchedulePostAnimationCallback) {
+                postOnAnimation();
+            }
+        }
+
+        void postOnAnimation() {
+            if (mEatRunOnAnimationRequest) {
+                mReSchedulePostAnimationCallback = true;
+            } else {
+                removeCallbacks(this);
+                RecyclerView.this.postOnAnimation(this);
+            }
+        }
+
+        public void fling(int velocityX, int velocityY) {
+            setScrollState(SCROLL_STATE_SETTLING);
+            mLastFlingX = mLastFlingY = 0;
+            mScroller.fling(0, 0, velocityX, velocityY,
+                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
+            postOnAnimation();
+        }
+
+        public void smoothScrollBy(int dx, int dy) {
+            smoothScrollBy(dx, dy, 0, 0);
+        }
+
+        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
+            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
+        }
+
+        private float distanceInfluenceForSnapDuration(float f) {
+            f -= 0.5f; // center the values about 0.
+            f *= 0.3f * Math.PI / 2.0f;
+            return (float) Math.sin(f);
+        }
+
+        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
+            final int absDx = Math.abs(dx);
+            final int absDy = Math.abs(dy);
+            final boolean horizontal = absDx > absDy;
+            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
+            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
+            final int containerSize = horizontal ? getWidth() : getHeight();
+            final int halfContainerSize = containerSize / 2;
+            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
+            final float distance = halfContainerSize + halfContainerSize
+                    * distanceInfluenceForSnapDuration(distanceRatio);
+
+            final int duration;
+            if (velocity > 0) {
+                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
+            } else {
+                float absDelta = (float) (horizontal ? absDx : absDy);
+                duration = (int) (((absDelta / containerSize) + 1) * 300);
+            }
+            return Math.min(duration, MAX_SCROLL_DURATION);
+        }
+
+        public void smoothScrollBy(int dx, int dy, int duration) {
+            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
+        }
+
+        public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
+            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
+                    interpolator == null ? sQuinticInterpolator : interpolator);
+        }
+
+        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
+            if (mInterpolator != interpolator) {
+                mInterpolator = interpolator;
+                mScroller = new OverScroller(getContext(), interpolator);
+            }
+            setScrollState(SCROLL_STATE_SETTLING);
+            mLastFlingX = mLastFlingY = 0;
+            mScroller.startScroll(0, 0, dx, dy, duration);
+            postOnAnimation();
+        }
+
+        public void stop() {
+            removeCallbacks(this);
+            mScroller.abortAnimation();
+        }
+
+    }
+
+    void repositionShadowingViews() {
+        // Fix up shadow views used by change animations
+        int count = mChildHelper.getChildCount();
+        for (int i = 0; i < count; i++) {
+            View view = mChildHelper.getChildAt(i);
+            ViewHolder holder = getChildViewHolder(view);
+            if (holder != null && holder.mShadowingHolder != null) {
+                View shadowingView = holder.mShadowingHolder.itemView;
+                int left = view.getLeft();
+                int top = view.getTop();
+                if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
+                    shadowingView.layout(left, top,
+                            left + shadowingView.getWidth(),
+                            top + shadowingView.getHeight());
+                }
+            }
+        }
+    }
+
+    private class RecyclerViewDataObserver extends AdapterDataObserver {
+        RecyclerViewDataObserver() {
+        }
+
+        @Override
+        public void onChanged() {
+            assertNotInLayoutOrScroll(null);
+            mState.mStructureChanged = true;
+
+            setDataSetChangedAfterLayout();
+            if (!mAdapterHelper.hasPendingUpdates()) {
+                requestLayout();
+            }
+        }
+
+        @Override
+        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
+            assertNotInLayoutOrScroll(null);
+            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
+                triggerUpdateProcessor();
+            }
+        }
+
+        @Override
+        public void onItemRangeInserted(int positionStart, int itemCount) {
+            assertNotInLayoutOrScroll(null);
+            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
+                triggerUpdateProcessor();
+            }
+        }
+
+        @Override
+        public void onItemRangeRemoved(int positionStart, int itemCount) {
+            assertNotInLayoutOrScroll(null);
+            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
+                triggerUpdateProcessor();
+            }
+        }
+
+        @Override
+        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+            assertNotInLayoutOrScroll(null);
+            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
+                triggerUpdateProcessor();
+            }
+        }
+
+        void triggerUpdateProcessor() {
+            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
+                RecyclerView.this.postOnAnimation(mUpdateChildViewsRunnable);
+            } else {
+                mAdapterUpdateDuringMeasure = true;
+                requestLayout();
+            }
+        }
+    }
+
+    /**
+     * RecycledViewPool lets you share Views between multiple RecyclerViews.
+     * <p>
+     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
+     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
+     * <p>
+     * RecyclerView automatically creates a pool for itself if you don't provide one.
+     *
+     */
+    public static class RecycledViewPool {
+        private static final int DEFAULT_MAX_SCRAP = 5;
+
+        /**
+         * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
+         *
+         * Note that this tracks running averages of create/bind time across all RecyclerViews
+         * (and, indirectly, Adapters) that use this pool.
+         *
+         * 1) This enables us to track average create and bind times across multiple adapters. Even
+         * though create (and especially bind) may behave differently for different Adapter
+         * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
+         *
+         * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
+         * false for all other views of its type for the same deadline. This prevents items
+         * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
+         */
+        static class ScrapData {
+            ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
+            int mMaxScrap = DEFAULT_MAX_SCRAP;
+            long mCreateRunningAverageNs = 0;
+            long mBindRunningAverageNs = 0;
+        }
+        SparseArray<ScrapData> mScrap = new SparseArray<>();
+
+        private int mAttachCount = 0;
+
+        public void clear() {
+            for (int i = 0; i < mScrap.size(); i++) {
+                ScrapData data = mScrap.valueAt(i);
+                data.mScrapHeap.clear();
+            }
+        }
+
+        public void setMaxRecycledViews(int viewType, int max) {
+            ScrapData scrapData = getScrapDataForType(viewType);
+            scrapData.mMaxScrap = max;
+            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
+            if (scrapHeap != null) {
+                while (scrapHeap.size() > max) {
+                    scrapHeap.remove(scrapHeap.size() - 1);
+                }
+            }
+        }
+
+        /**
+         * Returns the current number of Views held by the RecycledViewPool of the given view type.
+         */
+        public int getRecycledViewCount(int viewType) {
+            return getScrapDataForType(viewType).mScrapHeap.size();
+        }
+
+        public ViewHolder getRecycledView(int viewType) {
+            final ScrapData scrapData = mScrap.get(viewType);
+            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
+                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
+                return scrapHeap.remove(scrapHeap.size() - 1);
+            }
+            return null;
+        }
+
+        int size() {
+            int count = 0;
+            for (int i = 0; i < mScrap.size(); i++) {
+                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
+                if (viewHolders != null) {
+                    count += viewHolders.size();
+                }
+            }
+            return count;
+        }
+
+        public void putRecycledView(ViewHolder scrap) {
+            final int viewType = scrap.getItemViewType();
+            final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;
+            if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
+                return;
+            }
+            if (DEBUG && scrapHeap.contains(scrap)) {
+                throw new IllegalArgumentException("this scrap item already exists");
+            }
+            scrap.resetInternal();
+            scrapHeap.add(scrap);
+        }
+
+        long runningAverage(long oldAverage, long newValue) {
+            if (oldAverage == 0) {
+                return newValue;
+            }
+            return (oldAverage / 4 * 3) + (newValue / 4);
+        }
+
+        void factorInCreateTime(int viewType, long createTimeNs) {
+            ScrapData scrapData = getScrapDataForType(viewType);
+            scrapData.mCreateRunningAverageNs = runningAverage(
+                    scrapData.mCreateRunningAverageNs, createTimeNs);
+        }
+
+        void factorInBindTime(int viewType, long bindTimeNs) {
+            ScrapData scrapData = getScrapDataForType(viewType);
+            scrapData.mBindRunningAverageNs = runningAverage(
+                    scrapData.mBindRunningAverageNs, bindTimeNs);
+        }
+
+        boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
+            long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
+            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
+        }
+
+        boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
+            long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
+            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
+        }
+
+        void attach(Adapter adapter) {
+            mAttachCount++;
+        }
+
+        void detach() {
+            mAttachCount--;
+        }
+
+
+        /**
+         * Detaches the old adapter and attaches the new one.
+         * <p>
+         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
+         * adapter uses a different ViewHolder than the oldAdapter.
+         *
+         * @param oldAdapter The previous adapter instance. Will be detached.
+         * @param newAdapter The new adapter instance. Will be attached.
+         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
+         *                               ViewHolder and view types.
+         */
+        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
+                boolean compatibleWithPrevious) {
+            if (oldAdapter != null) {
+                detach();
+            }
+            if (!compatibleWithPrevious && mAttachCount == 0) {
+                clear();
+            }
+            if (newAdapter != null) {
+                attach(newAdapter);
+            }
+        }
+
+        private ScrapData getScrapDataForType(int viewType) {
+            ScrapData scrapData = mScrap.get(viewType);
+            if (scrapData == null) {
+                scrapData = new ScrapData();
+                mScrap.put(viewType, scrapData);
+            }
+            return scrapData;
+        }
+    }
+
+    /**
+     * Utility method for finding an internal RecyclerView, if present
+     */
+    @Nullable
+    static RecyclerView findNestedRecyclerView(@NonNull View view) {
+        if (!(view instanceof ViewGroup)) {
+            return null;
+        }
+        if (view instanceof RecyclerView) {
+            return (RecyclerView) view;
+        }
+        final ViewGroup parent = (ViewGroup) view;
+        final int count = parent.getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = parent.getChildAt(i);
+            final RecyclerView descendant = findNestedRecyclerView(child);
+            if (descendant != null) {
+                return descendant;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Utility method for clearing holder's internal RecyclerView, if present
+     */
+    static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
+        if (holder.mNestedRecyclerView != null) {
+            View item = holder.mNestedRecyclerView.get();
+            while (item != null) {
+                if (item == holder.itemView) {
+                    return; // match found, don't need to clear
+                }
+
+                ViewParent parent = item.getParent();
+                if (parent instanceof View) {
+                    item = (View) parent;
+                } else {
+                    item = null;
+                }
+            }
+            holder.mNestedRecyclerView = null; // not nested
+        }
+    }
+
+    /**
+     * Time base for deadline-aware work scheduling. Overridable for testing.
+     *
+     * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
+     * isn't relevant.
+     */
+    long getNanoTime() {
+        if (ALLOW_THREAD_GAP_WORK) {
+            return System.nanoTime();
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * A Recycler is responsible for managing scrapped or detached item views for reuse.
+     *
+     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
+     * that has been marked for removal or reuse.</p>
+     *
+     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
+     * an adapter's data set representing the data at a given position or item ID.
+     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
+     * If not, the view can be quickly reused by the LayoutManager with no further work.
+     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
+     * may be repositioned by a LayoutManager without remeasurement.</p>
+     */
+    public final class Recycler {
+        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
+        ArrayList<ViewHolder> mChangedScrap = null;
+
+        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
+
+        private final List<ViewHolder>
+                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
+
+        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
+        int mViewCacheMax = DEFAULT_CACHE_SIZE;
+
+        RecycledViewPool mRecyclerPool;
+
+        private ViewCacheExtension mViewCacheExtension;
+
+        static final int DEFAULT_CACHE_SIZE = 2;
+
+        /**
+         * Clear scrap views out of this recycler. Detached views contained within a
+         * recycled view pool will remain.
+         */
+        public void clear() {
+            mAttachedScrap.clear();
+            recycleAndClearCachedViews();
+        }
+
+        /**
+         * Set the maximum number of detached, valid views we should retain for later use.
+         *
+         * @param viewCount Number of views to keep before sending views to the shared pool
+         */
+        public void setViewCacheSize(int viewCount) {
+            mRequestedCacheMax = viewCount;
+            updateViewCacheSize();
+        }
+
+        void updateViewCacheSize() {
+            int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
+            mViewCacheMax = mRequestedCacheMax + extraCache;
+
+            // first, try the views that can be recycled
+            for (int i = mCachedViews.size() - 1;
+                    i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
+                recycleCachedViewAt(i);
+            }
+        }
+
+        /**
+         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
+         *
+         * @return List of ViewHolders in the scrap list.
+         */
+        public List<ViewHolder> getScrapList() {
+            return mUnmodifiableAttachedScrap;
+        }
+
+        /**
+         * Helper method for getViewForPosition.
+         * <p>
+         * Checks whether a given view holder can be used for the provided position.
+         *
+         * @param holder ViewHolder
+         * @return true if ViewHolder matches the provided position, false otherwise
+         */
+        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
+            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
+            // if it is not removed, verify the type and id.
+            if (holder.isRemoved()) {
+                if (DEBUG && !mState.isPreLayout()) {
+                    throw new IllegalStateException("should not receive a removed view unless it"
+                            + " is pre layout");
+                }
+                return mState.isPreLayout();
+            }
+            if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
+                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
+                        + "adapter position" + holder);
+            }
+            if (!mState.isPreLayout()) {
+                // don't check type if it is pre-layout.
+                final int type = mAdapter.getItemViewType(holder.mPosition);
+                if (type != holder.getItemViewType()) {
+                    return false;
+                }
+            }
+            if (mAdapter.hasStableIds()) {
+                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
+            }
+            return true;
+        }
+
+        /**
+         * Attempts to bind view, and account for relevant timing information. If
+         * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
+         *
+         * @param holder Holder to be bound.
+         * @param offsetPosition Position of item to be bound.
+         * @param position Pre-layout position of item to be bound.
+         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
+         *                   complete. If FOREVER_NS is passed, this method will not fail to
+         *                   bind the holder.
+         * @return
+         */
+        private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
+                int position, long deadlineNs) {
+            holder.mOwnerRecyclerView = RecyclerView.this;
+            final int viewType = holder.getItemViewType();
+            long startBindNs = getNanoTime();
+            if (deadlineNs != FOREVER_NS
+                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
+                // abort - we have a deadline we can't meet
+                return false;
+            }
+            mAdapter.bindViewHolder(holder, offsetPosition);
+            long endBindNs = getNanoTime();
+            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
+            attachAccessibilityDelegate(holder.itemView);
+            if (mState.isPreLayout()) {
+                holder.mPreLayoutPosition = position;
+            }
+            return true;
+        }
+
+        /**
+         * Binds the given View to the position. The View can be a View previously retrieved via
+         * {@link #getViewForPosition(int)} or created by
+         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
+         * <p>
+         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
+         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
+         * wants to handle its own recycling logic.
+         * <p>
+         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
+         * you don't need to call this method unless you want to bind this View to another position.
+         *
+         * @param view The view to update.
+         * @param position The position of the item to bind to this View.
+         */
+        public void bindViewToPosition(View view, int position) {
+            ViewHolder holder = getChildViewHolderInt(view);
+            if (holder == null) {
+                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
+                        + " pass arbitrary views to this method, they should be created by the "
+                        + "Adapter");
+            }
+            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
+            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
+                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
+                        + "position " + position + "(offset:" + offsetPosition + ")."
+                        + "state:" + mState.getItemCount());
+            }
+            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
+
+            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
+            final LayoutParams rvLayoutParams;
+            if (lp == null) {
+                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
+                holder.itemView.setLayoutParams(rvLayoutParams);
+            } else if (!checkLayoutParams(lp)) {
+                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
+                holder.itemView.setLayoutParams(rvLayoutParams);
+            } else {
+                rvLayoutParams = (LayoutParams) lp;
+            }
+
+            rvLayoutParams.mInsetsDirty = true;
+            rvLayoutParams.mViewHolder = holder;
+            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
+        }
+
+        /**
+         * RecyclerView provides artificial position range (item count) in pre-layout state and
+         * automatically maps these positions to {@link Adapter} positions when
+         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
+         * <p>
+         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
+         * LayoutManager may need to call some custom component with item positions in which
+         * case you need the actual adapter position instead of the pre layout position. You
+         * can use this method to convert a pre-layout position to adapter (post layout) position.
+         * <p>
+         * Note that if the provided position belongs to a deleted ViewHolder, this method will
+         * return -1.
+         * <p>
+         * Calling this method in post-layout state returns the same value back.
+         *
+         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
+         *                 less than {@link State#getItemCount()}.
+         */
+        public int convertPreLayoutPositionToPostLayout(int position) {
+            if (position < 0 || position >= mState.getItemCount()) {
+                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
+                        + "item count is " + mState.getItemCount());
+            }
+            if (!mState.isPreLayout()) {
+                return position;
+            }
+            return mAdapterHelper.findPositionOffset(position);
+        }
+
+        /**
+         * Obtain a view initialized for the given position.
+         *
+         * This method should be used by {@link LayoutManager} implementations to obtain
+         * views to represent data from an {@link Adapter}.
+         * <p>
+         * The Recycler may reuse a scrap or detached view from a shared pool if one is
+         * available for the correct view type. If the adapter has not indicated that the
+         * data at the given position has changed, the Recycler will attempt to hand back
+         * a scrap view that was previously initialized for that data without rebinding.
+         *
+         * @param position Position to obtain a view for
+         * @return A view representing the data at <code>position</code> from <code>adapter</code>
+         */
+        public View getViewForPosition(int position) {
+            return getViewForPosition(position, false);
+        }
+
+        View getViewForPosition(int position, boolean dryRun) {
+            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
+        }
+
+        /**
+         * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
+         * cache, the RecycledViewPool, or creating it directly.
+         * <p>
+         * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
+         * rather than constructing or binding a ViewHolder if it doesn't think it has time.
+         * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
+         * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
+         * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
+         *
+         * @param position Position of ViewHolder to be returned.
+         * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
+         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
+         *                   complete. If FOREVER_NS is passed, this method will not fail to
+         *                   create/bind the holder if needed.
+         *
+         * @return ViewHolder for requested position
+         */
+        @Nullable
+        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
+                boolean dryRun, long deadlineNs) {
+            if (position < 0 || position >= mState.getItemCount()) {
+                throw new IndexOutOfBoundsException("Invalid item position " + position
+                        + "(" + position + "). Item count:" + mState.getItemCount());
+            }
+            boolean fromScrapOrHiddenOrCache = false;
+            ViewHolder holder = null;
+            // 0) If there is a changed scrap, try to find from there
+            if (mState.isPreLayout()) {
+                holder = getChangedScrapViewForPosition(position);
+                fromScrapOrHiddenOrCache = holder != null;
+            }
+            // 1) Find by position from scrap/hidden list/cache
+            if (holder == null) {
+                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
+                if (holder != null) {
+                    if (!validateViewHolderForOffsetPosition(holder)) {
+                        // recycle holder (and unscrap if relevant) since it can't be used
+                        if (!dryRun) {
+                            // we would like to recycle this but need to make sure it is not used by
+                            // animation logic etc.
+                            holder.addFlags(ViewHolder.FLAG_INVALID);
+                            if (holder.isScrap()) {
+                                removeDetachedView(holder.itemView, false);
+                                holder.unScrap();
+                            } else if (holder.wasReturnedFromScrap()) {
+                                holder.clearReturnedFromScrapFlag();
+                            }
+                            recycleViewHolderInternal(holder);
+                        }
+                        holder = null;
+                    } else {
+                        fromScrapOrHiddenOrCache = true;
+                    }
+                }
+            }
+            if (holder == null) {
+                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
+                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
+                    throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
+                            + "position " + position + "(offset:" + offsetPosition + ")."
+                            + "state:" + mState.getItemCount());
+                }
+
+                final int type = mAdapter.getItemViewType(offsetPosition);
+                // 2) Find from scrap/cache via stable ids, if exists
+                if (mAdapter.hasStableIds()) {
+                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
+                            type, dryRun);
+                    if (holder != null) {
+                        // update position
+                        holder.mPosition = offsetPosition;
+                        fromScrapOrHiddenOrCache = true;
+                    }
+                }
+                if (holder == null && mViewCacheExtension != null) {
+                    // We are NOT sending the offsetPosition because LayoutManager does not
+                    // know it.
+                    final View view = mViewCacheExtension
+                            .getViewForPositionAndType(this, position, type);
+                    if (view != null) {
+                        holder = getChildViewHolder(view);
+                        if (holder == null) {
+                            throw new IllegalArgumentException("getViewForPositionAndType returned"
+                                    + " a view which does not have a ViewHolder");
+                        } else if (holder.shouldIgnore()) {
+                            throw new IllegalArgumentException("getViewForPositionAndType returned"
+                                    + " a view that is ignored. You must call stopIgnoring before"
+                                    + " returning this view.");
+                        }
+                    }
+                }
+                if (holder == null) { // fallback to pool
+                    if (DEBUG) {
+                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
+                                + position + ") fetching from shared pool");
+                    }
+                    holder = getRecycledViewPool().getRecycledView(type);
+                    if (holder != null) {
+                        holder.resetInternal();
+                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
+                            invalidateDisplayListInt(holder);
+                        }
+                    }
+                }
+                if (holder == null) {
+                    long start = getNanoTime();
+                    if (deadlineNs != FOREVER_NS
+                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
+                        // abort - we have a deadline we can't meet
+                        return null;
+                    }
+                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
+                    if (ALLOW_THREAD_GAP_WORK) {
+                        // only bother finding nested RV if prefetching
+                        RecyclerView innerView = findNestedRecyclerView(holder.itemView);
+                        if (innerView != null) {
+                            holder.mNestedRecyclerView = new WeakReference<>(innerView);
+                        }
+                    }
+
+                    long end = getNanoTime();
+                    mRecyclerPool.factorInCreateTime(type, end - start);
+                    if (DEBUG) {
+                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
+                    }
+                }
+            }
+
+            // This is very ugly but the only place we can grab this information
+            // before the View is rebound and returned to the LayoutManager for post layout ops.
+            // We don't need this in pre-layout since the VH is not updated by the LM.
+            if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
+                    .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
+                holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
+                if (mState.mRunSimpleAnimations) {
+                    int changeFlags = ItemAnimator
+                            .buildAdapterChangeFlagsForAnimations(holder);
+                    changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
+                    final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
+                            holder, changeFlags, holder.getUnmodifiedPayloads());
+                    recordAnimationInfoIfBouncedHiddenView(holder, info);
+                }
+            }
+
+            boolean bound = false;
+            if (mState.isPreLayout() && holder.isBound()) {
+                // do not update unless we absolutely have to.
+                holder.mPreLayoutPosition = position;
+            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
+                if (DEBUG && holder.isRemoved()) {
+                    throw new IllegalStateException("Removed holder should be bound and it should"
+                            + " come here only in pre-layout. Holder: " + holder);
+                }
+                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
+                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
+            }
+
+            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
+            final LayoutParams rvLayoutParams;
+            if (lp == null) {
+                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
+                holder.itemView.setLayoutParams(rvLayoutParams);
+            } else if (!checkLayoutParams(lp)) {
+                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
+                holder.itemView.setLayoutParams(rvLayoutParams);
+            } else {
+                rvLayoutParams = (LayoutParams) lp;
+            }
+            rvLayoutParams.mViewHolder = holder;
+            rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
+            return holder;
+        }
+
+        private void attachAccessibilityDelegate(View itemView) {
+            if (isAccessibilityEnabled()) {
+                if (itemView.getImportantForAccessibility()
+                        == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+                    itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+                }
+
+                if (itemView.getAccessibilityDelegate() == null) {
+                    itemView.setAccessibilityDelegate(mAccessibilityDelegate.getItemDelegate());
+                }
+            }
+        }
+
+        private void invalidateDisplayListInt(ViewHolder holder) {
+            if (holder.itemView instanceof ViewGroup) {
+                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
+            }
+        }
+
+        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
+            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
+                final View view = viewGroup.getChildAt(i);
+                if (view instanceof ViewGroup) {
+                    invalidateDisplayListInt((ViewGroup) view, true);
+                }
+            }
+            if (!invalidateThis) {
+                return;
+            }
+            // we need to force it to become invisible
+            if (viewGroup.getVisibility() == View.INVISIBLE) {
+                viewGroup.setVisibility(View.VISIBLE);
+                viewGroup.setVisibility(View.INVISIBLE);
+            } else {
+                final int visibility = viewGroup.getVisibility();
+                viewGroup.setVisibility(View.INVISIBLE);
+                viewGroup.setVisibility(visibility);
+            }
+        }
+
+        /**
+         * Recycle a detached view. The specified view will be added to a pool of views
+         * for later rebinding and reuse.
+         *
+         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
+         * View is scrapped, it will be removed from scrap list.</p>
+         *
+         * @param view Removed view for recycling
+         * @see LayoutManager#removeAndRecycleView(View, Recycler)
+         */
+        public void recycleView(View view) {
+            // This public recycle method tries to make view recycle-able since layout manager
+            // intended to recycle this view (e.g. even if it is in scrap or change cache)
+            ViewHolder holder = getChildViewHolderInt(view);
+            if (holder.isTmpDetached()) {
+                removeDetachedView(view, false);
+            }
+            if (holder.isScrap()) {
+                holder.unScrap();
+            } else if (holder.wasReturnedFromScrap()) {
+                holder.clearReturnedFromScrapFlag();
+            }
+            recycleViewHolderInternal(holder);
+        }
+
+        /**
+         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
+         * catch potential bugs.
+         * @param view
+         */
+        void recycleViewInternal(View view) {
+            recycleViewHolderInternal(getChildViewHolderInt(view));
+        }
+
+        void recycleAndClearCachedViews() {
+            final int count = mCachedViews.size();
+            for (int i = count - 1; i >= 0; i--) {
+                recycleCachedViewAt(i);
+            }
+            mCachedViews.clear();
+            if (ALLOW_THREAD_GAP_WORK) {
+                mPrefetchRegistry.clearPrefetchPositions();
+            }
+        }
+
+        /**
+         * Recycles a cached view and removes the view from the list. Views are added to cache
+         * if and only if they are recyclable, so this method does not check it again.
+         * <p>
+         * A small exception to this rule is when the view does not have an animator reference
+         * but transient state is true (due to animations created outside ItemAnimator). In that
+         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
+         * still recyclable since Adapter wants to do so.
+         *
+         * @param cachedViewIndex The index of the view in cached views list
+         */
+        void recycleCachedViewAt(int cachedViewIndex) {
+            if (DEBUG) {
+                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
+            }
+            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
+            if (DEBUG) {
+                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
+            }
+            addViewHolderToRecycledViewPool(viewHolder, true);
+            mCachedViews.remove(cachedViewIndex);
+        }
+
+        /**
+         * internal implementation checks if view is scrapped or attached and throws an exception
+         * if so.
+         * Public version un-scraps before calling recycle.
+         */
+        void recycleViewHolderInternal(ViewHolder holder) {
+            if (holder.isScrap() || holder.itemView.getParent() != null) {
+                throw new IllegalArgumentException(
+                        "Scrapped or attached views may not be recycled. isScrap:"
+                                + holder.isScrap() + " isAttached:"
+                                + (holder.itemView.getParent() != null));
+            }
+
+            if (holder.isTmpDetached()) {
+                throw new IllegalArgumentException("Tmp detached view should be removed "
+                        + "from RecyclerView before it can be recycled: " + holder);
+            }
+
+            if (holder.shouldIgnore()) {
+                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
+                        + " should first call stopIgnoringView(view) before calling recycle.");
+            }
+            //noinspection unchecked
+            final boolean transientStatePreventsRecycling = holder
+                    .doesTransientStatePreventRecycling();
+            final boolean forceRecycle = mAdapter != null
+                    && transientStatePreventsRecycling
+                    && mAdapter.onFailedToRecycleView(holder);
+            boolean cached = false;
+            boolean recycled = false;
+            if (DEBUG && mCachedViews.contains(holder)) {
+                throw new IllegalArgumentException("cached view received recycle internal? "
+                        + holder);
+            }
+            if (forceRecycle || holder.isRecyclable()) {
+                if (mViewCacheMax > 0
+                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
+                                | ViewHolder.FLAG_REMOVED
+                                | ViewHolder.FLAG_UPDATE
+                                | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
+                    // Retire oldest cached view
+                    int cachedViewSize = mCachedViews.size();
+                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
+                        recycleCachedViewAt(0);
+                        cachedViewSize--;
+                    }
+
+                    int targetCacheIndex = cachedViewSize;
+                    if (ALLOW_THREAD_GAP_WORK
+                            && cachedViewSize > 0
+                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
+                        // when adding the view, skip past most recently prefetched views
+                        int cacheIndex = cachedViewSize - 1;
+                        while (cacheIndex >= 0) {
+                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
+                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
+                                break;
+                            }
+                            cacheIndex--;
+                        }
+                        targetCacheIndex = cacheIndex + 1;
+                    }
+                    mCachedViews.add(targetCacheIndex, holder);
+                    cached = true;
+                }
+                if (!cached) {
+                    addViewHolderToRecycledViewPool(holder, true);
+                    recycled = true;
+                }
+            } else {
+                // NOTE: A view can fail to be recycled when it is scrolled off while an animation
+                // runs. In this case, the item is eventually recycled by
+                // ItemAnimatorRestoreListener#onAnimationFinished.
+
+                // TODO: consider cancelling an animation when an item is removed scrollBy,
+                // to return it to the pool faster
+                if (DEBUG) {
+                    Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
+                            + "re-visit here. We are still removing it from animation lists");
+                }
+            }
+            // even if the holder is not removed, we still call this method so that it is removed
+            // from view holder lists.
+            mViewInfoStore.removeViewHolder(holder);
+            if (!cached && !recycled && transientStatePreventsRecycling) {
+                holder.mOwnerRecyclerView = null;
+            }
+        }
+
+        /**
+         * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
+         *
+         * Pass false to dispatchRecycled for views that have not been bound.
+         *
+         * @param holder Holder to be added to the pool.
+         * @param dispatchRecycled True to dispatch View recycled callbacks.
+         */
+        void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
+            clearNestedRecyclerViewIfNotNested(holder);
+            holder.itemView.setAccessibilityDelegate(null);
+            if (dispatchRecycled) {
+                dispatchViewRecycled(holder);
+            }
+            holder.mOwnerRecyclerView = null;
+            getRecycledViewPool().putRecycledView(holder);
+        }
+
+        /**
+         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
+         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
+         * internal bookkeeping.
+         */
+        void quickRecycleScrapView(View view) {
+            final ViewHolder holder = getChildViewHolderInt(view);
+            holder.mScrapContainer = null;
+            holder.mInChangeScrap = false;
+            holder.clearReturnedFromScrapFlag();
+            recycleViewHolderInternal(holder);
+        }
+
+        /**
+         * Mark an attached view as scrap.
+         *
+         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
+         * for rebinding and reuse. Requests for a view for a given position may return a
+         * reused or rebound scrap view instance.</p>
+         *
+         * @param view View to scrap
+         */
+        void scrapView(View view) {
+            final ViewHolder holder = getChildViewHolderInt(view);
+            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
+                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
+                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
+                    throw new IllegalArgumentException("Called scrap view with an invalid view."
+                            + " Invalid views cannot be reused from scrap, they should rebound from"
+                            + " recycler pool.");
+                }
+                holder.setScrapContainer(this, false);
+                mAttachedScrap.add(holder);
+            } else {
+                if (mChangedScrap == null) {
+                    mChangedScrap = new ArrayList<ViewHolder>();
+                }
+                holder.setScrapContainer(this, true);
+                mChangedScrap.add(holder);
+            }
+        }
+
+        /**
+         * Remove a previously scrapped view from the pool of eligible scrap.
+         *
+         * <p>This view will no longer be eligible for reuse until re-scrapped or
+         * until it is explicitly removed and recycled.</p>
+         */
+        void unscrapView(ViewHolder holder) {
+            if (holder.mInChangeScrap) {
+                mChangedScrap.remove(holder);
+            } else {
+                mAttachedScrap.remove(holder);
+            }
+            holder.mScrapContainer = null;
+            holder.mInChangeScrap = false;
+            holder.clearReturnedFromScrapFlag();
+        }
+
+        int getScrapCount() {
+            return mAttachedScrap.size();
+        }
+
+        View getScrapViewAt(int index) {
+            return mAttachedScrap.get(index).itemView;
+        }
+
+        void clearScrap() {
+            mAttachedScrap.clear();
+            if (mChangedScrap != null) {
+                mChangedScrap.clear();
+            }
+        }
+
+        ViewHolder getChangedScrapViewForPosition(int position) {
+            // If pre-layout, check the changed scrap for an exact match.
+            final int changedScrapSize;
+            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
+                return null;
+            }
+            // find by position
+            for (int i = 0; i < changedScrapSize; i++) {
+                final ViewHolder holder = mChangedScrap.get(i);
+                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
+                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
+                    return holder;
+                }
+            }
+            // find by id
+            if (mAdapter.hasStableIds()) {
+                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
+                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
+                    final long id = mAdapter.getItemId(offsetPosition);
+                    for (int i = 0; i < changedScrapSize; i++) {
+                        final ViewHolder holder = mChangedScrap.get(i);
+                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
+                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
+                            return holder;
+                        }
+                    }
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Returns a view for the position either from attach scrap, hidden children, or cache.
+         *
+         * @param position Item position
+         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
+         * @return a ViewHolder that can be re-used for this position.
+         */
+        ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
+            final int scrapCount = mAttachedScrap.size();
+
+            // Try first for an exact, non-invalid match from scrap.
+            for (int i = 0; i < scrapCount; i++) {
+                final ViewHolder holder = mAttachedScrap.get(i);
+                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
+                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
+                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
+                    return holder;
+                }
+            }
+
+            if (!dryRun) {
+                View view = mChildHelper.findHiddenNonRemovedView(position);
+                if (view != null) {
+                    // This View is good to be used. We just need to unhide, detach and move to the
+                    // scrap list.
+                    final ViewHolder vh = getChildViewHolderInt(view);
+                    mChildHelper.unhide(view);
+                    int layoutIndex = mChildHelper.indexOfChild(view);
+                    if (layoutIndex == RecyclerView.NO_POSITION) {
+                        throw new IllegalStateException("layout index should not be -1 after "
+                                + "unhiding a view:" + vh);
+                    }
+                    mChildHelper.detachViewFromParent(layoutIndex);
+                    scrapView(view);
+                    vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
+                            | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
+                    return vh;
+                }
+            }
+
+            // Search in our first-level recycled view cache.
+            final int cacheSize = mCachedViews.size();
+            for (int i = 0; i < cacheSize; i++) {
+                final ViewHolder holder = mCachedViews.get(i);
+                // invalid view holders may be in cache if adapter has stable ids as they can be
+                // retrieved via getScrapOrCachedViewForId
+                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
+                    if (!dryRun) {
+                        mCachedViews.remove(i);
+                    }
+                    if (DEBUG) {
+                        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
+                                + ") found match in cache: " + holder);
+                    }
+                    return holder;
+                }
+            }
+            return null;
+        }
+
+        ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
+            // Look in our attached views first
+            final int count = mAttachedScrap.size();
+            for (int i = count - 1; i >= 0; i--) {
+                final ViewHolder holder = mAttachedScrap.get(i);
+                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
+                    if (type == holder.getItemViewType()) {
+                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
+                        if (holder.isRemoved()) {
+                            // this might be valid in two cases:
+                            // > item is removed but we are in pre-layout pass
+                            // >> do nothing. return as is. make sure we don't rebind
+                            // > item is removed then added to another position and we are in
+                            // post layout.
+                            // >> remove removed and invalid flags, add update flag to rebind
+                            // because item was invisible to us and we don't know what happened in
+                            // between.
+                            if (!mState.isPreLayout()) {
+                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
+                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
+                            }
+                        }
+                        return holder;
+                    } else if (!dryRun) {
+                        // if we are running animations, it is actually better to keep it in scrap
+                        // but this would force layout manager to lay it out which would be bad.
+                        // Recycle this scrap. Type mismatch.
+                        mAttachedScrap.remove(i);
+                        removeDetachedView(holder.itemView, false);
+                        quickRecycleScrapView(holder.itemView);
+                    }
+                }
+            }
+
+            // Search the first-level cache
+            final int cacheSize = mCachedViews.size();
+            for (int i = cacheSize - 1; i >= 0; i--) {
+                final ViewHolder holder = mCachedViews.get(i);
+                if (holder.getItemId() == id) {
+                    if (type == holder.getItemViewType()) {
+                        if (!dryRun) {
+                            mCachedViews.remove(i);
+                        }
+                        return holder;
+                    } else if (!dryRun) {
+                        recycleCachedViewAt(i);
+                        return null;
+                    }
+                }
+            }
+            return null;
+        }
+
+        void dispatchViewRecycled(ViewHolder holder) {
+            if (mRecyclerListener != null) {
+                mRecyclerListener.onViewRecycled(holder);
+            }
+            if (mAdapter != null) {
+                mAdapter.onViewRecycled(holder);
+            }
+            if (mState != null) {
+                mViewInfoStore.removeViewHolder(holder);
+            }
+            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
+        }
+
+        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
+                boolean compatibleWithPrevious) {
+            clear();
+            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
+        }
+
+        void offsetPositionRecordsForMove(int from, int to) {
+            final int start, end, inBetweenOffset;
+            if (from < to) {
+                start = from;
+                end = to;
+                inBetweenOffset = -1;
+            } else {
+                start = to;
+                end = from;
+                inBetweenOffset = 1;
+            }
+            final int cachedCount = mCachedViews.size();
+            for (int i = 0; i < cachedCount; i++) {
+                final ViewHolder holder = mCachedViews.get(i);
+                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
+                    continue;
+                }
+                if (holder.mPosition == from) {
+                    holder.offsetPosition(to - from, false);
+                } else {
+                    holder.offsetPosition(inBetweenOffset, false);
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
+                            + holder);
+                }
+            }
+        }
+
+        void offsetPositionRecordsForInsert(int insertedAt, int count) {
+            final int cachedCount = mCachedViews.size();
+            for (int i = 0; i < cachedCount; i++) {
+                final ViewHolder holder = mCachedViews.get(i);
+                if (holder != null && holder.mPosition >= insertedAt) {
+                    if (DEBUG) {
+                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
+                                + holder + " now at position " + (holder.mPosition + count));
+                    }
+                    holder.offsetPosition(count, true);
+                }
+            }
+        }
+
+        /**
+         * @param removedFrom Remove start index
+         * @param count Remove count
+         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
+         *                         false, they'll be applied before the second layout pass
+         */
+        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
+            final int removedEnd = removedFrom + count;
+            final int cachedCount = mCachedViews.size();
+            for (int i = cachedCount - 1; i >= 0; i--) {
+                final ViewHolder holder = mCachedViews.get(i);
+                if (holder != null) {
+                    if (holder.mPosition >= removedEnd) {
+                        if (DEBUG) {
+                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
+                                    + " holder " + holder + " now at position "
+                                    + (holder.mPosition - count));
+                        }
+                        holder.offsetPosition(-count, applyToPreLayout);
+                    } else if (holder.mPosition >= removedFrom) {
+                        // Item for this view was removed. Dump it from the cache.
+                        holder.addFlags(ViewHolder.FLAG_REMOVED);
+                        recycleCachedViewAt(i);
+                    }
+                }
+            }
+        }
+
+        void setViewCacheExtension(ViewCacheExtension extension) {
+            mViewCacheExtension = extension;
+        }
+
+        void setRecycledViewPool(RecycledViewPool pool) {
+            if (mRecyclerPool != null) {
+                mRecyclerPool.detach();
+            }
+            mRecyclerPool = pool;
+            if (pool != null) {
+                mRecyclerPool.attach(getAdapter());
+            }
+        }
+
+        RecycledViewPool getRecycledViewPool() {
+            if (mRecyclerPool == null) {
+                mRecyclerPool = new RecycledViewPool();
+            }
+            return mRecyclerPool;
+        }
+
+        void viewRangeUpdate(int positionStart, int itemCount) {
+            final int positionEnd = positionStart + itemCount;
+            final int cachedCount = mCachedViews.size();
+            for (int i = cachedCount - 1; i >= 0; i--) {
+                final ViewHolder holder = mCachedViews.get(i);
+                if (holder == null) {
+                    continue;
+                }
+
+                final int pos = holder.getLayoutPosition();
+                if (pos >= positionStart && pos < positionEnd) {
+                    holder.addFlags(ViewHolder.FLAG_UPDATE);
+                    recycleCachedViewAt(i);
+                    // cached views should not be flagged as changed because this will cause them
+                    // to animate when they are returned from cache.
+                }
+            }
+        }
+
+        void setAdapterPositionsAsUnknown() {
+            final int cachedCount = mCachedViews.size();
+            for (int i = 0; i < cachedCount; i++) {
+                final ViewHolder holder = mCachedViews.get(i);
+                if (holder != null) {
+                    holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
+                }
+            }
+        }
+
+        void markKnownViewsInvalid() {
+            if (mAdapter != null && mAdapter.hasStableIds()) {
+                final int cachedCount = mCachedViews.size();
+                for (int i = 0; i < cachedCount; i++) {
+                    final ViewHolder holder = mCachedViews.get(i);
+                    if (holder != null) {
+                        holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
+                        holder.addChangePayload(null);
+                    }
+                }
+            } else {
+                // we cannot re-use cached views in this case. Recycle them all
+                recycleAndClearCachedViews();
+            }
+        }
+
+        void clearOldPositions() {
+            final int cachedCount = mCachedViews.size();
+            for (int i = 0; i < cachedCount; i++) {
+                final ViewHolder holder = mCachedViews.get(i);
+                holder.clearOldPosition();
+            }
+            final int scrapCount = mAttachedScrap.size();
+            for (int i = 0; i < scrapCount; i++) {
+                mAttachedScrap.get(i).clearOldPosition();
+            }
+            if (mChangedScrap != null) {
+                final int changedScrapCount = mChangedScrap.size();
+                for (int i = 0; i < changedScrapCount; i++) {
+                    mChangedScrap.get(i).clearOldPosition();
+                }
+            }
+        }
+
+        void markItemDecorInsetsDirty() {
+            final int cachedCount = mCachedViews.size();
+            for (int i = 0; i < cachedCount; i++) {
+                final ViewHolder holder = mCachedViews.get(i);
+                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
+                if (layoutParams != null) {
+                    layoutParams.mInsetsDirty = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
+     * be controlled by the developer.
+     * <p>
+     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
+     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
+     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
+     * {@link RecycledViewPool}.
+     * <p>
+     * Note that, Recycler never sends Views to this method to be cached. It is developers
+     * responsibility to decide whether they want to keep their Views in this custom cache or let
+     * the default recycling policy handle it.
+     */
+    public abstract static class ViewCacheExtension {
+
+        /**
+         * Returns a View that can be binded to the given Adapter position.
+         * <p>
+         * This method should <b>not</b> create a new View. Instead, it is expected to return
+         * an already created View that can be re-used for the given type and position.
+         * If the View is marked as ignored, it should first call
+         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
+         * <p>
+         * RecyclerView will re-bind the returned View to the position if necessary.
+         *
+         * @param recycler The Recycler that can be used to bind the View
+         * @param position The adapter position
+         * @param type     The type of the View, defined by adapter
+         * @return A View that is bound to the given position or NULL if there is no View to re-use
+         * @see LayoutManager#ignoreView(View)
+         */
+        public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
+    }
+
+    /**
+     * Base class for an Adapter
+     *
+     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
+     * within a {@link RecyclerView}.</p>
+     *
+     * @param <VH> A class that extends ViewHolder that will be used by the adapter.
+     */
+    public abstract static class Adapter<VH extends ViewHolder> {
+        private final AdapterDataObservable mObservable = new AdapterDataObservable();
+        private boolean mHasStableIds = false;
+
+        /**
+         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
+         * an item.
+         * <p>
+         * This new ViewHolder should be constructed with a new View that can represent the items
+         * of the given type. You can either create a new View manually or inflate it from an XML
+         * layout file.
+         * <p>
+         * The new ViewHolder will be used to display items of the adapter using
+         * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
+         * different items in the data set, it is a good idea to cache references to sub views of
+         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
+         *
+         * @param parent The ViewGroup into which the new View will be added after it is bound to
+         *               an adapter position.
+         * @param viewType The view type of the new View.
+         *
+         * @return A new ViewHolder that holds a View of the given view type.
+         * @see #getItemViewType(int)
+         * @see #onBindViewHolder(ViewHolder, int)
+         */
+        public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
+
+        /**
+         * Called by RecyclerView to display the data at the specified position. This method should
+         * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
+         * position.
+         * <p>
+         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
+         * again if the position of the item changes in the data set unless the item itself is
+         * invalidated or the new position cannot be determined. For this reason, you should only
+         * use the <code>position</code> parameter while acquiring the related data item inside
+         * this method and should not keep a copy of it. If you need the position of an item later
+         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
+         * have the updated adapter position.
+         *
+         * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
+         * handle efficient partial bind.
+         *
+         * @param holder The ViewHolder which should be updated to represent the contents of the
+         *        item at the given position in the data set.
+         * @param position The position of the item within the adapter's data set.
+         */
+        public abstract void onBindViewHolder(VH holder, int position);
+
+        /**
+         * Called by RecyclerView to display the data at the specified position. This method
+         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
+         * the given position.
+         * <p>
+         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
+         * again if the position of the item changes in the data set unless the item itself is
+         * invalidated or the new position cannot be determined. For this reason, you should only
+         * use the <code>position</code> parameter while acquiring the related data item inside
+         * this method and should not keep a copy of it. If you need the position of an item later
+         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
+         * have the updated adapter position.
+         * <p>
+         * Partial bind vs full bind:
+         * <p>
+         * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
+         * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
+         * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
+         * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
+         * Adapter should not assume that the payload passed in notify methods will be received by
+         * onBindViewHolder().  For example when the view is not attached to the screen, the
+         * payload in notifyItemChange() will be simply dropped.
+         *
+         * @param holder The ViewHolder which should be updated to represent the contents of the
+         *               item at the given position in the data set.
+         * @param position The position of the item within the adapter's data set.
+         * @param payloads A non-null list of merged payloads. Can be empty list if requires full
+         *                 update.
+         */
+        public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
+            onBindViewHolder(holder, position);
+        }
+
+        /**
+         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
+         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
+         *
+         * @see #onCreateViewHolder(ViewGroup, int)
+         */
+        public final VH createViewHolder(ViewGroup parent, int viewType) {
+            Trace.beginSection(TRACE_CREATE_VIEW_TAG);
+            final VH holder = onCreateViewHolder(parent, viewType);
+            holder.mItemViewType = viewType;
+            Trace.endSection();
+            return holder;
+        }
+
+        /**
+         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
+         * {@link ViewHolder} contents with the item at the given position and also sets up some
+         * private fields to be used by RecyclerView.
+         *
+         * @see #onBindViewHolder(ViewHolder, int)
+         */
+        public final void bindViewHolder(VH holder, int position) {
+            holder.mPosition = position;
+            if (hasStableIds()) {
+                holder.mItemId = getItemId(position);
+            }
+            holder.setFlags(ViewHolder.FLAG_BOUND,
+                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
+                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
+            Trace.beginSection(TRACE_BIND_VIEW_TAG);
+            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
+            holder.clearPayload();
+            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
+            if (layoutParams instanceof RecyclerView.LayoutParams) {
+                ((LayoutParams) layoutParams).mInsetsDirty = true;
+            }
+            Trace.endSection();
+        }
+
+        /**
+         * Return the view type of the item at <code>position</code> for the purposes
+         * of view recycling.
+         *
+         * <p>The default implementation of this method returns 0, making the assumption of
+         * a single view type for the adapter. Unlike ListView adapters, types need not
+         * be contiguous. Consider using id resources to uniquely identify item view types.
+         *
+         * @param position position to query
+         * @return integer value identifying the type of the view needed to represent the item at
+         *                 <code>position</code>. Type codes need not be contiguous.
+         */
+        public int getItemViewType(int position) {
+            return 0;
+        }
+
+        /**
+         * Indicates whether each item in the data set can be represented with a unique identifier
+         * of type {@link java.lang.Long}.
+         *
+         * @param hasStableIds Whether items in data set have unique identifiers or not.
+         * @see #hasStableIds()
+         * @see #getItemId(int)
+         */
+        public void setHasStableIds(boolean hasStableIds) {
+            if (hasObservers()) {
+                throw new IllegalStateException("Cannot change whether this adapter has "
+                        + "stable IDs while the adapter has registered observers.");
+            }
+            mHasStableIds = hasStableIds;
+        }
+
+        /**
+         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
+         * would return false this method should return {@link #NO_ID}. The default implementation
+         * of this method returns {@link #NO_ID}.
+         *
+         * @param position Adapter position to query
+         * @return the stable ID of the item at position
+         */
+        public long getItemId(int position) {
+            return NO_ID;
+        }
+
+        /**
+         * Returns the total number of items in the data set held by the adapter.
+         *
+         * @return The total number of items in this adapter.
+         */
+        public abstract int getItemCount();
+
+        /**
+         * Returns true if this adapter publishes a unique <code>long</code> value that can
+         * act as a key for the item at a given position in the data set. If that item is relocated
+         * in the data set, the ID returned for that item should be the same.
+         *
+         * @return true if this adapter's items have stable IDs
+         */
+        public final boolean hasStableIds() {
+            return mHasStableIds;
+        }
+
+        /**
+         * Called when a view created by this adapter has been recycled.
+         *
+         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
+         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
+         * fallen out of visibility or a set of cached views represented by views still
+         * attached to the parent RecyclerView. If an item view has large or expensive data
+         * bound to it such as large bitmaps, this may be a good place to release those
+         * resources.</p>
+         * <p>
+         * RecyclerView calls this method right before clearing ViewHolder's internal data and
+         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
+         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
+         * its adapter position.
+         *
+         * @param holder The ViewHolder for the view being recycled
+         */
+        public void onViewRecycled(VH holder) {
+        }
+
+        /**
+         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
+         * due to its transient state. Upon receiving this callback, Adapter can clear the
+         * animation(s) that effect the View's transient state and return <code>true</code> so that
+         * the View can be recycled. Keep in mind that the View in question is already removed from
+         * the RecyclerView.
+         * <p>
+         * In some cases, it is acceptable to recycle a View although it has transient state. Most
+         * of the time, this is a case where the transient state will be cleared in
+         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
+         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
+         * value of this method to decide whether the View should be recycled or not.
+         * <p>
+         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
+         * should never receive this callback because RecyclerView keeps those Views as children
+         * until their animations are complete. This callback is useful when children of the item
+         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
+         * <p>
+         * You should <em>never</em> fix this issue by calling
+         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
+         * <code>holder.itemView.setHasTransientState(true);</code>. Each
+         * <code>View.setHasTransientState(true)</code> call must be matched by a
+         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
+         * may become inconsistent. You should always prefer to end or cancel animations that are
+         * triggering the transient state instead of handling it manually.
+         *
+         * @param holder The ViewHolder containing the View that could not be recycled due to its
+         *               transient state.
+         * @return True if the View should be recycled, false otherwise. Note that if this method
+         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
+         * the View and recycle it regardless. If this method returns <code>false</code>,
+         * RecyclerView will check the View's transient state again before giving a final decision.
+         * Default implementation returns false.
+         */
+        public boolean onFailedToRecycleView(VH holder) {
+            return false;
+        }
+
+        /**
+         * Called when a view created by this adapter has been attached to a window.
+         *
+         * <p>This can be used as a reasonable signal that the view is about to be seen
+         * by the user. If the adapter previously freed any resources in
+         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
+         * those resources should be restored here.</p>
+         *
+         * @param holder Holder of the view being attached
+         */
+        public void onViewAttachedToWindow(VH holder) {
+        }
+
+        /**
+         * Called when a view created by this adapter has been detached from its window.
+         *
+         * <p>Becoming detached from the window is not necessarily a permanent condition;
+         * the consumer of an Adapter's views may choose to cache views offscreen while they
+         * are not visible, attaching and detaching them as appropriate.</p>
+         *
+         * @param holder Holder of the view being detached
+         */
+        public void onViewDetachedFromWindow(VH holder) {
+        }
+
+        /**
+         * Returns true if one or more observers are attached to this adapter.
+         *
+         * @return true if this adapter has observers
+         */
+        public final boolean hasObservers() {
+            return mObservable.hasObservers();
+        }
+
+        /**
+         * Register a new observer to listen for data changes.
+         *
+         * <p>The adapter may publish a variety of events describing specific changes.
+         * Not all adapters may support all change types and some may fall back to a generic
+         * {@link com.android.internal.widget.RecyclerView.AdapterDataObserver#onChanged()
+         * "something changed"} event if more specific data is not available.</p>
+         *
+         * <p>Components registering observers with an adapter are responsible for
+         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
+         * unregistering} those observers when finished.</p>
+         *
+         * @param observer Observer to register
+         *
+         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
+         */
+        public void registerAdapterDataObserver(AdapterDataObserver observer) {
+            mObservable.registerObserver(observer);
+        }
+
+        /**
+         * Unregister an observer currently listening for data changes.
+         *
+         * <p>The unregistered observer will no longer receive events about changes
+         * to the adapter.</p>
+         *
+         * @param observer Observer to unregister
+         *
+         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
+         */
+        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
+            mObservable.unregisterObserver(observer);
+        }
+
+        /**
+         * Called by RecyclerView when it starts observing this Adapter.
+         * <p>
+         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
+         *
+         * @param recyclerView The RecyclerView instance which started observing this adapter.
+         * @see #onDetachedFromRecyclerView(RecyclerView)
+         */
+        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
+        }
+
+        /**
+         * Called by RecyclerView when it stops observing this Adapter.
+         *
+         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
+         * @see #onAttachedToRecyclerView(RecyclerView)
+         */
+        public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
+        }
+
+        /**
+         * Notify any registered observers that the data set has changed.
+         *
+         * <p>There are two different classes of data change events, item changes and structural
+         * changes. Item changes are when a single item has its data updated but no positional
+         * changes have occurred. Structural changes are when items are inserted, removed or moved
+         * within the data set.</p>
+         *
+         * <p>This event does not specify what about the data set has changed, forcing
+         * any observers to assume that all existing items and structure may no longer be valid.
+         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
+         *
+         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
+         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
+         * this method is used. This can help for the purposes of animation and visual
+         * object persistence but individual item views will still need to be rebound
+         * and relaid out.</p>
+         *
+         * <p>If you are writing an adapter it will always be more efficient to use the more
+         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
+         * as a last resort.</p>
+         *
+         * @see #notifyItemChanged(int)
+         * @see #notifyItemInserted(int)
+         * @see #notifyItemRemoved(int)
+         * @see #notifyItemRangeChanged(int, int)
+         * @see #notifyItemRangeInserted(int, int)
+         * @see #notifyItemRangeRemoved(int, int)
+         */
+        public final void notifyDataSetChanged() {
+            mObservable.notifyChanged();
+        }
+
+        /**
+         * Notify any registered observers that the item at <code>position</code> has changed.
+         * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
+         *
+         * <p>This is an item change event, not a structural change event. It indicates that any
+         * reflection of the data at <code>position</code> is out of date and should be updated.
+         * The item at <code>position</code> retains the same identity.</p>
+         *
+         * @param position Position of the item that has changed
+         *
+         * @see #notifyItemRangeChanged(int, int)
+         */
+        public final void notifyItemChanged(int position) {
+            mObservable.notifyItemRangeChanged(position, 1);
+        }
+
+        /**
+         * Notify any registered observers that the item at <code>position</code> has changed with
+         * an optional payload object.
+         *
+         * <p>This is an item change event, not a structural change event. It indicates that any
+         * reflection of the data at <code>position</code> is out of date and should be updated.
+         * The item at <code>position</code> retains the same identity.
+         * </p>
+         *
+         * <p>
+         * Client can optionally pass a payload for partial change. These payloads will be merged
+         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
+         * item is already represented by a ViewHolder and it will be rebound to the same
+         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
+         * payloads on that item and prevent future payload until
+         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
+         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
+         * attached, the payload will be simply dropped.
+         *
+         * @param position Position of the item that has changed
+         * @param payload Optional parameter, use null to identify a "full" update
+         *
+         * @see #notifyItemRangeChanged(int, int)
+         */
+        public final void notifyItemChanged(int position, Object payload) {
+            mObservable.notifyItemRangeChanged(position, 1, payload);
+        }
+
+        /**
+         * Notify any registered observers that the <code>itemCount</code> items starting at
+         * position <code>positionStart</code> have changed.
+         * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
+         *
+         * <p>This is an item change event, not a structural change event. It indicates that
+         * any reflection of the data in the given position range is out of date and should
+         * be updated. The items in the given range retain the same identity.</p>
+         *
+         * @param positionStart Position of the first item that has changed
+         * @param itemCount Number of items that have changed
+         *
+         * @see #notifyItemChanged(int)
+         */
+        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
+            mObservable.notifyItemRangeChanged(positionStart, itemCount);
+        }
+
+        /**
+         * Notify any registered observers that the <code>itemCount</code> items starting at
+         * position <code>positionStart</code> have changed. An optional payload can be
+         * passed to each changed item.
+         *
+         * <p>This is an item change event, not a structural change event. It indicates that any
+         * reflection of the data in the given position range is out of date and should be updated.
+         * The items in the given range retain the same identity.
+         * </p>
+         *
+         * <p>
+         * Client can optionally pass a payload for partial change. These payloads will be merged
+         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
+         * item is already represented by a ViewHolder and it will be rebound to the same
+         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
+         * payloads on that item and prevent future payload until
+         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
+         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
+         * attached, the payload will be simply dropped.
+         *
+         * @param positionStart Position of the first item that has changed
+         * @param itemCount Number of items that have changed
+         * @param payload  Optional parameter, use null to identify a "full" update
+         *
+         * @see #notifyItemChanged(int)
+         */
+        public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
+            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
+        }
+
+        /**
+         * Notify any registered observers that the item reflected at <code>position</code>
+         * has been newly inserted. The item previously at <code>position</code> is now at
+         * position <code>position + 1</code>.
+         *
+         * <p>This is a structural change event. Representations of other existing items in the
+         * data set are still considered up to date and will not be rebound, though their
+         * positions may be altered.</p>
+         *
+         * @param position Position of the newly inserted item in the data set
+         *
+         * @see #notifyItemRangeInserted(int, int)
+         */
+        public final void notifyItemInserted(int position) {
+            mObservable.notifyItemRangeInserted(position, 1);
+        }
+
+        /**
+         * Notify any registered observers that the item reflected at <code>fromPosition</code>
+         * has been moved to <code>toPosition</code>.
+         *
+         * <p>This is a structural change event. Representations of other existing items in the
+         * data set are still considered up to date and will not be rebound, though their
+         * positions may be altered.</p>
+         *
+         * @param fromPosition Previous position of the item.
+         * @param toPosition New position of the item.
+         */
+        public final void notifyItemMoved(int fromPosition, int toPosition) {
+            mObservable.notifyItemMoved(fromPosition, toPosition);
+        }
+
+        /**
+         * Notify any registered observers that the currently reflected <code>itemCount</code>
+         * items starting at <code>positionStart</code> have been newly inserted. The items
+         * previously located at <code>positionStart</code> and beyond can now be found starting
+         * at position <code>positionStart + itemCount</code>.
+         *
+         * <p>This is a structural change event. Representations of other existing items in the
+         * data set are still considered up to date and will not be rebound, though their positions
+         * may be altered.</p>
+         *
+         * @param positionStart Position of the first item that was inserted
+         * @param itemCount Number of items inserted
+         *
+         * @see #notifyItemInserted(int)
+         */
+        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
+            mObservable.notifyItemRangeInserted(positionStart, itemCount);
+        }
+
+        /**
+         * Notify any registered observers that the item previously located at <code>position</code>
+         * has been removed from the data set. The items previously located at and after
+         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
+         *
+         * <p>This is a structural change event. Representations of other existing items in the
+         * data set are still considered up to date and will not be rebound, though their positions
+         * may be altered.</p>
+         *
+         * @param position Position of the item that has now been removed
+         *
+         * @see #notifyItemRangeRemoved(int, int)
+         */
+        public final void notifyItemRemoved(int position) {
+            mObservable.notifyItemRangeRemoved(position, 1);
+        }
+
+        /**
+         * Notify any registered observers that the <code>itemCount</code> items previously
+         * located at <code>positionStart</code> have been removed from the data set. The items
+         * previously located at and after <code>positionStart + itemCount</code> may now be found
+         * at <code>oldPosition - itemCount</code>.
+         *
+         * <p>This is a structural change event. Representations of other existing items in the data
+         * set are still considered up to date and will not be rebound, though their positions
+         * may be altered.</p>
+         *
+         * @param positionStart Previous position of the first item that was removed
+         * @param itemCount Number of items removed from the data set
+         */
+        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
+            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
+        }
+    }
+
+    void dispatchChildDetached(View child) {
+        final ViewHolder viewHolder = getChildViewHolderInt(child);
+        onChildDetachedFromWindow(child);
+        if (mAdapter != null && viewHolder != null) {
+            mAdapter.onViewDetachedFromWindow(viewHolder);
+        }
+        if (mOnChildAttachStateListeners != null) {
+            final int cnt = mOnChildAttachStateListeners.size();
+            for (int i = cnt - 1; i >= 0; i--) {
+                mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
+            }
+        }
+    }
+
+    void dispatchChildAttached(View child) {
+        final ViewHolder viewHolder = getChildViewHolderInt(child);
+        onChildAttachedToWindow(child);
+        if (mAdapter != null && viewHolder != null) {
+            mAdapter.onViewAttachedToWindow(viewHolder);
+        }
+        if (mOnChildAttachStateListeners != null) {
+            final int cnt = mOnChildAttachStateListeners.size();
+            for (int i = cnt - 1; i >= 0; i--) {
+                mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
+            }
+        }
+    }
+
+    /**
+     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
+     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
+     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
+     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
+     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
+     * layout managers are provided for general use.
+     * <p/>
+     * If the LayoutManager specifies a default constructor or one with the signature
+     * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
+     * instantiate and set the LayoutManager when being inflated. Most used properties can
+     * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
+     * a LayoutManager specifies both constructors, the non-default constructor will take
+     * precedence.
+     *
+     */
+    public abstract static class LayoutManager {
+        ChildHelper mChildHelper;
+        RecyclerView mRecyclerView;
+
+        @Nullable
+        SmoothScroller mSmoothScroller;
+
+        boolean mRequestedSimpleAnimations = false;
+
+        boolean mIsAttachedToWindow = false;
+
+        boolean mAutoMeasure = false;
+
+        /**
+         * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
+         * if the space that will be given to it is already larger than what it has measured before.
+         */
+        private boolean mMeasurementCacheEnabled = true;
+
+        private boolean mItemPrefetchEnabled = true;
+
+        /**
+         * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
+         * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
+         * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
+         *
+         * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
+         * will be reset upon layout to prevent initial prefetches (often large, since they're
+         * proportional to expected child count) from expanding cache permanently.
+         */
+        int mPrefetchMaxCountObserved;
+
+        /**
+         * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
+         */
+        boolean mPrefetchMaxObservedInInitialPrefetch;
+
+        /**
+         * These measure specs might be the measure specs that were passed into RecyclerView's
+         * onMeasure method OR fake measure specs created by the RecyclerView.
+         * For example, when a layout is run, RecyclerView always sets these specs to be
+         * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
+         * <p>
+         * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
+         * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
+         * corrupt values. Older platforms have no responsibility to provide a size if they set
+         * mode to unspecified.
+         */
+        private int mWidthMode, mHeightMode;
+        private int mWidth, mHeight;
+
+
+        /**
+         * Interface for LayoutManagers to request items to be prefetched, based on position, with
+         * specified distance from viewport, which indicates priority.
+         *
+         * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
+         * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
+         */
+        public interface LayoutPrefetchRegistry {
+            /**
+             * Requests an an item to be prefetched, based on position, with a specified distance,
+             * indicating priority.
+             *
+             * @param layoutPosition Position of the item to prefetch.
+             * @param pixelDistance Distance from the current viewport to the bounds of the item,
+             *                      must be non-negative.
+             */
+            void addPosition(int layoutPosition, int pixelDistance);
+        }
+
+        void setRecyclerView(RecyclerView recyclerView) {
+            if (recyclerView == null) {
+                mRecyclerView = null;
+                mChildHelper = null;
+                mWidth = 0;
+                mHeight = 0;
+            } else {
+                mRecyclerView = recyclerView;
+                mChildHelper = recyclerView.mChildHelper;
+                mWidth = recyclerView.getWidth();
+                mHeight = recyclerView.getHeight();
+            }
+            mWidthMode = MeasureSpec.EXACTLY;
+            mHeightMode = MeasureSpec.EXACTLY;
+        }
+
+        void setMeasureSpecs(int wSpec, int hSpec) {
+            mWidth = MeasureSpec.getSize(wSpec);
+            mWidthMode = MeasureSpec.getMode(wSpec);
+            if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
+                mWidth = 0;
+            }
+
+            mHeight = MeasureSpec.getSize(hSpec);
+            mHeightMode = MeasureSpec.getMode(hSpec);
+            if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
+                mHeight = 0;
+            }
+        }
+
+        /**
+         * Called after a layout is calculated during a measure pass when using auto-measure.
+         * <p>
+         * It simply traverses all children to calculate a bounding box then calls
+         * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
+         * if they need to handle the bounding box differently.
+         * <p>
+         * For example, GridLayoutManager override that method to ensure that even if a column is
+         * empty, the GridLayoutManager still measures wide enough to include it.
+         *
+         * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
+         * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
+         */
+        void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
+            final int count = getChildCount();
+            if (count == 0) {
+                mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
+                return;
+            }
+            int minX = Integer.MAX_VALUE;
+            int minY = Integer.MAX_VALUE;
+            int maxX = Integer.MIN_VALUE;
+            int maxY = Integer.MIN_VALUE;
+
+            for (int i = 0; i < count; i++) {
+                View child = getChildAt(i);
+                final Rect bounds = mRecyclerView.mTempRect;
+                getDecoratedBoundsWithMargins(child, bounds);
+                if (bounds.left < minX) {
+                    minX = bounds.left;
+                }
+                if (bounds.right > maxX) {
+                    maxX = bounds.right;
+                }
+                if (bounds.top < minY) {
+                    minY = bounds.top;
+                }
+                if (bounds.bottom > maxY) {
+                    maxY = bounds.bottom;
+                }
+            }
+            mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
+            setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
+        }
+
+        /**
+         * Sets the measured dimensions from the given bounding box of the children and the
+         * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
+         * called after the RecyclerView calls
+         * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
+         * <p>
+         * This method should call {@link #setMeasuredDimension(int, int)}.
+         * <p>
+         * The default implementation adds the RecyclerView's padding to the given bounding box
+         * then caps the value to be within the given measurement specs.
+         * <p>
+         * This method is only called if the LayoutManager opted into the auto measurement API.
+         *
+         * @param childrenBounds The bounding box of all children
+         * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
+         * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
+         *
+         * @see #setAutoMeasureEnabled(boolean)
+         */
+        public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
+            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
+            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
+            int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
+            int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
+            setMeasuredDimension(width, height);
+        }
+
+        /**
+         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
+         */
+        public void requestLayout() {
+            if (mRecyclerView != null) {
+                mRecyclerView.requestLayout();
+            }
+        }
+
+        /**
+         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
+         * {@link IllegalStateException} if it <b>is not</b>.
+         *
+         * @param message The message for the exception. Can be null.
+         * @see #assertNotInLayoutOrScroll(String)
+         */
+        public void assertInLayoutOrScroll(String message) {
+            if (mRecyclerView != null) {
+                mRecyclerView.assertInLayoutOrScroll(message);
+            }
+        }
+
+        /**
+         * Chooses a size from the given specs and parameters that is closest to the desired size
+         * and also complies with the spec.
+         *
+         * @param spec The measureSpec
+         * @param desired The preferred measurement
+         * @param min The minimum value
+         *
+         * @return A size that fits to the given specs
+         */
+        public static int chooseSize(int spec, int desired, int min) {
+            final int mode = View.MeasureSpec.getMode(spec);
+            final int size = View.MeasureSpec.getSize(spec);
+            switch (mode) {
+                case View.MeasureSpec.EXACTLY:
+                    return size;
+                case View.MeasureSpec.AT_MOST:
+                    return Math.min(size, Math.max(desired, min));
+                case View.MeasureSpec.UNSPECIFIED:
+                default:
+                    return Math.max(desired, min);
+            }
+        }
+
+        /**
+         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
+         * {@link IllegalStateException} if it <b>is</b>.
+         *
+         * @param message The message for the exception. Can be null.
+         * @see #assertInLayoutOrScroll(String)
+         */
+        public void assertNotInLayoutOrScroll(String message) {
+            if (mRecyclerView != null) {
+                mRecyclerView.assertNotInLayoutOrScroll(message);
+            }
+        }
+
+        /**
+         * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
+         * wants to handle the layout measurements itself.
+         * <p>
+         * This method is usually called by the LayoutManager with value {@code true} if it wants
+         * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
+         * the measurement logic, you can call this method with {@code false} and override
+         * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
+         * <p>
+         * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
+         * handle various specs provided by the RecyclerView's parent.
+         * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
+         * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
+         * on children's positions. It does this while supporting all existing animation
+         * capabilities of the RecyclerView.
+         * <p>
+         * AutoMeasure works as follows:
+         * <ol>
+         * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
+         * the framework LayoutManagers use {@code auto-measure}.</li>
+         * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
+         * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
+         * doing any layout calculation.</li>
+         * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
+         * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
+         * decide whether to run a predictive layout or not. If it decides to do so, it will first
+         * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
+         * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
+         * return the width and height of the RecyclerView as of the last layout calculation.
+         * <p>
+         * After handling the predictive case, RecyclerView will call
+         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
+         * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
+         * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
+         * {@link #getWidth()} and {@link #getWidthMode()}.</li>
+         * <li>After the layout calculation, RecyclerView sets the measured width & height by
+         * calculating the bounding box for the children (+ RecyclerView's padding). The
+         * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
+         * different values. For instance, GridLayoutManager overrides this value to handle the case
+         * where if it is vertical and has 3 columns but only 2 items, it should still measure its
+         * width to fit 3 items, not 2.</li>
+         * <li>Any following on measure call to the RecyclerView will run
+         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
+         * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
+         * take care of which views are actually added / removed / moved / changed for animations so
+         * that the LayoutManager should not worry about them and handle each
+         * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
+         * </li>
+         * <li>When measure is complete and RecyclerView's
+         * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
+         * whether it already did layout calculations during the measure pass and if so, it re-uses
+         * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
+         * if the last measure spec was different from the final dimensions or adapter contents
+         * have changed between the measure call and the layout call.</li>
+         * <li>Finally, animations are calculated and run as usual.</li>
+         * </ol>
+         *
+         * @param enabled <code>True</code> if the Layout should be measured by the
+         *                             RecyclerView, <code>false</code> if the LayoutManager wants
+         *                             to measure itself.
+         *
+         * @see #setMeasuredDimension(Rect, int, int)
+         * @see #isAutoMeasureEnabled()
+         */
+        public void setAutoMeasureEnabled(boolean enabled) {
+            mAutoMeasure = enabled;
+        }
+
+        /**
+         * Returns whether the LayoutManager uses the automatic measurement API or not.
+         *
+         * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
+         * <code>false</code> if it measures itself.
+         *
+         * @see #setAutoMeasureEnabled(boolean)
+         */
+        public boolean isAutoMeasureEnabled() {
+            return mAutoMeasure;
+        }
+
+        /**
+         * Returns whether this LayoutManager supports automatic item animations.
+         * A LayoutManager wishing to support item animations should obey certain
+         * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
+         * The default return value is <code>false</code>, so subclasses of LayoutManager
+         * will not get predictive item animations by default.
+         *
+         * <p>Whether item animations are enabled in a RecyclerView is determined both
+         * by the return value from this method and the
+         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
+         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
+         * method returns false, then simple item animations will be enabled, in which
+         * views that are moving onto or off of the screen are simply faded in/out. If
+         * the RecyclerView has a non-null ItemAnimator and this method returns true,
+         * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
+         * setup up the information needed to more intelligently predict where appearing
+         * and disappearing views should be animated from/to.</p>
+         *
+         * @return true if predictive item animations should be enabled, false otherwise
+         */
+        public boolean supportsPredictiveItemAnimations() {
+            return false;
+        }
+
+        /**
+         * Sets whether the LayoutManager should be queried for views outside of
+         * its viewport while the UI thread is idle between frames.
+         *
+         * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
+         * view system traversals on devices running API 21 or greater. Default value is true.</p>
+         *
+         * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
+         * to RenderThread and the starting up its next frame at the next VSync pulse. By
+         * prefetching out of window views in this time period, delays from inflation and view
+         * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
+         *
+         * <p>While prefetch is enabled, it will have the side effect of expanding the effective
+         * size of the View cache to hold prefetched views.</p>
+         *
+         * @param enabled <code>True</code> if items should be prefetched in between traversals.
+         *
+         * @see #isItemPrefetchEnabled()
+         */
+        public final void setItemPrefetchEnabled(boolean enabled) {
+            if (enabled != mItemPrefetchEnabled) {
+                mItemPrefetchEnabled = enabled;
+                mPrefetchMaxCountObserved = 0;
+                if (mRecyclerView != null) {
+                    mRecyclerView.mRecycler.updateViewCacheSize();
+                }
+            }
+        }
+
+        /**
+         * Sets whether the LayoutManager should be queried for views outside of
+         * its viewport while the UI thread is idle between frames.
+         *
+         * @see #setItemPrefetchEnabled(boolean)
+         *
+         * @return true if item prefetch is enabled, false otherwise
+         */
+        public final boolean isItemPrefetchEnabled() {
+            return mItemPrefetchEnabled;
+        }
+
+        /**
+         * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
+         *
+         * <p>If item prefetch is enabled, this method is called in between traversals to gather
+         * which positions the LayoutManager will soon need, given upcoming movement in subsequent
+         * traversals.</p>
+         *
+         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
+         * each item to be prepared, and these positions will have their ViewHolders created and
+         * bound, if there is sufficient time available, in advance of being needed by a
+         * scroll or layout.</p>
+         *
+         * @param dx X movement component.
+         * @param dy Y movement component.
+         * @param state State of RecyclerView
+         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
+         *
+         * @see #isItemPrefetchEnabled()
+         * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
+         */
+        public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
+                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
+
+        /**
+         * Gather all positions from the LayoutManager to be prefetched in preperation for its
+         * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
+         *
+         * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
+         *
+         * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
+         * LayoutManager, this method is called in between draw traversals to gather
+         * which positions this LayoutManager will first need, once it appears on the screen.</p>
+         *
+         * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
+         * vertically scrolling LayoutManager, this method would be called when the horizontal list
+         * is about to come onscreen.</p>
+         *
+         * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
+         * each item to be prepared, and these positions will have their ViewHolders created and
+         * bound, if there is sufficient time available, in advance of being needed by a
+         * scroll or layout.</p>
+         *
+         * @param adapterItemCount number of items in the associated adapter.
+         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
+         *
+         * @see #isItemPrefetchEnabled()
+         * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
+         */
+        public void collectInitialPrefetchPositions(int adapterItemCount,
+                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
+
+        void dispatchAttachedToWindow(RecyclerView view) {
+            mIsAttachedToWindow = true;
+            onAttachedToWindow(view);
+        }
+
+        void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
+            mIsAttachedToWindow = false;
+            onDetachedFromWindow(view, recycler);
+        }
+
+        /**
+         * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
+         * to a window.
+         *
+         * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
+         * is attached to window.
+         */
+        public boolean isAttachedToWindow() {
+            return mIsAttachedToWindow;
+        }
+
+        /**
+         * Causes the Runnable to execute on the next animation time step.
+         * The runnable will be run on the user interface thread.
+         * <p>
+         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
+         *
+         * @param action The Runnable that will be executed.
+         *
+         * @see #removeCallbacks
+         */
+        public void postOnAnimation(Runnable action) {
+            if (mRecyclerView != null) {
+                mRecyclerView.postOnAnimation(action);
+            }
+        }
+
+        /**
+         * Removes the specified Runnable from the message queue.
+         * <p>
+         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
+         *
+         * @param action The Runnable to remove from the message handling queue
+         *
+         * @return true if RecyclerView could ask the Handler to remove the Runnable,
+         *         false otherwise. When the returned value is true, the Runnable
+         *         may or may not have been actually removed from the message queue
+         *         (for instance, if the Runnable was not in the queue already.)
+         *
+         * @see #postOnAnimation
+         */
+        public boolean removeCallbacks(Runnable action) {
+            if (mRecyclerView != null) {
+                return mRecyclerView.removeCallbacks(action);
+            }
+            return false;
+        }
+        /**
+         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
+         * is attached to a window.
+         * <p>
+         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
+         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
+         * not requested on the RecyclerView while it was detached.
+         * <p>
+         * Subclass implementations should always call through to the superclass implementation.
+         *
+         * @param view The RecyclerView this LayoutManager is bound to
+         *
+         * @see #onDetachedFromWindow(RecyclerView, Recycler)
+         */
+        @CallSuper
+        public void onAttachedToWindow(RecyclerView view) {
+        }
+
+        /**
+         * @deprecated
+         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
+         */
+        @Deprecated
+        public void onDetachedFromWindow(RecyclerView view) {
+
+        }
+
+        /**
+         * Called when this LayoutManager is detached from its parent RecyclerView or when
+         * its parent RecyclerView is detached from its window.
+         * <p>
+         * LayoutManager should clear all of its View references as another LayoutManager might be
+         * assigned to the RecyclerView.
+         * <p>
+         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
+         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
+         * not requested on the RecyclerView while it was detached.
+         * <p>
+         * If your LayoutManager has View references that it cleans in on-detach, it should also
+         * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
+         * RecyclerView is re-attached.
+         * <p>
+         * Subclass implementations should always call through to the superclass implementation.
+         *
+         * @param view The RecyclerView this LayoutManager is bound to
+         * @param recycler The recycler to use if you prefer to recycle your children instead of
+         *                 keeping them around.
+         *
+         * @see #onAttachedToWindow(RecyclerView)
+         */
+        @CallSuper
+        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
+            onDetachedFromWindow(view);
+        }
+
+        /**
+         * Check if the RecyclerView is configured to clip child views to its padding.
+         *
+         * @return true if this RecyclerView clips children to its padding, false otherwise
+         */
+        public boolean getClipToPadding() {
+            return mRecyclerView != null && mRecyclerView.mClipToPadding;
+        }
+
+        /**
+         * Lay out all relevant child views from the given adapter.
+         *
+         * The LayoutManager is in charge of the behavior of item animations. By default,
+         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
+         * item animations are enabled. This means that add/remove operations on the
+         * adapter will result in animations to add new or appearing items, removed or
+         * disappearing items, and moved items. If a LayoutManager returns false from
+         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
+         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
+         * RecyclerView will have enough information to run those animations in a simple
+         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
+         * simply fade views in and out, whether they are actually added/removed or whether
+         * they are moved on or off the screen due to other add/remove operations.
+         *
+         * <p>A LayoutManager wanting a better item animation experience, where items can be
+         * animated onto and off of the screen according to where the items exist when they
+         * are not on screen, then the LayoutManager should return true from
+         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
+         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
+         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
+         * once as a "pre" layout step to determine where items would have been prior to
+         * a real layout, and again to do the "real" layout. In the pre-layout phase,
+         * items will remember their pre-layout positions to allow them to be laid out
+         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
+         * be returned from the scrap to help determine correct placement of other items.
+         * These removed items should not be added to the child list, but should be used
+         * to help calculate correct positioning of other views, including views that
+         * were not previously onscreen (referred to as APPEARING views), but whose
+         * pre-layout offscreen position can be determined given the extra
+         * information about the pre-layout removed views.</p>
+         *
+         * <p>The second layout pass is the real layout in which only non-removed views
+         * will be used. The only additional requirement during this pass is, if
+         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
+         * views exist in the child list prior to layout and which are not there after
+         * layout (referred to as DISAPPEARING views), and to position/layout those views
+         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
+         * the animation system to know the location to which to animate these disappearing
+         * views.</p>
+         *
+         * <p>The default LayoutManager implementations for RecyclerView handle all of these
+         * requirements for animations already. Clients of RecyclerView can either use one
+         * of these layout managers directly or look at their implementations of
+         * onLayoutChildren() to see how they account for the APPEARING and
+         * DISAPPEARING views.</p>
+         *
+         * @param recycler         Recycler to use for fetching potentially cached views for a
+         *                         position
+         * @param state            Transient state of RecyclerView
+         */
+        public void onLayoutChildren(Recycler recycler, State state) {
+            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
+        }
+
+        /**
+         * Called after a full layout calculation is finished. The layout calculation may include
+         * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
+         * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
+         * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
+         * <p>
+         * This is a good place for the LayoutManager to do some cleanup like pending scroll
+         * position, saved state etc.
+         *
+         * @param state Transient state of RecyclerView
+         */
+        public void onLayoutCompleted(State state) {
+        }
+
+        /**
+         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
+         *
+         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
+         * to store extra information specific to the layout. Client code should subclass
+         * {@link RecyclerView.LayoutParams} for this purpose.</p>
+         *
+         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
+         * you must also override
+         * {@link #checkLayoutParams(LayoutParams)},
+         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
+         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
+         *
+         * @return A new LayoutParams for a child view
+         */
+        public abstract LayoutParams generateDefaultLayoutParams();
+
+        /**
+         * Determines the validity of the supplied LayoutParams object.
+         *
+         * <p>This should check to make sure that the object is of the correct type
+         * and all values are within acceptable ranges. The default implementation
+         * returns <code>true</code> for non-null params.</p>
+         *
+         * @param lp LayoutParams object to check
+         * @return true if this LayoutParams object is valid, false otherwise
+         */
+        public boolean checkLayoutParams(LayoutParams lp) {
+            return lp != null;
+        }
+
+        /**
+         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
+         * values from the supplied LayoutParams object if possible.
+         *
+         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
+         * you must also override
+         * {@link #checkLayoutParams(LayoutParams)},
+         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
+         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
+         *
+         * @param lp Source LayoutParams object to copy values from
+         * @return a new LayoutParams object
+         */
+        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+            if (lp instanceof LayoutParams) {
+                return new LayoutParams((LayoutParams) lp);
+            } else if (lp instanceof MarginLayoutParams) {
+                return new LayoutParams((MarginLayoutParams) lp);
+            } else {
+                return new LayoutParams(lp);
+            }
+        }
+
+        /**
+         * Create a LayoutParams object suitable for this LayoutManager from
+         * an inflated layout resource.
+         *
+         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
+         * you must also override
+         * {@link #checkLayoutParams(LayoutParams)},
+         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
+         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
+         *
+         * @param c Context for obtaining styled attributes
+         * @param attrs AttributeSet describing the supplied arguments
+         * @return a new LayoutParams object
+         */
+        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
+            return new LayoutParams(c, attrs);
+        }
+
+        /**
+         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
+         * The default implementation does nothing and returns 0.
+         *
+         * @param dx            distance to scroll by in pixels. X increases as scroll position
+         *                      approaches the right.
+         * @param recycler      Recycler to use for fetching potentially cached views for a
+         *                      position
+         * @param state         Transient state of RecyclerView
+         * @return The actual distance scrolled. The return value will be negative if dx was
+         * negative and scrolling proceeeded in that direction.
+         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
+         */
+        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
+            return 0;
+        }
+
+        /**
+         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
+         * The default implementation does nothing and returns 0.
+         *
+         * @param dy            distance to scroll in pixels. Y increases as scroll position
+         *                      approaches the bottom.
+         * @param recycler      Recycler to use for fetching potentially cached views for a
+         *                      position
+         * @param state         Transient state of RecyclerView
+         * @return The actual distance scrolled. The return value will be negative if dy was
+         * negative and scrolling proceeeded in that direction.
+         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
+         */
+        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
+            return 0;
+        }
+
+        /**
+         * Query if horizontal scrolling is currently supported. The default implementation
+         * returns false.
+         *
+         * @return True if this LayoutManager can scroll the current contents horizontally
+         */
+        public boolean canScrollHorizontally() {
+            return false;
+        }
+
+        /**
+         * Query if vertical scrolling is currently supported. The default implementation
+         * returns false.
+         *
+         * @return True if this LayoutManager can scroll the current contents vertically
+         */
+        public boolean canScrollVertically() {
+            return false;
+        }
+
+        /**
+         * Scroll to the specified adapter position.
+         *
+         * Actual position of the item on the screen depends on the LayoutManager implementation.
+         * @param position Scroll to this adapter position.
+         */
+        public void scrollToPosition(int position) {
+            if (DEBUG) {
+                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
+            }
+        }
+
+        /**
+         * <p>Smooth scroll to the specified adapter position.</p>
+         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
+         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
+         * </p>
+         * @param recyclerView The RecyclerView to which this layout manager is attached
+         * @param state    Current State of RecyclerView
+         * @param position Scroll to this adapter position.
+         */
+        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
+                int position) {
+            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
+        }
+
+        /**
+         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
+         * <p>Calling this method will cancel any previous smooth scroll request.</p>
+         * @param smoothScroller Instance which defines how smooth scroll should be animated
+         */
+        public void startSmoothScroll(SmoothScroller smoothScroller) {
+            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
+                    && mSmoothScroller.isRunning()) {
+                mSmoothScroller.stop();
+            }
+            mSmoothScroller = smoothScroller;
+            mSmoothScroller.start(mRecyclerView, this);
+        }
+
+        /**
+         * @return true if RecycylerView is currently in the state of smooth scrolling.
+         */
+        public boolean isSmoothScrolling() {
+            return mSmoothScroller != null && mSmoothScroller.isRunning();
+        }
+
+
+        /**
+         * Returns the resolved layout direction for this RecyclerView.
+         *
+         * @return {@link android.view.View#LAYOUT_DIRECTION_RTL} if the layout
+         * direction is RTL or returns
+         * {@link android.view.View#LAYOUT_DIRECTION_LTR} if the layout direction
+         * is not RTL.
+         */
+        public int getLayoutDirection() {
+            return mRecyclerView.getLayoutDirection();
+        }
+
+        /**
+         * Ends all animations on the view created by the {@link ItemAnimator}.
+         *
+         * @param view The View for which the animations should be ended.
+         * @see RecyclerView.ItemAnimator#endAnimations()
+         */
+        public void endAnimation(View view) {
+            if (mRecyclerView.mItemAnimator != null) {
+                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
+            }
+        }
+
+        /**
+         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
+         * to the layout that is known to be going away, either because it has been
+         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
+         * visible portion of the container but is being laid out in order to inform RecyclerView
+         * in how to animate the item out of view.
+         * <p>
+         * Views added via this method are going to be invisible to LayoutManager after the
+         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
+         * or won't be included in {@link #getChildCount()} method.
+         *
+         * @param child View to add and then remove with animation.
+         */
+        public void addDisappearingView(View child) {
+            addDisappearingView(child, -1);
+        }
+
+        /**
+         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
+         * to the layout that is known to be going away, either because it has been
+         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
+         * visible portion of the container but is being laid out in order to inform RecyclerView
+         * in how to animate the item out of view.
+         * <p>
+         * Views added via this method are going to be invisible to LayoutManager after the
+         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
+         * or won't be included in {@link #getChildCount()} method.
+         *
+         * @param child View to add and then remove with animation.
+         * @param index Index of the view.
+         */
+        public void addDisappearingView(View child, int index) {
+            addViewInt(child, index, true);
+        }
+
+        /**
+         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
+         * use this method to add views obtained from a {@link Recycler} using
+         * {@link Recycler#getViewForPosition(int)}.
+         *
+         * @param child View to add
+         */
+        public void addView(View child) {
+            addView(child, -1);
+        }
+
+        /**
+         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
+         * use this method to add views obtained from a {@link Recycler} using
+         * {@link Recycler#getViewForPosition(int)}.
+         *
+         * @param child View to add
+         * @param index Index to add child at
+         */
+        public void addView(View child, int index) {
+            addViewInt(child, index, false);
+        }
+
+        private void addViewInt(View child, int index, boolean disappearing) {
+            final ViewHolder holder = getChildViewHolderInt(child);
+            if (disappearing || holder.isRemoved()) {
+                // these views will be hidden at the end of the layout pass.
+                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
+            } else {
+                // This may look like unnecessary but may happen if layout manager supports
+                // predictive layouts and adapter removed then re-added the same item.
+                // In this case, added version will be visible in the post layout (because add is
+                // deferred) but RV will still bind it to the same View.
+                // So if a View re-appears in post layout pass, remove it from disappearing list.
+                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
+            }
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
+                if (holder.isScrap()) {
+                    holder.unScrap();
+                } else {
+                    holder.clearReturnedFromScrapFlag();
+                }
+                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
+                if (DISPATCH_TEMP_DETACH) {
+                    child.dispatchFinishTemporaryDetach();
+                }
+            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
+                // ensure in correct position
+                int currentIndex = mChildHelper.indexOfChild(child);
+                if (index == -1) {
+                    index = mChildHelper.getChildCount();
+                }
+                if (currentIndex == -1) {
+                    throw new IllegalStateException("Added View has RecyclerView as parent but"
+                            + " view is not a real child. Unfiltered index:"
+                            + mRecyclerView.indexOfChild(child));
+                }
+                if (currentIndex != index) {
+                    mRecyclerView.mLayout.moveView(currentIndex, index);
+                }
+            } else {
+                mChildHelper.addView(child, index, false);
+                lp.mInsetsDirty = true;
+                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
+                    mSmoothScroller.onChildAttachedToWindow(child);
+                }
+            }
+            if (lp.mPendingInvalidate) {
+                if (DEBUG) {
+                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
+                }
+                holder.itemView.invalidate();
+                lp.mPendingInvalidate = false;
+            }
+        }
+
+        /**
+         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
+         * use this method to completely remove a child view that is no longer needed.
+         * LayoutManagers should strongly consider recycling removed views using
+         * {@link Recycler#recycleView(android.view.View)}.
+         *
+         * @param child View to remove
+         */
+        public void removeView(View child) {
+            mChildHelper.removeView(child);
+        }
+
+        /**
+         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
+         * use this method to completely remove a child view that is no longer needed.
+         * LayoutManagers should strongly consider recycling removed views using
+         * {@link Recycler#recycleView(android.view.View)}.
+         *
+         * @param index Index of the child view to remove
+         */
+        public void removeViewAt(int index) {
+            final View child = getChildAt(index);
+            if (child != null) {
+                mChildHelper.removeViewAt(index);
+            }
+        }
+
+        /**
+         * Remove all views from the currently attached RecyclerView. This will not recycle
+         * any of the affected views; the LayoutManager is responsible for doing so if desired.
+         */
+        public void removeAllViews() {
+            // Only remove non-animating views
+            final int childCount = getChildCount();
+            for (int i = childCount - 1; i >= 0; i--) {
+                mChildHelper.removeViewAt(i);
+            }
+        }
+
+        /**
+         * Returns offset of the RecyclerView's text baseline from the its top boundary.
+         *
+         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
+         * there is no baseline.
+         */
+        public int getBaseline() {
+            return -1;
+        }
+
+        /**
+         * Returns the adapter position of the item represented by the given View. This does not
+         * contain any adapter changes that might have happened after the last layout.
+         *
+         * @param view The view to query
+         * @return The adapter position of the item which is rendered by this View.
+         */
+        public int getPosition(View view) {
+            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
+        }
+
+        /**
+         * Returns the View type defined by the adapter.
+         *
+         * @param view The view to query
+         * @return The type of the view assigned by the adapter.
+         */
+        public int getItemViewType(View view) {
+            return getChildViewHolderInt(view).getItemViewType();
+        }
+
+        /**
+         * Traverses the ancestors of the given view and returns the item view that contains it
+         * and also a direct child of the LayoutManager.
+         * <p>
+         * Note that this method may return null if the view is a child of the RecyclerView but
+         * not a child of the LayoutManager (e.g. running a disappear animation).
+         *
+         * @param view The view that is a descendant of the LayoutManager.
+         *
+         * @return The direct child of the LayoutManager which contains the given view or null if
+         * the provided view is not a descendant of this LayoutManager.
+         *
+         * @see RecyclerView#getChildViewHolder(View)
+         * @see RecyclerView#findContainingViewHolder(View)
+         */
+        @Nullable
+        public View findContainingItemView(View view) {
+            if (mRecyclerView == null) {
+                return null;
+            }
+            View found = mRecyclerView.findContainingItemView(view);
+            if (found == null) {
+                return null;
+            }
+            if (mChildHelper.isHidden(found)) {
+                return null;
+            }
+            return found;
+        }
+
+        /**
+         * Finds the view which represents the given adapter position.
+         * <p>
+         * This method traverses each child since it has no information about child order.
+         * Override this method to improve performance if your LayoutManager keeps data about
+         * child views.
+         * <p>
+         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
+         *
+         * @param position Position of the item in adapter
+         * @return The child view that represents the given position or null if the position is not
+         * laid out
+         */
+        public View findViewByPosition(int position) {
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                ViewHolder vh = getChildViewHolderInt(child);
+                if (vh == null) {
+                    continue;
+                }
+                if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
+                        && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
+                    return child;
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Temporarily detach a child view.
+         *
+         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
+         * views currently attached to the RecyclerView. Generally LayoutManager implementations
+         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
+         * so that the detached view may be rebound and reused.</p>
+         *
+         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
+         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
+         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
+         * before the LayoutManager entry point method called by RecyclerView returns.</p>
+         *
+         * @param child Child to detach
+         */
+        public void detachView(View child) {
+            final int ind = mChildHelper.indexOfChild(child);
+            if (ind >= 0) {
+                detachViewInternal(ind, child);
+            }
+        }
+
+        /**
+         * Temporarily detach a child view.
+         *
+         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
+         * views currently attached to the RecyclerView. Generally LayoutManager implementations
+         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
+         * so that the detached view may be rebound and reused.</p>
+         *
+         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
+         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
+         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
+         * before the LayoutManager entry point method called by RecyclerView returns.</p>
+         *
+         * @param index Index of the child to detach
+         */
+        public void detachViewAt(int index) {
+            detachViewInternal(index, getChildAt(index));
+        }
+
+        private void detachViewInternal(int index, View view) {
+            if (DISPATCH_TEMP_DETACH) {
+                view.dispatchStartTemporaryDetach();
+            }
+            mChildHelper.detachViewFromParent(index);
+        }
+
+        /**
+         * Reattach a previously {@link #detachView(android.view.View) detached} view.
+         * This method should not be used to reattach views that were previously
+         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
+         *
+         * @param child Child to reattach
+         * @param index Intended child index for child
+         * @param lp LayoutParams for child
+         */
+        public void attachView(View child, int index, LayoutParams lp) {
+            ViewHolder vh = getChildViewHolderInt(child);
+            if (vh.isRemoved()) {
+                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
+            } else {
+                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
+            }
+            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
+            if (DISPATCH_TEMP_DETACH)  {
+                child.dispatchFinishTemporaryDetach();
+            }
+        }
+
+        /**
+         * Reattach a previously {@link #detachView(android.view.View) detached} view.
+         * This method should not be used to reattach views that were previously
+         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
+         *
+         * @param child Child to reattach
+         * @param index Intended child index for child
+         */
+        public void attachView(View child, int index) {
+            attachView(child, index, (LayoutParams) child.getLayoutParams());
+        }
+
+        /**
+         * Reattach a previously {@link #detachView(android.view.View) detached} view.
+         * This method should not be used to reattach views that were previously
+         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
+         *
+         * @param child Child to reattach
+         */
+        public void attachView(View child) {
+            attachView(child, -1);
+        }
+
+        /**
+         * Finish removing a view that was previously temporarily
+         * {@link #detachView(android.view.View) detached}.
+         *
+         * @param child Detached child to remove
+         */
+        public void removeDetachedView(View child) {
+            mRecyclerView.removeDetachedView(child, false);
+        }
+
+        /**
+         * Moves a View from one position to another.
+         *
+         * @param fromIndex The View's initial index
+         * @param toIndex The View's target index
+         */
+        public void moveView(int fromIndex, int toIndex) {
+            View view = getChildAt(fromIndex);
+            if (view == null) {
+                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
+                        + fromIndex);
+            }
+            detachViewAt(fromIndex);
+            attachView(view, toIndex);
+        }
+
+        /**
+         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
+         *
+         * <p>Scrapping a view allows it to be rebound and reused to show updated or
+         * different data.</p>
+         *
+         * @param child Child to detach and scrap
+         * @param recycler Recycler to deposit the new scrap view into
+         */
+        public void detachAndScrapView(View child, Recycler recycler) {
+            int index = mChildHelper.indexOfChild(child);
+            scrapOrRecycleView(recycler, index, child);
+        }
+
+        /**
+         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
+         *
+         * <p>Scrapping a view allows it to be rebound and reused to show updated or
+         * different data.</p>
+         *
+         * @param index Index of child to detach and scrap
+         * @param recycler Recycler to deposit the new scrap view into
+         */
+        public void detachAndScrapViewAt(int index, Recycler recycler) {
+            final View child = getChildAt(index);
+            scrapOrRecycleView(recycler, index, child);
+        }
+
+        /**
+         * Remove a child view and recycle it using the given Recycler.
+         *
+         * @param child Child to remove and recycle
+         * @param recycler Recycler to use to recycle child
+         */
+        public void removeAndRecycleView(View child, Recycler recycler) {
+            removeView(child);
+            recycler.recycleView(child);
+        }
+
+        /**
+         * Remove a child view and recycle it using the given Recycler.
+         *
+         * @param index Index of child to remove and recycle
+         * @param recycler Recycler to use to recycle child
+         */
+        public void removeAndRecycleViewAt(int index, Recycler recycler) {
+            final View view = getChildAt(index);
+            removeViewAt(index);
+            recycler.recycleView(view);
+        }
+
+        /**
+         * Return the current number of child views attached to the parent RecyclerView.
+         * This does not include child views that were temporarily detached and/or scrapped.
+         *
+         * @return Number of attached children
+         */
+        public int getChildCount() {
+            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
+        }
+
+        /**
+         * Return the child view at the given index
+         * @param index Index of child to return
+         * @return Child view at index
+         */
+        public View getChildAt(int index) {
+            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
+        }
+
+        /**
+         * Return the width measurement spec mode of the RecyclerView.
+         * <p>
+         * This value is set only if the LayoutManager opts into the auto measure api via
+         * {@link #setAutoMeasureEnabled(boolean)}.
+         * <p>
+         * When RecyclerView is running a layout, this value is always set to
+         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
+         *
+         * @return Width measure spec mode.
+         *
+         * @see View.MeasureSpec#getMode(int)
+         * @see View#onMeasure(int, int)
+         */
+        public int getWidthMode() {
+            return mWidthMode;
+        }
+
+        /**
+         * Return the height measurement spec mode of the RecyclerView.
+         * <p>
+         * This value is set only if the LayoutManager opts into the auto measure api via
+         * {@link #setAutoMeasureEnabled(boolean)}.
+         * <p>
+         * When RecyclerView is running a layout, this value is always set to
+         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
+         *
+         * @return Height measure spec mode.
+         *
+         * @see View.MeasureSpec#getMode(int)
+         * @see View#onMeasure(int, int)
+         */
+        public int getHeightMode() {
+            return mHeightMode;
+        }
+
+        /**
+         * Return the width of the parent RecyclerView
+         *
+         * @return Width in pixels
+         */
+        public int getWidth() {
+            return mWidth;
+        }
+
+        /**
+         * Return the height of the parent RecyclerView
+         *
+         * @return Height in pixels
+         */
+        public int getHeight() {
+            return mHeight;
+        }
+
+        /**
+         * Return the left padding of the parent RecyclerView
+         *
+         * @return Padding in pixels
+         */
+        public int getPaddingLeft() {
+            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
+        }
+
+        /**
+         * Return the top padding of the parent RecyclerView
+         *
+         * @return Padding in pixels
+         */
+        public int getPaddingTop() {
+            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
+        }
+
+        /**
+         * Return the right padding of the parent RecyclerView
+         *
+         * @return Padding in pixels
+         */
+        public int getPaddingRight() {
+            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
+        }
+
+        /**
+         * Return the bottom padding of the parent RecyclerView
+         *
+         * @return Padding in pixels
+         */
+        public int getPaddingBottom() {
+            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
+        }
+
+        /**
+         * Return the start padding of the parent RecyclerView
+         *
+         * @return Padding in pixels
+         */
+        public int getPaddingStart() {
+            return mRecyclerView != null ? mRecyclerView.getPaddingStart() : 0;
+        }
+
+        /**
+         * Return the end padding of the parent RecyclerView
+         *
+         * @return Padding in pixels
+         */
+        public int getPaddingEnd() {
+            return mRecyclerView != null ? mRecyclerView.getPaddingEnd() : 0;
+        }
+
+        /**
+         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
+         *
+         * @return True if the RecyclerView has focus, false otherwise.
+         * @see View#isFocused()
+         */
+        public boolean isFocused() {
+            return mRecyclerView != null && mRecyclerView.isFocused();
+        }
+
+        /**
+         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
+         *
+         * @return true if the RecyclerView has or contains focus
+         * @see View#hasFocus()
+         */
+        public boolean hasFocus() {
+            return mRecyclerView != null && mRecyclerView.hasFocus();
+        }
+
+        /**
+         * Returns the item View which has or contains focus.
+         *
+         * @return A direct child of RecyclerView which has focus or contains the focused child.
+         */
+        public View getFocusedChild() {
+            if (mRecyclerView == null) {
+                return null;
+            }
+            final View focused = mRecyclerView.getFocusedChild();
+            if (focused == null || mChildHelper.isHidden(focused)) {
+                return null;
+            }
+            return focused;
+        }
+
+        /**
+         * Returns the number of items in the adapter bound to the parent RecyclerView.
+         * <p>
+         * Note that this number is not necessarily equal to
+         * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
+         * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
+         * For more details, check the documentation for
+         * {@link State#getItemCount() State#getItemCount()}.
+         *
+         * @return The number of items in the bound adapter
+         * @see State#getItemCount()
+         */
+        public int getItemCount() {
+            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
+            return a != null ? a.getItemCount() : 0;
+        }
+
+        /**
+         * Offset all child views attached to the parent RecyclerView by dx pixels along
+         * the horizontal axis.
+         *
+         * @param dx Pixels to offset by
+         */
+        public void offsetChildrenHorizontal(int dx) {
+            if (mRecyclerView != null) {
+                mRecyclerView.offsetChildrenHorizontal(dx);
+            }
+        }
+
+        /**
+         * Offset all child views attached to the parent RecyclerView by dy pixels along
+         * the vertical axis.
+         *
+         * @param dy Pixels to offset by
+         */
+        public void offsetChildrenVertical(int dy) {
+            if (mRecyclerView != null) {
+                mRecyclerView.offsetChildrenVertical(dy);
+            }
+        }
+
+        /**
+         * Flags a view so that it will not be scrapped or recycled.
+         * <p>
+         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
+         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
+         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
+         * ignore the child.
+         * <p>
+         * Before this child can be recycled again, you have to call
+         * {@link #stopIgnoringView(View)}.
+         * <p>
+         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
+         *
+         * @param view View to ignore.
+         * @see #stopIgnoringView(View)
+         */
+        public void ignoreView(View view) {
+            if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
+                // checking this because calling this method on a recycled or detached view may
+                // cause loss of state.
+                throw new IllegalArgumentException("View should be fully attached to be ignored");
+            }
+            final ViewHolder vh = getChildViewHolderInt(view);
+            vh.addFlags(ViewHolder.FLAG_IGNORE);
+            mRecyclerView.mViewInfoStore.removeViewHolder(vh);
+        }
+
+        /**
+         * View can be scrapped and recycled again.
+         * <p>
+         * Note that calling this method removes all information in the view holder.
+         * <p>
+         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
+         *
+         * @param view View to ignore.
+         */
+        public void stopIgnoringView(View view) {
+            final ViewHolder vh = getChildViewHolderInt(view);
+            vh.stopIgnoring();
+            vh.resetInternal();
+            vh.addFlags(ViewHolder.FLAG_INVALID);
+        }
+
+        /**
+         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
+         * into the given Recycler. The Recycler may prefer to reuse scrap views before
+         * other views that were previously recycled.
+         *
+         * @param recycler Recycler to scrap views into
+         */
+        public void detachAndScrapAttachedViews(Recycler recycler) {
+            final int childCount = getChildCount();
+            for (int i = childCount - 1; i >= 0; i--) {
+                final View v = getChildAt(i);
+                scrapOrRecycleView(recycler, i, v);
+            }
+        }
+
+        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
+            final ViewHolder viewHolder = getChildViewHolderInt(view);
+            if (viewHolder.shouldIgnore()) {
+                if (DEBUG) {
+                    Log.d(TAG, "ignoring view " + viewHolder);
+                }
+                return;
+            }
+            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
+                    && !mRecyclerView.mAdapter.hasStableIds()) {
+                removeViewAt(index);
+                recycler.recycleViewHolderInternal(viewHolder);
+            } else {
+                detachViewAt(index);
+                recycler.scrapView(view);
+                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
+            }
+        }
+
+        /**
+         * Recycles the scrapped views.
+         * <p>
+         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
+         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
+         * call remove and invalidate RecyclerView to ensure UI update.
+         *
+         * @param recycler Recycler
+         */
+        void removeAndRecycleScrapInt(Recycler recycler) {
+            final int scrapCount = recycler.getScrapCount();
+            // Loop backward, recycler might be changed by removeDetachedView()
+            for (int i = scrapCount - 1; i >= 0; i--) {
+                final View scrap = recycler.getScrapViewAt(i);
+                final ViewHolder vh = getChildViewHolderInt(scrap);
+                if (vh.shouldIgnore()) {
+                    continue;
+                }
+                // If the scrap view is animating, we need to cancel them first. If we cancel it
+                // here, ItemAnimator callback may recycle it which will cause double recycling.
+                // To avoid this, we mark it as not recycleable before calling the item animator.
+                // Since removeDetachedView calls a user API, a common mistake (ending animations on
+                // the view) may recycle it too, so we guard it before we call user APIs.
+                vh.setIsRecyclable(false);
+                if (vh.isTmpDetached()) {
+                    mRecyclerView.removeDetachedView(scrap, false);
+                }
+                if (mRecyclerView.mItemAnimator != null) {
+                    mRecyclerView.mItemAnimator.endAnimation(vh);
+                }
+                vh.setIsRecyclable(true);
+                recycler.quickRecycleScrapView(scrap);
+            }
+            recycler.clearScrap();
+            if (scrapCount > 0) {
+                mRecyclerView.invalidate();
+            }
+        }
+
+
+        /**
+         * Measure a child view using standard measurement policy, taking the padding
+         * of the parent RecyclerView and any added item decorations into account.
+         *
+         * <p>If the RecyclerView can be scrolled in either dimension the caller may
+         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
+         *
+         * @param child Child view to measure
+         * @param widthUsed Width in pixels currently consumed by other views, if relevant
+         * @param heightUsed Height in pixels currently consumed by other views, if relevant
+         */
+        public void measureChild(View child, int widthUsed, int heightUsed) {
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
+            widthUsed += insets.left + insets.right;
+            heightUsed += insets.top + insets.bottom;
+            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
+                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
+                    canScrollHorizontally());
+            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
+                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
+                    canScrollVertically());
+            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
+                child.measure(widthSpec, heightSpec);
+            }
+        }
+
+        /**
+         * RecyclerView internally does its own View measurement caching which should help with
+         * WRAP_CONTENT.
+         * <p>
+         * Use this method if the View is already measured once in this layout pass.
+         */
+        boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
+            return !mMeasurementCacheEnabled
+                    || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
+                    || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
+        }
+
+        // we may consider making this public
+        /**
+         * RecyclerView internally does its own View measurement caching which should help with
+         * WRAP_CONTENT.
+         * <p>
+         * Use this method if the View is not yet measured and you need to decide whether to
+         * measure this View or not.
+         */
+        boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
+            return child.isLayoutRequested()
+                    || !mMeasurementCacheEnabled
+                    || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
+                    || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
+        }
+
+        /**
+         * In addition to the View Framework's measurement cache, RecyclerView uses its own
+         * additional measurement cache for its children to avoid re-measuring them when not
+         * necessary. It is on by default but it can be turned off via
+         * {@link #setMeasurementCacheEnabled(boolean)}.
+         *
+         * @return True if measurement cache is enabled, false otherwise.
+         *
+         * @see #setMeasurementCacheEnabled(boolean)
+         */
+        public boolean isMeasurementCacheEnabled() {
+            return mMeasurementCacheEnabled;
+        }
+
+        /**
+         * Sets whether RecyclerView should use its own measurement cache for the children. This is
+         * a more aggressive cache than the framework uses.
+         *
+         * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
+         *
+         * @see #isMeasurementCacheEnabled()
+         */
+        public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
+            mMeasurementCacheEnabled = measurementCacheEnabled;
+        }
+
+        private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
+            final int specMode = MeasureSpec.getMode(spec);
+            final int specSize = MeasureSpec.getSize(spec);
+            if (dimension > 0 && childSize != dimension) {
+                return false;
+            }
+            switch (specMode) {
+                case MeasureSpec.UNSPECIFIED:
+                    return true;
+                case MeasureSpec.AT_MOST:
+                    return specSize >= childSize;
+                case MeasureSpec.EXACTLY:
+                    return  specSize == childSize;
+            }
+            return false;
+        }
+
+        /**
+         * Measure a child view using standard measurement policy, taking the padding
+         * of the parent RecyclerView, any added item decorations and the child margins
+         * into account.
+         *
+         * <p>If the RecyclerView can be scrolled in either dimension the caller may
+         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
+         *
+         * @param child Child view to measure
+         * @param widthUsed Width in pixels currently consumed by other views, if relevant
+         * @param heightUsed Height in pixels currently consumed by other views, if relevant
+         */
+        public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
+            widthUsed += insets.left + insets.right;
+            heightUsed += insets.top + insets.bottom;
+
+            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
+                    getPaddingLeft() + getPaddingRight()
+                            + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
+                    canScrollHorizontally());
+            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
+                    getPaddingTop() + getPaddingBottom()
+                            + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
+                    canScrollVertically());
+            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
+                child.measure(widthSpec, heightSpec);
+            }
+        }
+
+        /**
+         * Calculate a MeasureSpec value for measuring a child view in one dimension.
+         *
+         * @param parentSize Size of the parent view where the child will be placed
+         * @param padding Total space currently consumed by other elements of the parent
+         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
+         *                       Generally obtained from the child view's LayoutParams
+         * @param canScroll true if the parent RecyclerView can scroll in this dimension
+         *
+         * @return a MeasureSpec value for the child view
+         * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
+         */
+        @Deprecated
+        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
+                boolean canScroll) {
+            int size = Math.max(0, parentSize - padding);
+            int resultSize = 0;
+            int resultMode = 0;
+            if (canScroll) {
+                if (childDimension >= 0) {
+                    resultSize = childDimension;
+                    resultMode = MeasureSpec.EXACTLY;
+                } else {
+                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
+                    // instead using UNSPECIFIED.
+                    resultSize = 0;
+                    resultMode = MeasureSpec.UNSPECIFIED;
+                }
+            } else {
+                if (childDimension >= 0) {
+                    resultSize = childDimension;
+                    resultMode = MeasureSpec.EXACTLY;
+                } else if (childDimension == LayoutParams.MATCH_PARENT) {
+                    resultSize = size;
+                    // TODO this should be my spec.
+                    resultMode = MeasureSpec.EXACTLY;
+                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
+                    resultSize = size;
+                    resultMode = MeasureSpec.AT_MOST;
+                }
+            }
+            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
+        }
+
+        /**
+         * Calculate a MeasureSpec value for measuring a child view in one dimension.
+         *
+         * @param parentSize Size of the parent view where the child will be placed
+         * @param parentMode The measurement spec mode of the parent
+         * @param padding Total space currently consumed by other elements of parent
+         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
+         *                       Generally obtained from the child view's LayoutParams
+         * @param canScroll true if the parent RecyclerView can scroll in this dimension
+         *
+         * @return a MeasureSpec value for the child view
+         */
+        public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
+                int childDimension, boolean canScroll) {
+            int size = Math.max(0, parentSize - padding);
+            int resultSize = 0;
+            int resultMode = 0;
+            if (canScroll) {
+                if (childDimension >= 0) {
+                    resultSize = childDimension;
+                    resultMode = MeasureSpec.EXACTLY;
+                } else if (childDimension == LayoutParams.MATCH_PARENT) {
+                    switch (parentMode) {
+                        case MeasureSpec.AT_MOST:
+                        case MeasureSpec.EXACTLY:
+                            resultSize = size;
+                            resultMode = parentMode;
+                            break;
+                        case MeasureSpec.UNSPECIFIED:
+                            resultSize = 0;
+                            resultMode = MeasureSpec.UNSPECIFIED;
+                            break;
+                    }
+                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
+                    resultSize = 0;
+                    resultMode = MeasureSpec.UNSPECIFIED;
+                }
+            } else {
+                if (childDimension >= 0) {
+                    resultSize = childDimension;
+                    resultMode = MeasureSpec.EXACTLY;
+                } else if (childDimension == LayoutParams.MATCH_PARENT) {
+                    resultSize = size;
+                    resultMode = parentMode;
+                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
+                    resultSize = size;
+                    if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
+                        resultMode = MeasureSpec.AT_MOST;
+                    } else {
+                        resultMode = MeasureSpec.UNSPECIFIED;
+                    }
+
+                }
+            }
+            //noinspection WrongConstant
+            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
+        }
+
+        /**
+         * Returns the measured width of the given child, plus the additional size of
+         * any insets applied by {@link ItemDecoration ItemDecorations}.
+         *
+         * @param child Child view to query
+         * @return child's measured width plus <code>ItemDecoration</code> insets
+         *
+         * @see View#getMeasuredWidth()
+         */
+        public int getDecoratedMeasuredWidth(View child) {
+            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
+            return child.getMeasuredWidth() + insets.left + insets.right;
+        }
+
+        /**
+         * Returns the measured height of the given child, plus the additional size of
+         * any insets applied by {@link ItemDecoration ItemDecorations}.
+         *
+         * @param child Child view to query
+         * @return child's measured height plus <code>ItemDecoration</code> insets
+         *
+         * @see View#getMeasuredHeight()
+         */
+        public int getDecoratedMeasuredHeight(View child) {
+            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
+            return child.getMeasuredHeight() + insets.top + insets.bottom;
+        }
+
+        /**
+         * Lay out the given child view within the RecyclerView using coordinates that
+         * include any current {@link ItemDecoration ItemDecorations}.
+         *
+         * <p>LayoutManagers should prefer working in sizes and coordinates that include
+         * item decoration insets whenever possible. This allows the LayoutManager to effectively
+         * ignore decoration insets within measurement and layout code. See the following
+         * methods:</p>
+         * <ul>
+         *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
+         *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
+         *     <li>{@link #measureChild(View, int, int)}</li>
+         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
+         *     <li>{@link #getDecoratedLeft(View)}</li>
+         *     <li>{@link #getDecoratedTop(View)}</li>
+         *     <li>{@link #getDecoratedRight(View)}</li>
+         *     <li>{@link #getDecoratedBottom(View)}</li>
+         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
+         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
+         * </ul>
+         *
+         * @param child Child to lay out
+         * @param left Left edge, with item decoration insets included
+         * @param top Top edge, with item decoration insets included
+         * @param right Right edge, with item decoration insets included
+         * @param bottom Bottom edge, with item decoration insets included
+         *
+         * @see View#layout(int, int, int, int)
+         * @see #layoutDecoratedWithMargins(View, int, int, int, int)
+         */
+        public void layoutDecorated(View child, int left, int top, int right, int bottom) {
+            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
+            child.layout(left + insets.left, top + insets.top, right - insets.right,
+                    bottom - insets.bottom);
+        }
+
+        /**
+         * Lay out the given child view within the RecyclerView using coordinates that
+         * include any current {@link ItemDecoration ItemDecorations} and margins.
+         *
+         * <p>LayoutManagers should prefer working in sizes and coordinates that include
+         * item decoration insets whenever possible. This allows the LayoutManager to effectively
+         * ignore decoration insets within measurement and layout code. See the following
+         * methods:</p>
+         * <ul>
+         *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
+         *     <li>{@link #measureChild(View, int, int)}</li>
+         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
+         *     <li>{@link #getDecoratedLeft(View)}</li>
+         *     <li>{@link #getDecoratedTop(View)}</li>
+         *     <li>{@link #getDecoratedRight(View)}</li>
+         *     <li>{@link #getDecoratedBottom(View)}</li>
+         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
+         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
+         * </ul>
+         *
+         * @param child Child to lay out
+         * @param left Left edge, with item decoration insets and left margin included
+         * @param top Top edge, with item decoration insets and top margin included
+         * @param right Right edge, with item decoration insets and right margin included
+         * @param bottom Bottom edge, with item decoration insets and bottom margin included
+         *
+         * @see View#layout(int, int, int, int)
+         * @see #layoutDecorated(View, int, int, int, int)
+         */
+        public void layoutDecoratedWithMargins(View child, int left, int top, int right,
+                int bottom) {
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            final Rect insets = lp.mDecorInsets;
+            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
+                    right - insets.right - lp.rightMargin,
+                    bottom - insets.bottom - lp.bottomMargin);
+        }
+
+        /**
+         * Calculates the bounding box of the View while taking into account its matrix changes
+         * (translation, scale etc) with respect to the RecyclerView.
+         * <p>
+         * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
+         * the View's matrix so that the decor offsets also go through the same transformation.
+         *
+         * @param child The ItemView whose bounding box should be calculated.
+         * @param includeDecorInsets True if the decor insets should be included in the bounding box
+         * @param out The rectangle into which the output will be written.
+         */
+        public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
+            if (includeDecorInsets) {
+                Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
+                out.set(-insets.left, -insets.top,
+                        child.getWidth() + insets.right, child.getHeight() + insets.bottom);
+            } else {
+                out.set(0, 0, child.getWidth(), child.getHeight());
+            }
+
+            if (mRecyclerView != null) {
+                final Matrix childMatrix = child.getMatrix();
+                if (childMatrix != null && !childMatrix.isIdentity()) {
+                    final RectF tempRectF = mRecyclerView.mTempRectF;
+                    tempRectF.set(out);
+                    childMatrix.mapRect(tempRectF);
+                    out.set(
+                            (int) Math.floor(tempRectF.left),
+                            (int) Math.floor(tempRectF.top),
+                            (int) Math.ceil(tempRectF.right),
+                            (int) Math.ceil(tempRectF.bottom)
+                    );
+                }
+            }
+            out.offset(child.getLeft(), child.getTop());
+        }
+
+        /**
+         * Returns the bounds of the view including its decoration and margins.
+         *
+         * @param view The view element to check
+         * @param outBounds A rect that will receive the bounds of the element including its
+         *                  decoration and margins.
+         */
+        public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
+            RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
+        }
+
+        /**
+         * Returns the left edge of the given child view within its parent, offset by any applied
+         * {@link ItemDecoration ItemDecorations}.
+         *
+         * @param child Child to query
+         * @return Child left edge with offsets applied
+         * @see #getLeftDecorationWidth(View)
+         */
+        public int getDecoratedLeft(View child) {
+            return child.getLeft() - getLeftDecorationWidth(child);
+        }
+
+        /**
+         * Returns the top edge of the given child view within its parent, offset by any applied
+         * {@link ItemDecoration ItemDecorations}.
+         *
+         * @param child Child to query
+         * @return Child top edge with offsets applied
+         * @see #getTopDecorationHeight(View)
+         */
+        public int getDecoratedTop(View child) {
+            return child.getTop() - getTopDecorationHeight(child);
+        }
+
+        /**
+         * Returns the right edge of the given child view within its parent, offset by any applied
+         * {@link ItemDecoration ItemDecorations}.
+         *
+         * @param child Child to query
+         * @return Child right edge with offsets applied
+         * @see #getRightDecorationWidth(View)
+         */
+        public int getDecoratedRight(View child) {
+            return child.getRight() + getRightDecorationWidth(child);
+        }
+
+        /**
+         * Returns the bottom edge of the given child view within its parent, offset by any applied
+         * {@link ItemDecoration ItemDecorations}.
+         *
+         * @param child Child to query
+         * @return Child bottom edge with offsets applied
+         * @see #getBottomDecorationHeight(View)
+         */
+        public int getDecoratedBottom(View child) {
+            return child.getBottom() + getBottomDecorationHeight(child);
+        }
+
+        /**
+         * Calculates the item decor insets applied to the given child and updates the provided
+         * Rect instance with the inset values.
+         * <ul>
+         *     <li>The Rect's left is set to the total width of left decorations.</li>
+         *     <li>The Rect's top is set to the total height of top decorations.</li>
+         *     <li>The Rect's right is set to the total width of right decorations.</li>
+         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
+         * </ul>
+         * <p>
+         * Note that item decorations are automatically calculated when one of the LayoutManager's
+         * measure child methods is called. If you need to measure the child with custom specs via
+         * {@link View#measure(int, int)}, you can use this method to get decorations.
+         *
+         * @param child The child view whose decorations should be calculated
+         * @param outRect The Rect to hold result values
+         */
+        public void calculateItemDecorationsForChild(View child, Rect outRect) {
+            if (mRecyclerView == null) {
+                outRect.set(0, 0, 0, 0);
+                return;
+            }
+            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
+            outRect.set(insets);
+        }
+
+        /**
+         * Returns the total height of item decorations applied to child's top.
+         * <p>
+         * Note that this value is not updated until the View is measured or
+         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
+         *
+         * @param child Child to query
+         * @return The total height of item decorations applied to the child's top.
+         * @see #getDecoratedTop(View)
+         * @see #calculateItemDecorationsForChild(View, Rect)
+         */
+        public int getTopDecorationHeight(View child) {
+            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
+        }
+
+        /**
+         * Returns the total height of item decorations applied to child's bottom.
+         * <p>
+         * Note that this value is not updated until the View is measured or
+         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
+         *
+         * @param child Child to query
+         * @return The total height of item decorations applied to the child's bottom.
+         * @see #getDecoratedBottom(View)
+         * @see #calculateItemDecorationsForChild(View, Rect)
+         */
+        public int getBottomDecorationHeight(View child) {
+            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
+        }
+
+        /**
+         * Returns the total width of item decorations applied to child's left.
+         * <p>
+         * Note that this value is not updated until the View is measured or
+         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
+         *
+         * @param child Child to query
+         * @return The total width of item decorations applied to the child's left.
+         * @see #getDecoratedLeft(View)
+         * @see #calculateItemDecorationsForChild(View, Rect)
+         */
+        public int getLeftDecorationWidth(View child) {
+            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
+        }
+
+        /**
+         * Returns the total width of item decorations applied to child's right.
+         * <p>
+         * Note that this value is not updated until the View is measured or
+         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
+         *
+         * @param child Child to query
+         * @return The total width of item decorations applied to the child's right.
+         * @see #getDecoratedRight(View)
+         * @see #calculateItemDecorationsForChild(View, Rect)
+         */
+        public int getRightDecorationWidth(View child) {
+            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
+        }
+
+        /**
+         * Called when searching for a focusable view in the given direction has failed
+         * for the current content of the RecyclerView.
+         *
+         * <p>This is the LayoutManager's opportunity to populate views in the given direction
+         * to fulfill the request if it can. The LayoutManager should attach and return
+         * the view to be focused. The default implementation returns null.</p>
+         *
+         * @param focused   The currently focused view
+         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
+         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
+         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
+         *                  or 0 for not applicable
+         * @param recycler  The recycler to use for obtaining views for currently offscreen items
+         * @param state     Transient state of RecyclerView
+         * @return The chosen view to be focused
+         */
+        @Nullable
+        public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
+                State state) {
+            return null;
+        }
+
+        /**
+         * This method gives a LayoutManager an opportunity to intercept the initial focus search
+         * before the default behavior of {@link FocusFinder} is used. If this method returns
+         * null FocusFinder will attempt to find a focusable child view. If it fails
+         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
+         * will be called to give the LayoutManager an opportunity to add new views for items
+         * that did not have attached views representing them. The LayoutManager should not add
+         * or remove views from this method.
+         *
+         * @param focused The currently focused view
+         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
+         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
+         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
+         * @return A descendant view to focus or null to fall back to default behavior.
+         *         The default implementation returns null.
+         */
+        public View onInterceptFocusSearch(View focused, int direction) {
+            return null;
+        }
+
+        /**
+         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
+         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
+         * android.graphics.Rect, boolean)} for more details.
+         *
+         * <p>The base implementation will attempt to perform a standard programmatic scroll
+         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
+         *
+         * @param child The direct child making the request.
+         * @param rect  The rectangle in the child's coordinates the child
+         *              wishes to be on the screen.
+         * @param immediate True to forbid animated or delayed scrolling,
+         *                  false otherwise
+         * @return Whether the group scrolled to handle the operation
+         */
+        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
+                boolean immediate) {
+            final int parentLeft = getPaddingLeft();
+            final int parentTop = getPaddingTop();
+            final int parentRight = getWidth() - getPaddingRight();
+            final int parentBottom = getHeight() - getPaddingBottom();
+            final int childLeft = child.getLeft() + rect.left - child.getScrollX();
+            final int childTop = child.getTop() + rect.top - child.getScrollY();
+            final int childRight = childLeft + rect.width();
+            final int childBottom = childTop + rect.height();
+
+            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
+            final int offScreenTop = Math.min(0, childTop - parentTop);
+            final int offScreenRight = Math.max(0, childRight - parentRight);
+            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
+
+            // Favor the "start" layout direction over the end when bringing one side or the other
+            // of a large rect into view. If we decide to bring in end because start is already
+            // visible, limit the scroll such that start won't go out of bounds.
+            final int dx;
+            if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+                dx = offScreenRight != 0 ? offScreenRight
+                        : Math.max(offScreenLeft, childRight - parentRight);
+            } else {
+                dx = offScreenLeft != 0 ? offScreenLeft
+                        : Math.min(childLeft - parentLeft, offScreenRight);
+            }
+
+            // Favor bringing the top into view over the bottom. If top is already visible and
+            // we should scroll to make bottom visible, make sure top does not go out of bounds.
+            final int dy = offScreenTop != 0 ? offScreenTop
+                    : Math.min(childTop - parentTop, offScreenBottom);
+
+            if (dx != 0 || dy != 0) {
+                if (immediate) {
+                    parent.scrollBy(dx, dy);
+                } else {
+                    parent.smoothScrollBy(dx, dy);
+                }
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
+         */
+        @Deprecated
+        public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
+            // eat the request if we are in the middle of a scroll or layout
+            return isSmoothScrolling() || parent.isComputingLayout();
+        }
+
+        /**
+         * Called when a descendant view of the RecyclerView requests focus.
+         *
+         * <p>A LayoutManager wishing to keep focused views aligned in a specific
+         * portion of the view may implement that behavior in an override of this method.</p>
+         *
+         * <p>If the LayoutManager executes different behavior that should override the default
+         * behavior of scrolling the focused child on screen instead of running alongside it,
+         * this method should return true.</p>
+         *
+         * @param parent  The RecyclerView hosting this LayoutManager
+         * @param state   Current state of RecyclerView
+         * @param child   Direct child of the RecyclerView containing the newly focused view
+         * @param focused The newly focused view. This may be the same view as child or it may be
+         *                null
+         * @return true if the default scroll behavior should be suppressed
+         */
+        public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
+                View focused) {
+            return onRequestChildFocus(parent, child, focused);
+        }
+
+        /**
+         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
+         * The LayoutManager may use this opportunity to clear caches and configure state such
+         * that it can relayout appropriately with the new data and potentially new view types.
+         *
+         * <p>The default implementation removes all currently attached views.</p>
+         *
+         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
+         *                   adapter.
+         * @param newAdapter The new adapter instance. Might be null if
+         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
+         */
+        public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
+        }
+
+        /**
+         * Called to populate focusable views within the RecyclerView.
+         *
+         * <p>The LayoutManager implementation should return <code>true</code> if the default
+         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
+         * suppressed.</p>
+         *
+         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
+         * to fall back to the default ViewGroup behavior.</p>
+         *
+         * @param recyclerView The RecyclerView hosting this LayoutManager
+         * @param views List of output views. This method should add valid focusable views
+         *              to this list.
+         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
+         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
+         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
+         * @param focusableMode The type of focusables to be added.
+         *
+         * @return true to suppress the default behavior, false to add default focusables after
+         *         this method returns.
+         *
+         * @see #FOCUSABLES_ALL
+         * @see #FOCUSABLES_TOUCH_MODE
+         */
+        public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
+                int direction, int focusableMode) {
+            return false;
+        }
+
+        /**
+         * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
+         * detailed information on what has actually changed.
+         *
+         * @param recyclerView
+         */
+        public void onItemsChanged(RecyclerView recyclerView) {
+        }
+
+        /**
+         * Called when items have been added to the adapter. The LayoutManager may choose to
+         * requestLayout if the inserted items would require refreshing the currently visible set
+         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
+         *
+         * @param recyclerView
+         * @param positionStart
+         * @param itemCount
+         */
+        public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
+        }
+
+        /**
+         * Called when items have been removed from the adapter.
+         *
+         * @param recyclerView
+         * @param positionStart
+         * @param itemCount
+         */
+        public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
+        }
+
+        /**
+         * Called when items have been changed in the adapter.
+         * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
+         * instead, then this callback will not be invoked.
+         *
+         * @param recyclerView
+         * @param positionStart
+         * @param itemCount
+         */
+        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
+        }
+
+        /**
+         * Called when items have been changed in the adapter and with optional payload.
+         * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
+         *
+         * @param recyclerView
+         * @param positionStart
+         * @param itemCount
+         * @param payload
+         */
+        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
+                Object payload) {
+            onItemsUpdated(recyclerView, positionStart, itemCount);
+        }
+
+        /**
+         * Called when an item is moved withing the adapter.
+         * <p>
+         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
+         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
+         * is called.
+         *
+         * @param recyclerView
+         * @param from
+         * @param to
+         * @param itemCount
+         */
+        public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
+
+        }
+
+
+        /**
+         * <p>Override this method if you want to support scroll bars.</p>
+         *
+         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
+         *
+         * <p>Default implementation returns 0.</p>
+         *
+         * @param state Current state of RecyclerView
+         * @return The horizontal extent of the scrollbar's thumb
+         * @see RecyclerView#computeHorizontalScrollExtent()
+         */
+        public int computeHorizontalScrollExtent(State state) {
+            return 0;
+        }
+
+        /**
+         * <p>Override this method if you want to support scroll bars.</p>
+         *
+         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
+         *
+         * <p>Default implementation returns 0.</p>
+         *
+         * @param state Current State of RecyclerView where you can find total item count
+         * @return The horizontal offset of the scrollbar's thumb
+         * @see RecyclerView#computeHorizontalScrollOffset()
+         */
+        public int computeHorizontalScrollOffset(State state) {
+            return 0;
+        }
+
+        /**
+         * <p>Override this method if you want to support scroll bars.</p>
+         *
+         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
+         *
+         * <p>Default implementation returns 0.</p>
+         *
+         * @param state Current State of RecyclerView where you can find total item count
+         * @return The total horizontal range represented by the vertical scrollbar
+         * @see RecyclerView#computeHorizontalScrollRange()
+         */
+        public int computeHorizontalScrollRange(State state) {
+            return 0;
+        }
+
+        /**
+         * <p>Override this method if you want to support scroll bars.</p>
+         *
+         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
+         *
+         * <p>Default implementation returns 0.</p>
+         *
+         * @param state Current state of RecyclerView
+         * @return The vertical extent of the scrollbar's thumb
+         * @see RecyclerView#computeVerticalScrollExtent()
+         */
+        public int computeVerticalScrollExtent(State state) {
+            return 0;
+        }
+
+        /**
+         * <p>Override this method if you want to support scroll bars.</p>
+         *
+         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
+         *
+         * <p>Default implementation returns 0.</p>
+         *
+         * @param state Current State of RecyclerView where you can find total item count
+         * @return The vertical offset of the scrollbar's thumb
+         * @see RecyclerView#computeVerticalScrollOffset()
+         */
+        public int computeVerticalScrollOffset(State state) {
+            return 0;
+        }
+
+        /**
+         * <p>Override this method if you want to support scroll bars.</p>
+         *
+         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
+         *
+         * <p>Default implementation returns 0.</p>
+         *
+         * @param state Current State of RecyclerView where you can find total item count
+         * @return The total vertical range represented by the vertical scrollbar
+         * @see RecyclerView#computeVerticalScrollRange()
+         */
+        public int computeVerticalScrollRange(State state) {
+            return 0;
+        }
+
+        /**
+         * Measure the attached RecyclerView. Implementations must call
+         * {@link #setMeasuredDimension(int, int)} before returning.
+         *
+         * <p>The default implementation will handle EXACTLY measurements and respect
+         * the minimum width and height properties of the host RecyclerView if measured
+         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
+         * will consume all available space.</p>
+         *
+         * @param recycler Recycler
+         * @param state Transient state of RecyclerView
+         * @param widthSpec Width {@link android.view.View.MeasureSpec}
+         * @param heightSpec Height {@link android.view.View.MeasureSpec}
+         */
+        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
+            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
+        }
+
+        /**
+         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
+         * host RecyclerView.
+         *
+         * @param widthSize Measured width
+         * @param heightSize Measured height
+         */
+        public void setMeasuredDimension(int widthSize, int heightSize) {
+            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
+        }
+
+        /**
+         * @return The host RecyclerView's {@link View#getMinimumWidth()}
+         */
+        public int getMinimumWidth() {
+            return mRecyclerView.getMinimumWidth();
+        }
+
+        /**
+         * @return The host RecyclerView's {@link View#getMinimumHeight()}
+         */
+        public int getMinimumHeight() {
+            return mRecyclerView.getMinimumHeight();
+        }
+        /**
+         * <p>Called when the LayoutManager should save its state. This is a good time to save your
+         * scroll position, configuration and anything else that may be required to restore the same
+         * layout state if the LayoutManager is recreated.</p>
+         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
+         * restore. This will let you share information between your LayoutManagers but it is also
+         * your responsibility to make sure they use the same parcelable class.</p>
+         *
+         * @return Necessary information for LayoutManager to be able to restore its state
+         */
+        public Parcelable onSaveInstanceState() {
+            return null;
+        }
+
+
+        public void onRestoreInstanceState(Parcelable state) {
+
+        }
+
+        void stopSmoothScroller() {
+            if (mSmoothScroller != null) {
+                mSmoothScroller.stop();
+            }
+        }
+
+        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
+            if (mSmoothScroller == smoothScroller) {
+                mSmoothScroller = null;
+            }
+        }
+
+        /**
+         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
+         *
+         * @param state The new scroll state for RecyclerView
+         */
+        public void onScrollStateChanged(int state) {
+        }
+
+        /**
+         * Removes all views and recycles them using the given recycler.
+         * <p>
+         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
+         * <p>
+         * If a View is marked as "ignored", it is not removed nor recycled.
+         *
+         * @param recycler Recycler to use to recycle children
+         * @see #removeAndRecycleView(View, Recycler)
+         * @see #removeAndRecycleViewAt(int, Recycler)
+         * @see #ignoreView(View)
+         */
+        public void removeAndRecycleAllViews(Recycler recycler) {
+            for (int i = getChildCount() - 1; i >= 0; i--) {
+                final View view = getChildAt(i);
+                if (!getChildViewHolderInt(view).shouldIgnore()) {
+                    removeAndRecycleViewAt(i, recycler);
+                }
+            }
+        }
+
+        // called by accessibility delegate
+        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
+        }
+
+        /**
+         * Called by the AccessibilityDelegate when the information about the current layout should
+         * be populated.
+         * <p>
+         * Default implementation adds a {@link
+         * android.view.accessibility.AccessibilityNodeInfo.CollectionInfo}.
+         * <p>
+         * You should override
+         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
+         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
+         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
+         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
+         * more accurate accessibility information.
+         *
+         * @param recycler The Recycler that can be used to convert view positions into adapter
+         *                 positions
+         * @param state    The current state of RecyclerView
+         * @param info     The info that should be filled by the LayoutManager
+         * @see View#onInitializeAccessibilityNodeInfo(
+         *android.view.accessibility.AccessibilityNodeInfo)
+         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
+         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
+         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
+         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
+         */
+        public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
+                AccessibilityNodeInfo info) {
+            if (mRecyclerView.canScrollVertically(-1)
+                    || mRecyclerView.canScrollHorizontally(-1)) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+                info.setScrollable(true);
+            }
+            if (mRecyclerView.canScrollVertically(1)
+                    || mRecyclerView.canScrollHorizontally(1)) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                info.setScrollable(true);
+            }
+            final AccessibilityNodeInfo.CollectionInfo collectionInfo =
+                    AccessibilityNodeInfo.CollectionInfo
+                            .obtain(getRowCountForAccessibility(recycler, state),
+                                    getColumnCountForAccessibility(recycler, state),
+                                    isLayoutHierarchical(recycler, state),
+                                    getSelectionModeForAccessibility(recycler, state));
+            info.setCollectionInfo(collectionInfo);
+        }
+
+        // called by accessibility delegate
+        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
+        }
+
+        /**
+         * Called by the accessibility delegate to initialize an accessibility event.
+         * <p>
+         * Default implementation adds item count and scroll information to the event.
+         *
+         * @param recycler The Recycler that can be used to convert view positions into adapter
+         *                 positions
+         * @param state    The current state of RecyclerView
+         * @param event    The event instance to initialize
+         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
+         */
+        public void onInitializeAccessibilityEvent(Recycler recycler, State state,
+                AccessibilityEvent event) {
+            if (mRecyclerView == null || event == null) {
+                return;
+            }
+            event.setScrollable(mRecyclerView.canScrollVertically(1)
+                    || mRecyclerView.canScrollVertically(-1)
+                    || mRecyclerView.canScrollHorizontally(-1)
+                    || mRecyclerView.canScrollHorizontally(1));
+
+            if (mRecyclerView.mAdapter != null) {
+                event.setItemCount(mRecyclerView.mAdapter.getItemCount());
+            }
+        }
+
+        // called by accessibility delegate
+        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfo info) {
+            final ViewHolder vh = getChildViewHolderInt(host);
+            // avoid trying to create accessibility node info for removed children
+            if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
+                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
+                        mRecyclerView.mState, host, info);
+            }
+        }
+
+        /**
+         * Called by the AccessibilityDelegate when the accessibility information for a specific
+         * item should be populated.
+         * <p>
+         * Default implementation adds basic positioning information about the item.
+         *
+         * @param recycler The Recycler that can be used to convert view positions into adapter
+         *                 positions
+         * @param state    The current state of RecyclerView
+         * @param host     The child for which accessibility node info should be populated
+         * @param info     The info to fill out about the item
+         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
+         * android.view.accessibility.AccessibilityNodeInfo)
+         */
+        public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
+                View host, AccessibilityNodeInfo info) {
+            int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
+            int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
+            final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
+                    AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndexGuess, 1,
+                            columnIndexGuess, 1, false, false);
+            info.setCollectionItemInfo(itemInfo);
+        }
+
+        /**
+         * A LayoutManager can call this method to force RecyclerView to run simple animations in
+         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
+         * change).
+         * <p>
+         * Note that, calling this method will not guarantee that RecyclerView will run animations
+         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
+         * not run any animations but will still clear this flag after the layout is complete.
+         *
+         */
+        public void requestSimpleAnimationsInNextLayout() {
+            mRequestedSimpleAnimations = true;
+        }
+
+        /**
+         * Returns the selection mode for accessibility. Should be
+         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE},
+         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_SINGLE} or
+         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_MULTIPLE}.
+         * <p>
+         * Default implementation returns
+         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
+         *
+         * @param recycler The Recycler that can be used to convert view positions into adapter
+         *                 positions
+         * @param state    The current state of RecyclerView
+         * @return Selection mode for accessibility. Default implementation returns
+         * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
+         */
+        public int getSelectionModeForAccessibility(Recycler recycler, State state) {
+            return AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE;
+        }
+
+        /**
+         * Returns the number of rows for accessibility.
+         * <p>
+         * Default implementation returns the number of items in the adapter if LayoutManager
+         * supports vertical scrolling or 1 if LayoutManager does not support vertical
+         * scrolling.
+         *
+         * @param recycler The Recycler that can be used to convert view positions into adapter
+         *                 positions
+         * @param state    The current state of RecyclerView
+         * @return The number of rows in LayoutManager for accessibility.
+         */
+        public int getRowCountForAccessibility(Recycler recycler, State state) {
+            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
+                return 1;
+            }
+            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
+        }
+
+        /**
+         * Returns the number of columns for accessibility.
+         * <p>
+         * Default implementation returns the number of items in the adapter if LayoutManager
+         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
+         * scrolling.
+         *
+         * @param recycler The Recycler that can be used to convert view positions into adapter
+         *                 positions
+         * @param state    The current state of RecyclerView
+         * @return The number of rows in LayoutManager for accessibility.
+         */
+        public int getColumnCountForAccessibility(Recycler recycler, State state) {
+            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
+                return 1;
+            }
+            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
+        }
+
+        /**
+         * Returns whether layout is hierarchical or not to be used for accessibility.
+         * <p>
+         * Default implementation returns false.
+         *
+         * @param recycler The Recycler that can be used to convert view positions into adapter
+         *                 positions
+         * @param state    The current state of RecyclerView
+         * @return True if layout is hierarchical.
+         */
+        public boolean isLayoutHierarchical(Recycler recycler, State state) {
+            return false;
+        }
+
+        // called by accessibility delegate
+        boolean performAccessibilityAction(int action, Bundle args) {
+            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
+                    action, args);
+        }
+
+        /**
+         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
+         *
+         * @param recycler  The Recycler that can be used to convert view positions into adapter
+         *                  positions
+         * @param state     The current state of RecyclerView
+         * @param action    The action to perform
+         * @param args      Optional action arguments
+         * @see View#performAccessibilityAction(int, android.os.Bundle)
+         */
+        public boolean performAccessibilityAction(Recycler recycler, State state, int action,
+                Bundle args) {
+            if (mRecyclerView == null) {
+                return false;
+            }
+            int vScroll = 0, hScroll = 0;
+            switch (action) {
+                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                    if (mRecyclerView.canScrollVertically(-1)) {
+                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
+                    }
+                    if (mRecyclerView.canScrollHorizontally(-1)) {
+                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
+                    }
+                    break;
+                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                    if (mRecyclerView.canScrollVertically(1)) {
+                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
+                    }
+                    if (mRecyclerView.canScrollHorizontally(1)) {
+                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
+                    }
+                    break;
+            }
+            if (vScroll == 0 && hScroll == 0) {
+                return false;
+            }
+            mRecyclerView.scrollBy(hScroll, vScroll);
+            return true;
+        }
+
+        // called by accessibility delegate
+        boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
+            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
+                    view, action, args);
+        }
+
+        /**
+         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
+         * children of LayoutManager.
+         * <p>
+         * Default implementation does not do anything.
+         *
+         * @param recycler The Recycler that can be used to convert view positions into adapter
+         *                 positions
+         * @param state    The current state of RecyclerView
+         * @param view     The child view on which the action is performed
+         * @param action   The action to perform
+         * @param args     Optional action arguments
+         * @return true if action is handled
+         * @see View#performAccessibilityAction(int, android.os.Bundle)
+         */
+        public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
+                int action, Bundle args) {
+            return false;
+        }
+
+        /**
+         * Parse the xml attributes to get the most common properties used by layout managers.
+         *
+         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
+         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
+         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
+         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
+         *
+         * @return an object containing the properties as specified in the attrs.
+         */
+        public static Properties getProperties(Context context, AttributeSet attrs,
+                int defStyleAttr, int defStyleRes) {
+            Properties properties = new Properties();
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
+                    defStyleAttr, defStyleRes);
+            properties.orientation = a.getInt(R.styleable.RecyclerView_orientation, VERTICAL);
+            properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
+            properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
+            properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
+            a.recycle();
+            return properties;
+        }
+
+        void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
+            setMeasureSpecs(
+                    MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
+            );
+        }
+
+        /**
+         * Internal API to allow LayoutManagers to be measured twice.
+         * <p>
+         * This is not public because LayoutManagers should be able to handle their layouts in one
+         * pass but it is very convenient to make existing LayoutManagers support wrapping content
+         * when both orientations are undefined.
+         * <p>
+         * This API will be removed after default LayoutManagers properly implement wrap content in
+         * non-scroll orientation.
+         */
+        boolean shouldMeasureTwice() {
+            return false;
+        }
+
+        boolean hasFlexibleChildInBothOrientations() {
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                final View child = getChildAt(i);
+                final ViewGroup.LayoutParams lp = child.getLayoutParams();
+                if (lp.width < 0 && lp.height < 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Some general properties that a LayoutManager may want to use.
+         */
+        public static class Properties {
+            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
+            public int orientation;
+            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
+            public int spanCount;
+            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
+            public boolean reverseLayout;
+            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
+            public boolean stackFromEnd;
+        }
+    }
+
+    /**
+     * An ItemDecoration allows the application to add a special drawing and layout offset
+     * to specific item views from the adapter's data set. This can be useful for drawing dividers
+     * between items, highlights, visual grouping boundaries and more.
+     *
+     * <p>All ItemDecorations are drawn in the order they were added, before the item
+     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
+     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
+     * RecyclerView.State)}.</p>
+     */
+    public abstract static class ItemDecoration {
+        /**
+         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
+         * Any content drawn by this method will be drawn before the item views are drawn,
+         * and will thus appear underneath the views.
+         *
+         * @param c Canvas to draw into
+         * @param parent RecyclerView this ItemDecoration is drawing into
+         * @param state The current state of RecyclerView
+         */
+        public void onDraw(Canvas c, RecyclerView parent, State state) {
+            onDraw(c, parent);
+        }
+
+        /**
+         * @deprecated
+         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
+         */
+        @Deprecated
+        public void onDraw(Canvas c, RecyclerView parent) {
+        }
+
+        /**
+         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
+         * Any content drawn by this method will be drawn after the item views are drawn
+         * and will thus appear over the views.
+         *
+         * @param c Canvas to draw into
+         * @param parent RecyclerView this ItemDecoration is drawing into
+         * @param state The current state of RecyclerView.
+         */
+        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
+            onDrawOver(c, parent);
+        }
+
+        /**
+         * @deprecated
+         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
+         */
+        @Deprecated
+        public void onDrawOver(Canvas c, RecyclerView parent) {
+        }
+
+
+        /**
+         * @deprecated
+         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
+         */
+        @Deprecated
+        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
+            outRect.set(0, 0, 0, 0);
+        }
+
+        /**
+         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
+         * the number of pixels that the item view should be inset by, similar to padding or margin.
+         * The default implementation sets the bounds of outRect to 0 and returns.
+         *
+         * <p>
+         * If this ItemDecoration does not affect the positioning of item views, it should set
+         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
+         * before returning.
+         *
+         * <p>
+         * If you need to access Adapter for additional data, you can call
+         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
+         * View.
+         *
+         * @param outRect Rect to receive the output.
+         * @param view    The child view to decorate
+         * @param parent  RecyclerView this ItemDecoration is decorating
+         * @param state   The current state of RecyclerView.
+         */
+        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
+            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
+                    parent);
+        }
+    }
+
+    /**
+     * An OnItemTouchListener allows the application to intercept touch events in progress at the
+     * view hierarchy level of the RecyclerView before those touch events are considered for
+     * RecyclerView's own scrolling behavior.
+     *
+     * <p>This can be useful for applications that wish to implement various forms of gestural
+     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
+     * a touch interaction already in progress even if the RecyclerView is already handling that
+     * gesture stream itself for the purposes of scrolling.</p>
+     *
+     * @see SimpleOnItemTouchListener
+     */
+    public interface OnItemTouchListener {
+        /**
+         * Silently observe and/or take over touch events sent to the RecyclerView
+         * before they are handled by either the RecyclerView itself or its child views.
+         *
+         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
+         * in the order in which each listener was added, before any other touch processing
+         * by the RecyclerView itself or child views occurs.</p>
+         *
+         * @param e MotionEvent describing the touch event. All coordinates are in
+         *          the RecyclerView's coordinate system.
+         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
+         *         to continue with the current behavior and continue observing future events in
+         *         the gesture.
+         */
+        boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
+
+        /**
+         * Process a touch event as part of a gesture that was claimed by returning true from
+         * a previous call to {@link #onInterceptTouchEvent}.
+         *
+         * @param e MotionEvent describing the touch event. All coordinates are in
+         *          the RecyclerView's coordinate system.
+         */
+        void onTouchEvent(RecyclerView rv, MotionEvent e);
+
+        /**
+         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
+         * intercept touch events with
+         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
+         *
+         * @param disallowIntercept True if the child does not want the parent to
+         *            intercept touch events.
+         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
+         */
+        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
+    }
+
+    /**
+     * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
+     * and default return values.
+     * <p>
+     * You may prefer to extend this class if you don't need to override all methods. Another
+     * benefit of using this class is future compatibility. As the interface may change, we'll
+     * always provide a default implementation on this class so that your code won't break when
+     * you update to a new version of the support library.
+     */
+    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
+        @Override
+        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+            return false;
+        }
+
+        @Override
+        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+        }
+
+        @Override
+        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        }
+    }
+
+
+    /**
+     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
+     * has occurred on that RecyclerView.
+     * <p>
+     * @see RecyclerView#addOnScrollListener(OnScrollListener)
+     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
+     *
+     */
+    public abstract static class OnScrollListener {
+        /**
+         * Callback method to be invoked when RecyclerView's scroll state changes.
+         *
+         * @param recyclerView The RecyclerView whose scroll state has changed.
+         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
+         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
+         */
+        public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
+
+        /**
+         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
+         * called after the scroll has completed.
+         * <p>
+         * This callback will also be called if visible item range changes after a layout
+         * calculation. In that case, dx and dy will be 0.
+         *
+         * @param recyclerView The RecyclerView which scrolled.
+         * @param dx The amount of horizontal scroll.
+         * @param dy The amount of vertical scroll.
+         */
+        public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
+    }
+
+    /**
+     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
+     * a view is recycled.
+     *
+     * @see RecyclerView#setRecyclerListener(RecyclerListener)
+     */
+    public interface RecyclerListener {
+
+        /**
+         * This method is called whenever the view in the ViewHolder is recycled.
+         *
+         * RecyclerView calls this method right before clearing ViewHolder's internal data and
+         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
+         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
+         * its adapter position.
+         *
+         * @param holder The ViewHolder containing the view that was recycled
+         */
+        void onViewRecycled(ViewHolder holder);
+    }
+
+    /**
+     * A Listener interface that can be attached to a RecylcerView to get notified
+     * whenever a ViewHolder is attached to or detached from RecyclerView.
+     */
+    public interface OnChildAttachStateChangeListener {
+
+        /**
+         * Called when a view is attached to the RecyclerView.
+         *
+         * @param view The View which is attached to the RecyclerView
+         */
+        void onChildViewAttachedToWindow(View view);
+
+        /**
+         * Called when a view is detached from RecyclerView.
+         *
+         * @param view The View which is being detached from the RecyclerView
+         */
+        void onChildViewDetachedFromWindow(View view);
+    }
+
+    /**
+     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
+     *
+     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
+     * potentially expensive {@link View#findViewById(int)} results.</p>
+     *
+     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
+     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
+     * their own custom ViewHolder implementations to store data that makes binding view contents
+     * easier. Implementations should assume that individual item views will hold strong references
+     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
+     * strong references to extra off-screen item views for caching purposes</p>
+     */
+    public abstract static class ViewHolder {
+        public final View itemView;
+        WeakReference<RecyclerView> mNestedRecyclerView;
+        int mPosition = NO_POSITION;
+        int mOldPosition = NO_POSITION;
+        long mItemId = NO_ID;
+        int mItemViewType = INVALID_TYPE;
+        int mPreLayoutPosition = NO_POSITION;
+
+        // The item that this holder is shadowing during an item change event/animation
+        ViewHolder mShadowedHolder = null;
+        // The item that is shadowing this holder during an item change event/animation
+        ViewHolder mShadowingHolder = null;
+
+        /**
+         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
+         * are all valid.
+         */
+        static final int FLAG_BOUND = 1 << 0;
+
+        /**
+         * The data this ViewHolder's view reflects is stale and needs to be rebound
+         * by the adapter. mPosition and mItemId are consistent.
+         */
+        static final int FLAG_UPDATE = 1 << 1;
+
+        /**
+         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
+         * are not to be trusted and may no longer match the item view type.
+         * This ViewHolder must be fully rebound to different data.
+         */
+        static final int FLAG_INVALID = 1 << 2;
+
+        /**
+         * This ViewHolder points at data that represents an item previously removed from the
+         * data set. Its view may still be used for things like outgoing animations.
+         */
+        static final int FLAG_REMOVED = 1 << 3;
+
+        /**
+         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
+         * and is intended to keep views around during animations.
+         */
+        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
+
+        /**
+         * This ViewHolder is returned from scrap which means we are expecting an addView call
+         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
+         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
+         * the RecyclerView.
+         */
+        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
+
+        /**
+         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
+         * it unless LayoutManager is replaced.
+         * It is still fully visible to the LayoutManager.
+         */
+        static final int FLAG_IGNORE = 1 << 7;
+
+        /**
+         * When the View is detached form the parent, we set this flag so that we can take correct
+         * action when we need to remove it or add it back.
+         */
+        static final int FLAG_TMP_DETACHED = 1 << 8;
+
+        /**
+         * Set when we can no longer determine the adapter position of this ViewHolder until it is
+         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
+         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
+         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
+         * re-calculated.
+         */
+        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
+
+        /**
+         * Set when a addChangePayload(null) is called
+         */
+        static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
+
+        /**
+         * Used by ItemAnimator when a ViewHolder's position changes
+         */
+        static final int FLAG_MOVED = 1 << 11;
+
+        /**
+         * Used by ItemAnimator when a ViewHolder appears in pre-layout
+         */
+        static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
+
+        static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
+
+        /**
+         * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
+         * hidden list (as if it was scrap) without being recycled in between.
+         *
+         * When a ViewHolder is hidden, there are 2 paths it can be re-used:
+         *   a) Animation ends, view is recycled and used from the recycle pool.
+         *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
+         *
+         * This flag is used to represent "case b" where the ViewHolder is reused without being
+         * recycled (thus "bounced" from the hidden list). This state requires special handling
+         * because the ViewHolder must be added to pre layout maps for animations as if it was
+         * already there.
+         */
+        static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
+
+        private int mFlags;
+
+        private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
+
+        List<Object> mPayloads = null;
+        List<Object> mUnmodifiedPayloads = null;
+
+        private int mIsRecyclableCount = 0;
+
+        // If non-null, view is currently considered scrap and may be reused for other data by the
+        // scrap container.
+        private Recycler mScrapContainer = null;
+        // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
+        private boolean mInChangeScrap = false;
+
+        // Saves isImportantForAccessibility value for the view item while it's in hidden state and
+        // marked as unimportant for accessibility.
+        private int mWasImportantForAccessibilityBeforeHidden =
+                View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+        // set if we defer the accessibility state change of the view holder
+        @VisibleForTesting
+        int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
+
+        /**
+         * Is set when VH is bound from the adapter and cleaned right before it is sent to
+         * {@link RecycledViewPool}.
+         */
+        RecyclerView mOwnerRecyclerView;
+
+        public ViewHolder(View itemView) {
+            if (itemView == null) {
+                throw new IllegalArgumentException("itemView may not be null");
+            }
+            this.itemView = itemView;
+        }
+
+        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
+            addFlags(ViewHolder.FLAG_REMOVED);
+            offsetPosition(offset, applyToPreLayout);
+            mPosition = mNewPosition;
+        }
+
+        void offsetPosition(int offset, boolean applyToPreLayout) {
+            if (mOldPosition == NO_POSITION) {
+                mOldPosition = mPosition;
+            }
+            if (mPreLayoutPosition == NO_POSITION) {
+                mPreLayoutPosition = mPosition;
+            }
+            if (applyToPreLayout) {
+                mPreLayoutPosition += offset;
+            }
+            mPosition += offset;
+            if (itemView.getLayoutParams() != null) {
+                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
+            }
+        }
+
+        void clearOldPosition() {
+            mOldPosition = NO_POSITION;
+            mPreLayoutPosition = NO_POSITION;
+        }
+
+        void saveOldPosition() {
+            if (mOldPosition == NO_POSITION) {
+                mOldPosition = mPosition;
+            }
+        }
+
+        boolean shouldIgnore() {
+            return (mFlags & FLAG_IGNORE) != 0;
+        }
+
+        /**
+         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
+         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
+         * {@link #getAdapterPosition()} depending on your use case.
+         *
+         * @see #getLayoutPosition()
+         * @see #getAdapterPosition()
+         */
+        @Deprecated
+        public final int getPosition() {
+            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
+        }
+
+        /**
+         * Returns the position of the ViewHolder in terms of the latest layout pass.
+         * <p>
+         * This position is mostly used by RecyclerView components to be consistent while
+         * RecyclerView lazily processes adapter updates.
+         * <p>
+         * For performance and animation reasons, RecyclerView batches all adapter updates until the
+         * next layout pass. This may cause mismatches between the Adapter position of the item and
+         * the position it had in the latest layout calculations.
+         * <p>
+         * LayoutManagers should always call this method while doing calculations based on item
+         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
+         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
+         * of the item.
+         * <p>
+         * If LayoutManager needs to call an external method that requires the adapter position of
+         * the item, it can use {@link #getAdapterPosition()} or
+         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
+         *
+         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
+         * @see #getAdapterPosition()
+         */
+        public final int getLayoutPosition() {
+            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
+        }
+
+        /**
+         * Returns the Adapter position of the item represented by this ViewHolder.
+         * <p>
+         * Note that this might be different than the {@link #getLayoutPosition()} if there are
+         * pending adapter updates but a new layout pass has not happened yet.
+         * <p>
+         * RecyclerView does not handle any adapter updates until the next layout traversal. This
+         * may create temporary inconsistencies between what user sees on the screen and what
+         * adapter contents have. This inconsistency is not important since it will be less than
+         * 16ms but it might be a problem if you want to use ViewHolder position to access the
+         * adapter. Sometimes, you may need to get the exact adapter position to do
+         * some actions in response to user events. In that case, you should use this method which
+         * will calculate the Adapter position of the ViewHolder.
+         * <p>
+         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
+         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
+         *
+         * @return The adapter position of the item if it still exists in the adapter.
+         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
+         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
+         * layout pass or the ViewHolder has already been recycled.
+         */
+        public final int getAdapterPosition() {
+            if (mOwnerRecyclerView == null) {
+                return NO_POSITION;
+            }
+            return mOwnerRecyclerView.getAdapterPositionFor(this);
+        }
+
+        /**
+         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
+         * to perform animations.
+         * <p>
+         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
+         * adapter index in the previous layout.
+         *
+         * @return The previous adapter index of the Item represented by this ViewHolder or
+         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
+         * complete).
+         */
+        public final int getOldPosition() {
+            return mOldPosition;
+        }
+
+        /**
+         * Returns The itemId represented by this ViewHolder.
+         *
+         * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
+         * otherwise
+         */
+        public final long getItemId() {
+            return mItemId;
+        }
+
+        /**
+         * @return The view type of this ViewHolder.
+         */
+        public final int getItemViewType() {
+            return mItemViewType;
+        }
+
+        boolean isScrap() {
+            return mScrapContainer != null;
+        }
+
+        void unScrap() {
+            mScrapContainer.unscrapView(this);
+        }
+
+        boolean wasReturnedFromScrap() {
+            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
+        }
+
+        void clearReturnedFromScrapFlag() {
+            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
+        }
+
+        void clearTmpDetachFlag() {
+            mFlags = mFlags & ~FLAG_TMP_DETACHED;
+        }
+
+        void stopIgnoring() {
+            mFlags = mFlags & ~FLAG_IGNORE;
+        }
+
+        void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
+            mScrapContainer = recycler;
+            mInChangeScrap = isChangeScrap;
+        }
+
+        boolean isInvalid() {
+            return (mFlags & FLAG_INVALID) != 0;
+        }
+
+        boolean needsUpdate() {
+            return (mFlags & FLAG_UPDATE) != 0;
+        }
+
+        boolean isBound() {
+            return (mFlags & FLAG_BOUND) != 0;
+        }
+
+        boolean isRemoved() {
+            return (mFlags & FLAG_REMOVED) != 0;
+        }
+
+        boolean hasAnyOfTheFlags(int flags) {
+            return (mFlags & flags) != 0;
+        }
+
+        boolean isTmpDetached() {
+            return (mFlags & FLAG_TMP_DETACHED) != 0;
+        }
+
+        boolean isAdapterPositionUnknown() {
+            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
+        }
+
+        void setFlags(int flags, int mask) {
+            mFlags = (mFlags & ~mask) | (flags & mask);
+        }
+
+        void addFlags(int flags) {
+            mFlags |= flags;
+        }
+
+        void addChangePayload(Object payload) {
+            if (payload == null) {
+                addFlags(FLAG_ADAPTER_FULLUPDATE);
+            } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
+                createPayloadsIfNeeded();
+                mPayloads.add(payload);
+            }
+        }
+
+        private void createPayloadsIfNeeded() {
+            if (mPayloads == null) {
+                mPayloads = new ArrayList<Object>();
+                mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
+            }
+        }
+
+        void clearPayload() {
+            if (mPayloads != null) {
+                mPayloads.clear();
+            }
+            mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
+        }
+
+        List<Object> getUnmodifiedPayloads() {
+            if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
+                if (mPayloads == null || mPayloads.size() == 0) {
+                    // Initial state,  no update being called.
+                    return FULLUPDATE_PAYLOADS;
+                }
+                // there are none-null payloads
+                return mUnmodifiedPayloads;
+            } else {
+                // a full update has been called.
+                return FULLUPDATE_PAYLOADS;
+            }
+        }
+
+        void resetInternal() {
+            mFlags = 0;
+            mPosition = NO_POSITION;
+            mOldPosition = NO_POSITION;
+            mItemId = NO_ID;
+            mPreLayoutPosition = NO_POSITION;
+            mIsRecyclableCount = 0;
+            mShadowedHolder = null;
+            mShadowingHolder = null;
+            clearPayload();
+            mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
+            clearNestedRecyclerViewIfNotNested(this);
+        }
+
+        /**
+         * Called when the child view enters the hidden state
+         */
+        private void onEnteredHiddenState(RecyclerView parent) {
+            // While the view item is in hidden state, make it invisible for the accessibility.
+            mWasImportantForAccessibilityBeforeHidden =
+                    itemView.getImportantForAccessibility();
+            parent.setChildImportantForAccessibilityInternal(this,
+                    View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+        }
+
+        /**
+         * Called when the child view leaves the hidden state
+         */
+        private void onLeftHiddenState(RecyclerView parent) {
+            parent.setChildImportantForAccessibilityInternal(this,
+                    mWasImportantForAccessibilityBeforeHidden);
+            mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("ViewHolder{"
+                    + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
+                    + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
+            if (isScrap()) {
+                sb.append(" scrap ")
+                        .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
+            }
+            if (isInvalid()) sb.append(" invalid");
+            if (!isBound()) sb.append(" unbound");
+            if (needsUpdate()) sb.append(" update");
+            if (isRemoved()) sb.append(" removed");
+            if (shouldIgnore()) sb.append(" ignored");
+            if (isTmpDetached()) sb.append(" tmpDetached");
+            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
+            if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
+
+            if (itemView.getParent() == null) sb.append(" no parent");
+            sb.append("}");
+            return sb.toString();
+        }
+
+        /**
+         * Informs the recycler whether this item can be recycled. Views which are not
+         * recyclable will not be reused for other items until setIsRecyclable() is
+         * later set to true. Calls to setIsRecyclable() should always be paired (one
+         * call to setIsRecyclabe(false) should always be matched with a later call to
+         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
+         * reference-counted.
+         *
+         * @param recyclable Whether this item is available to be recycled. Default value
+         * is true.
+         *
+         * @see #isRecyclable()
+         */
+        public final void setIsRecyclable(boolean recyclable) {
+            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
+            if (mIsRecyclableCount < 0) {
+                mIsRecyclableCount = 0;
+                if (DEBUG) {
+                    throw new RuntimeException("isRecyclable decremented below 0: "
+                            + "unmatched pair of setIsRecyable() calls for " + this);
+                }
+                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
+                        + "unmatched pair of setIsRecyable() calls for " + this);
+            } else if (!recyclable && mIsRecyclableCount == 1) {
+                mFlags |= FLAG_NOT_RECYCLABLE;
+            } else if (recyclable && mIsRecyclableCount == 0) {
+                mFlags &= ~FLAG_NOT_RECYCLABLE;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
+            }
+        }
+
+        /**
+         * @return true if this item is available to be recycled, false otherwise.
+         *
+         * @see #setIsRecyclable(boolean)
+         */
+        public final boolean isRecyclable() {
+            return (mFlags & FLAG_NOT_RECYCLABLE) == 0
+                    && !itemView.hasTransientState();
+        }
+
+        /**
+         * Returns whether we have animations referring to this view holder or not.
+         * This is similar to isRecyclable flag but does not check transient state.
+         */
+        private boolean shouldBeKeptAsChild() {
+            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
+        }
+
+        /**
+         * @return True if ViewHolder is not referenced by RecyclerView animations but has
+         * transient state which will prevent it from being recycled.
+         */
+        private boolean doesTransientStatePreventRecycling() {
+            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && itemView.hasTransientState();
+        }
+
+        boolean isUpdated() {
+            return (mFlags & FLAG_UPDATE) != 0;
+        }
+    }
+
+    /**
+     * This method is here so that we can control the important for a11y changes and test it.
+     */
+    @VisibleForTesting
+    boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
+            int importantForAccessibility) {
+        if (isComputingLayout()) {
+            viewHolder.mPendingAccessibilityState = importantForAccessibility;
+            mPendingAccessibilityImportanceChange.add(viewHolder);
+            return false;
+        }
+        viewHolder.itemView.setImportantForAccessibility(importantForAccessibility);
+        return true;
+    }
+
+    void dispatchPendingImportantForAccessibilityChanges() {
+        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
+            ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
+            if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
+                continue;
+            }
+            int state = viewHolder.mPendingAccessibilityState;
+            if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
+                //noinspection WrongConstant
+                viewHolder.itemView.setImportantForAccessibility(state);
+                viewHolder.mPendingAccessibilityState =
+                        ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
+            }
+        }
+        mPendingAccessibilityImportanceChange.clear();
+    }
+
+    int getAdapterPositionFor(ViewHolder viewHolder) {
+        if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
+                | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
+                || !viewHolder.isBound()) {
+            return RecyclerView.NO_POSITION;
+        }
+        return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
+    }
+
+    /**
+     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
+     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
+     * to create their own subclass of this <code>LayoutParams</code> class
+     * to store any additional required per-child view metadata about the layout.
+     */
+    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+        ViewHolder mViewHolder;
+        final Rect mDecorInsets = new Rect();
+        boolean mInsetsDirty = true;
+        // Flag is set to true if the view is bound while it is detached from RV.
+        // In this case, we need to manually call invalidate after view is added to guarantee that
+        // invalidation is populated through the View hierarchy
+        boolean mPendingInvalidate = false;
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public LayoutParams(MarginLayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams source) {
+            super(source);
+        }
+
+        public LayoutParams(LayoutParams source) {
+            super((ViewGroup.LayoutParams) source);
+        }
+
+        /**
+         * Returns true if the view this LayoutParams is attached to needs to have its content
+         * updated from the corresponding adapter.
+         *
+         * @return true if the view should have its content updated
+         */
+        public boolean viewNeedsUpdate() {
+            return mViewHolder.needsUpdate();
+        }
+
+        /**
+         * Returns true if the view this LayoutParams is attached to is now representing
+         * potentially invalid data. A LayoutManager should scrap/recycle it.
+         *
+         * @return true if the view is invalid
+         */
+        public boolean isViewInvalid() {
+            return mViewHolder.isInvalid();
+        }
+
+        /**
+         * Returns true if the adapter data item corresponding to the view this LayoutParams
+         * is attached to has been removed from the data set. A LayoutManager may choose to
+         * treat it differently in order to animate its outgoing or disappearing state.
+         *
+         * @return true if the item the view corresponds to was removed from the data set
+         */
+        public boolean isItemRemoved() {
+            return mViewHolder.isRemoved();
+        }
+
+        /**
+         * Returns true if the adapter data item corresponding to the view this LayoutParams
+         * is attached to has been changed in the data set. A LayoutManager may choose to
+         * treat it differently in order to animate its changing state.
+         *
+         * @return true if the item the view corresponds to was changed in the data set
+         */
+        public boolean isItemChanged() {
+            return mViewHolder.isUpdated();
+        }
+
+        /**
+         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
+         */
+        @Deprecated
+        public int getViewPosition() {
+            return mViewHolder.getPosition();
+        }
+
+        /**
+         * Returns the adapter position that the view this LayoutParams is attached to corresponds
+         * to as of latest layout calculation.
+         *
+         * @return the adapter position this view as of latest layout pass
+         */
+        public int getViewLayoutPosition() {
+            return mViewHolder.getLayoutPosition();
+        }
+
+        /**
+         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
+         * corresponds to.
+         *
+         * @return the up-to-date adapter position this view. It may return
+         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
+         * its up-to-date position cannot be calculated.
+         */
+        public int getViewAdapterPosition() {
+            return mViewHolder.getAdapterPosition();
+        }
+    }
+
+    /**
+     * Observer base class for watching changes to an {@link Adapter}.
+     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
+     */
+    public abstract static class AdapterDataObserver {
+        public void onChanged() {
+            // Do nothing
+        }
+
+        public void onItemRangeChanged(int positionStart, int itemCount) {
+            // do nothing
+        }
+
+        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
+            // fallback to onItemRangeChanged(positionStart, itemCount) if app
+            // does not override this method.
+            onItemRangeChanged(positionStart, itemCount);
+        }
+
+        public void onItemRangeInserted(int positionStart, int itemCount) {
+            // do nothing
+        }
+
+        public void onItemRangeRemoved(int positionStart, int itemCount) {
+            // do nothing
+        }
+
+        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+            // do nothing
+        }
+    }
+
+    /**
+     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
+     * provides methods to trigger a programmatic scroll.</p>
+     *
+     * @see LinearSmoothScroller
+     */
+    public abstract static class SmoothScroller {
+
+        private int mTargetPosition = RecyclerView.NO_POSITION;
+
+        private RecyclerView mRecyclerView;
+
+        private LayoutManager mLayoutManager;
+
+        private boolean mPendingInitialRun;
+
+        private boolean mRunning;
+
+        private View mTargetView;
+
+        private final Action mRecyclingAction;
+
+        public SmoothScroller() {
+            mRecyclingAction = new Action(0, 0);
+        }
+
+        /**
+         * Starts a smooth scroll for the given target position.
+         * <p>In each animation step, {@link RecyclerView} will check
+         * for the target view and call either
+         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
+         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
+         * SmoothScroller is stopped.</p>
+         *
+         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
+         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
+         * stop calling SmoothScroller in each animation step.</p>
+         */
+        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
+            mRecyclerView = recyclerView;
+            mLayoutManager = layoutManager;
+            if (mTargetPosition == RecyclerView.NO_POSITION) {
+                throw new IllegalArgumentException("Invalid target position");
+            }
+            mRecyclerView.mState.mTargetPosition = mTargetPosition;
+            mRunning = true;
+            mPendingInitialRun = true;
+            mTargetView = findViewByPosition(getTargetPosition());
+            onStart();
+            mRecyclerView.mViewFlinger.postOnAnimation();
+        }
+
+        public void setTargetPosition(int targetPosition) {
+            mTargetPosition = targetPosition;
+        }
+
+        /**
+         * @return The LayoutManager to which this SmoothScroller is attached. Will return
+         * <code>null</code> after the SmoothScroller is stopped.
+         */
+        @Nullable
+        public LayoutManager getLayoutManager() {
+            return mLayoutManager;
+        }
+
+        /**
+         * Stops running the SmoothScroller in each animation callback. Note that this does not
+         * cancel any existing {@link Action} updated by
+         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
+         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
+         */
+        protected final void stop() {
+            if (!mRunning) {
+                return;
+            }
+            onStop();
+            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
+            mTargetView = null;
+            mTargetPosition = RecyclerView.NO_POSITION;
+            mPendingInitialRun = false;
+            mRunning = false;
+            // trigger a cleanup
+            mLayoutManager.onSmoothScrollerStopped(this);
+            // clear references to avoid any potential leak by a custom smooth scroller
+            mLayoutManager = null;
+            mRecyclerView = null;
+        }
+
+        /**
+         * Returns true if SmoothScroller has been started but has not received the first
+         * animation
+         * callback yet.
+         *
+         * @return True if this SmoothScroller is waiting to start
+         */
+        public boolean isPendingInitialRun() {
+            return mPendingInitialRun;
+        }
+
+
+        /**
+         * @return True if SmoothScroller is currently active
+         */
+        public boolean isRunning() {
+            return mRunning;
+        }
+
+        /**
+         * Returns the adapter position of the target item
+         *
+         * @return Adapter position of the target item or
+         * {@link RecyclerView#NO_POSITION} if no target view is set.
+         */
+        public int getTargetPosition() {
+            return mTargetPosition;
+        }
+
+        private void onAnimation(int dx, int dy) {
+            final RecyclerView recyclerView = mRecyclerView;
+            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
+                stop();
+            }
+            mPendingInitialRun = false;
+            if (mTargetView != null) {
+                // verify target position
+                if (getChildPosition(mTargetView) == mTargetPosition) {
+                    onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
+                    mRecyclingAction.runIfNecessary(recyclerView);
+                    stop();
+                } else {
+                    Log.e(TAG, "Passed over target position while smooth scrolling.");
+                    mTargetView = null;
+                }
+            }
+            if (mRunning) {
+                onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
+                boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
+                mRecyclingAction.runIfNecessary(recyclerView);
+                if (hadJumpTarget) {
+                    // It is not stopped so needs to be restarted
+                    if (mRunning) {
+                        mPendingInitialRun = true;
+                        recyclerView.mViewFlinger.postOnAnimation();
+                    } else {
+                        stop(); // done
+                    }
+                }
+            }
+        }
+
+        /**
+         * @see RecyclerView#getChildLayoutPosition(android.view.View)
+         */
+        public int getChildPosition(View view) {
+            return mRecyclerView.getChildLayoutPosition(view);
+        }
+
+        /**
+         * @see RecyclerView.LayoutManager#getChildCount()
+         */
+        public int getChildCount() {
+            return mRecyclerView.mLayout.getChildCount();
+        }
+
+        /**
+         * @see RecyclerView.LayoutManager#findViewByPosition(int)
+         */
+        public View findViewByPosition(int position) {
+            return mRecyclerView.mLayout.findViewByPosition(position);
+        }
+
+        /**
+         * @see RecyclerView#scrollToPosition(int)
+         * @deprecated Use {@link Action#jumpTo(int)}.
+         */
+        @Deprecated
+        public void instantScrollToPosition(int position) {
+            mRecyclerView.scrollToPosition(position);
+        }
+
+        protected void onChildAttachedToWindow(View child) {
+            if (getChildPosition(child) == getTargetPosition()) {
+                mTargetView = child;
+                if (DEBUG) {
+                    Log.d(TAG, "smooth scroll target view has been attached");
+                }
+            }
+        }
+
+        /**
+         * Normalizes the vector.
+         * @param scrollVector The vector that points to the target scroll position
+         */
+        protected void normalize(PointF scrollVector) {
+            final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
+                    * scrollVector.y);
+            scrollVector.x /= magnitude;
+            scrollVector.y /= magnitude;
+        }
+
+        /**
+         * Called when smooth scroll is started. This might be a good time to do setup.
+         */
+        protected abstract void onStart();
+
+        /**
+         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
+         * @see #stop()
+         */
+        protected abstract void onStop();
+
+        /**
+         * <p>RecyclerView will call this method each time it scrolls until it can find the target
+         * position in the layout.</p>
+         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
+         * provided {@link Action} to define the next scroll.</p>
+         *
+         * @param dx        Last scroll amount horizontally
+         * @param dy        Last scroll amount vertically
+         * @param state     Transient state of RecyclerView
+         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
+         *                  update this object.
+         */
+        protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
+
+        /**
+         * Called when the target position is laid out. This is the last callback SmoothScroller
+         * will receive and it should update the provided {@link Action} to define the scroll
+         * details towards the target view.
+         * @param targetView    The view element which render the target position.
+         * @param state         Transient state of RecyclerView
+         * @param action        Action instance that you should update to define final scroll action
+         *                      towards the targetView
+         */
+        protected abstract void onTargetFound(View targetView, State state, Action action);
+
+        /**
+         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
+         */
+        public static class Action {
+
+            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
+
+            private int mDx;
+
+            private int mDy;
+
+            private int mDuration;
+
+            private int mJumpToPosition = NO_POSITION;
+
+            private Interpolator mInterpolator;
+
+            private boolean mChanged = false;
+
+            // we track this variable to inform custom implementer if they are updating the action
+            // in every animation callback
+            private int mConsecutiveUpdates = 0;
+
+            /**
+             * @param dx Pixels to scroll horizontally
+             * @param dy Pixels to scroll vertically
+             */
+            public Action(int dx, int dy) {
+                this(dx, dy, UNDEFINED_DURATION, null);
+            }
+
+            /**
+             * @param dx       Pixels to scroll horizontally
+             * @param dy       Pixels to scroll vertically
+             * @param duration Duration of the animation in milliseconds
+             */
+            public Action(int dx, int dy, int duration) {
+                this(dx, dy, duration, null);
+            }
+
+            /**
+             * @param dx           Pixels to scroll horizontally
+             * @param dy           Pixels to scroll vertically
+             * @param duration     Duration of the animation in milliseconds
+             * @param interpolator Interpolator to be used when calculating scroll position in each
+             *                     animation step
+             */
+            public Action(int dx, int dy, int duration, Interpolator interpolator) {
+                mDx = dx;
+                mDy = dy;
+                mDuration = duration;
+                mInterpolator = interpolator;
+            }
+
+            /**
+             * Instead of specifying pixels to scroll, use the target position to jump using
+             * {@link RecyclerView#scrollToPosition(int)}.
+             * <p>
+             * You may prefer using this method if scroll target is really far away and you prefer
+             * to jump to a location and smooth scroll afterwards.
+             * <p>
+             * Note that calling this method takes priority over other update methods such as
+             * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
+             * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
+             * {@link #jumpTo(int)}, the other changes will not be considered for this animation
+             * frame.
+             *
+             * @param targetPosition The target item position to scroll to using instant scrolling.
+             */
+            public void jumpTo(int targetPosition) {
+                mJumpToPosition = targetPosition;
+            }
+
+            boolean hasJumpTarget() {
+                return mJumpToPosition >= 0;
+            }
+
+            void runIfNecessary(RecyclerView recyclerView) {
+                if (mJumpToPosition >= 0) {
+                    final int position = mJumpToPosition;
+                    mJumpToPosition = NO_POSITION;
+                    recyclerView.jumpToPositionForSmoothScroller(position);
+                    mChanged = false;
+                    return;
+                }
+                if (mChanged) {
+                    validate();
+                    if (mInterpolator == null) {
+                        if (mDuration == UNDEFINED_DURATION) {
+                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
+                        } else {
+                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
+                        }
+                    } else {
+                        recyclerView.mViewFlinger.smoothScrollBy(
+                                mDx, mDy, mDuration, mInterpolator);
+                    }
+                    mConsecutiveUpdates++;
+                    if (mConsecutiveUpdates > 10) {
+                        // A new action is being set in every animation step. This looks like a bad
+                        // implementation. Inform developer.
+                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
+                                + " you are not changing it unless necessary");
+                    }
+                    mChanged = false;
+                } else {
+                    mConsecutiveUpdates = 0;
+                }
+            }
+
+            private void validate() {
+                if (mInterpolator != null && mDuration < 1) {
+                    throw new IllegalStateException("If you provide an interpolator, you must"
+                            + " set a positive duration");
+                } else if (mDuration < 1) {
+                    throw new IllegalStateException("Scroll duration must be a positive number");
+                }
+            }
+
+            public int getDx() {
+                return mDx;
+            }
+
+            public void setDx(int dx) {
+                mChanged = true;
+                mDx = dx;
+            }
+
+            public int getDy() {
+                return mDy;
+            }
+
+            public void setDy(int dy) {
+                mChanged = true;
+                mDy = dy;
+            }
+
+            public int getDuration() {
+                return mDuration;
+            }
+
+            public void setDuration(int duration) {
+                mChanged = true;
+                mDuration = duration;
+            }
+
+            public Interpolator getInterpolator() {
+                return mInterpolator;
+            }
+
+            /**
+             * Sets the interpolator to calculate scroll steps
+             * @param interpolator The interpolator to use. If you specify an interpolator, you must
+             *                     also set the duration.
+             * @see #setDuration(int)
+             */
+            public void setInterpolator(Interpolator interpolator) {
+                mChanged = true;
+                mInterpolator = interpolator;
+            }
+
+            /**
+             * Updates the action with given parameters.
+             * @param dx Pixels to scroll horizontally
+             * @param dy Pixels to scroll vertically
+             * @param duration Duration of the animation in milliseconds
+             * @param interpolator Interpolator to be used when calculating scroll position in each
+             *                     animation step
+             */
+            public void update(int dx, int dy, int duration, Interpolator interpolator) {
+                mDx = dx;
+                mDy = dy;
+                mDuration = duration;
+                mInterpolator = interpolator;
+                mChanged = true;
+            }
+        }
+
+        /**
+         * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
+         * to provide a hint to a {@link SmoothScroller} about the location of the target position.
+         */
+        public interface ScrollVectorProvider {
+            /**
+             * Should calculate the vector that points to the direction where the target position
+             * can be found.
+             * <p>
+             * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
+             * the target position.
+             * <p>
+             * The magnitude of the vector is not important. It is always normalized before being
+             * used by the {@link LinearSmoothScroller}.
+             * <p>
+             * LayoutManager should not check whether the position exists in the adapter or not.
+             *
+             * @param targetPosition the target position to which the returned vector should point
+             *
+             * @return the scroll vector for a given position.
+             */
+            PointF computeScrollVectorForPosition(int targetPosition);
+        }
+    }
+
+    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
+        public boolean hasObservers() {
+            return !mObservers.isEmpty();
+        }
+
+        public void notifyChanged() {
+            // since onChanged() is implemented by the app, it could do anything, including
+            // removing itself from {@link mObservers} - and that could cause problems if
+            // an iterator is used on the ArrayList {@link mObservers}.
+            // to avoid such problems, just march thru the list in the reverse order.
+            for (int i = mObservers.size() - 1; i >= 0; i--) {
+                mObservers.get(i).onChanged();
+            }
+        }
+
+        public void notifyItemRangeChanged(int positionStart, int itemCount) {
+            notifyItemRangeChanged(positionStart, itemCount, null);
+        }
+
+        public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
+            // since onItemRangeChanged() is implemented by the app, it could do anything, including
+            // removing itself from {@link mObservers} - and that could cause problems if
+            // an iterator is used on the ArrayList {@link mObservers}.
+            // to avoid such problems, just march thru the list in the reverse order.
+            for (int i = mObservers.size() - 1; i >= 0; i--) {
+                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
+            }
+        }
+
+        public void notifyItemRangeInserted(int positionStart, int itemCount) {
+            // since onItemRangeInserted() is implemented by the app, it could do anything,
+            // including removing itself from {@link mObservers} - and that could cause problems if
+            // an iterator is used on the ArrayList {@link mObservers}.
+            // to avoid such problems, just march thru the list in the reverse order.
+            for (int i = mObservers.size() - 1; i >= 0; i--) {
+                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
+            }
+        }
+
+        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
+            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
+            // removing itself from {@link mObservers} - and that could cause problems if
+            // an iterator is used on the ArrayList {@link mObservers}.
+            // to avoid such problems, just march thru the list in the reverse order.
+            for (int i = mObservers.size() - 1; i >= 0; i--) {
+                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
+            }
+        }
+
+        public void notifyItemMoved(int fromPosition, int toPosition) {
+            for (int i = mObservers.size() - 1; i >= 0; i--) {
+                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
+            }
+        }
+    }
+
+    /**
+     * This is public so that the CREATOR can be access on cold launch.
+     * @hide
+     */
+    public static class SavedState extends AbsSavedState {
+
+        Parcelable mLayoutState;
+
+        /**
+         * called by CREATOR
+         */
+        SavedState(Parcel in) {
+            super(in);
+            mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
+        }
+
+        /**
+         * Called by onSaveInstanceState
+         */
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeParcelable(mLayoutState, 0);
+        }
+
+        void copyFrom(SavedState other) {
+            mLayoutState = other.mLayoutState;
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+                    @Override
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+
+                    @Override
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
+    /**
+     * <p>Contains useful information about the current RecyclerView state like target scroll
+     * position or view focus. State object can also keep arbitrary data, identified by resource
+     * ids.</p>
+     * <p>Often times, RecyclerView components will need to pass information between each other.
+     * To provide a well defined data bus between components, RecyclerView passes the same State
+     * object to component callbacks and these components can use it to exchange data.</p>
+     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
+     * data between your components without needing to manage their lifecycles.</p>
+     */
+    public static class State {
+        static final int STEP_START = 1;
+        static final int STEP_LAYOUT = 1 << 1;
+        static final int STEP_ANIMATIONS = 1 << 2;
+
+        void assertLayoutStep(int accepted) {
+            if ((accepted & mLayoutStep) == 0) {
+                throw new IllegalStateException("Layout state should be one of "
+                        + Integer.toBinaryString(accepted) + " but it is "
+                        + Integer.toBinaryString(mLayoutStep));
+            }
+        }
+
+
+        /** Owned by SmoothScroller */
+        private int mTargetPosition = RecyclerView.NO_POSITION;
+
+        private SparseArray<Object> mData;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Fields below are carried from one layout pass to the next
+        ////////////////////////////////////////////////////////////////////////////////////////////
+
+        /**
+         * Number of items adapter had in the previous layout.
+         */
+        int mPreviousLayoutItemCount = 0;
+
+        /**
+         * Number of items that were NOT laid out but has been deleted from the adapter after the
+         * previous layout.
+         */
+        int mDeletedInvisibleItemCountSincePreviousLayout = 0;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Fields below must be updated or cleared before they are used (generally before a pass)
+        ////////////////////////////////////////////////////////////////////////////////////////////
+
+        @IntDef(flag = true, value = {
+                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface LayoutState {}
+
+        @LayoutState
+        int mLayoutStep = STEP_START;
+
+        /**
+         * Number of items adapter has.
+         */
+        int mItemCount = 0;
+
+        boolean mStructureChanged = false;
+
+        boolean mInPreLayout = false;
+
+        boolean mTrackOldChangeHolders = false;
+
+        boolean mIsMeasuring = false;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Fields below are always reset outside of the pass (or passes) that use them
+        ////////////////////////////////////////////////////////////////////////////////////////////
+
+        boolean mRunSimpleAnimations = false;
+
+        boolean mRunPredictiveAnimations = false;
+
+        /**
+         * This data is saved before a layout calculation happens. After the layout is finished,
+         * if the previously focused view has been replaced with another view for the same item, we
+         * move the focus to the new item automatically.
+         */
+        int mFocusedItemPosition;
+        long mFocusedItemId;
+        // when a sub child has focus, record its id and see if we can directly request focus on
+        // that one instead
+        int mFocusedSubChildId;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+
+        State reset() {
+            mTargetPosition = RecyclerView.NO_POSITION;
+            if (mData != null) {
+                mData.clear();
+            }
+            mItemCount = 0;
+            mStructureChanged = false;
+            mIsMeasuring = false;
+            return this;
+        }
+
+        /**
+         * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
+         * prior to any layout passes.
+         *
+         * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
+         * that Recycler#getViewForPosition() can function safely.</p>
+         */
+        void prepareForNestedPrefetch(Adapter adapter) {
+            mLayoutStep = STEP_START;
+            mItemCount = adapter.getItemCount();
+            mStructureChanged = false;
+            mInPreLayout = false;
+            mTrackOldChangeHolders = false;
+            mIsMeasuring = false;
+        }
+
+        /**
+         * Returns true if the RecyclerView is currently measuring the layout. This value is
+         * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
+         * has non-exact measurement specs.
+         * <p>
+         * Note that if the LayoutManager supports predictive animations and it is calculating the
+         * pre-layout step, this value will be {@code false} even if the RecyclerView is in
+         * {@code onMeasure} call. This is because pre-layout means the previous state of the
+         * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
+         * LayoutManager is always guaranteed to receive another call to
+         * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
+         *
+         * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
+         */
+        public boolean isMeasuring() {
+            return mIsMeasuring;
+        }
+
+        /**
+         * Returns true if
+         * @return
+         */
+        public boolean isPreLayout() {
+            return mInPreLayout;
+        }
+
+        /**
+         * Returns whether RecyclerView will run predictive animations in this layout pass
+         * or not.
+         *
+         * @return true if RecyclerView is calculating predictive animations to be run at the end
+         *         of the layout pass.
+         */
+        public boolean willRunPredictiveAnimations() {
+            return mRunPredictiveAnimations;
+        }
+
+        /**
+         * Returns whether RecyclerView will run simple animations in this layout pass
+         * or not.
+         *
+         * @return true if RecyclerView is calculating simple animations to be run at the end of
+         *         the layout pass.
+         */
+        public boolean willRunSimpleAnimations() {
+            return mRunSimpleAnimations;
+        }
+
+        /**
+         * Removes the mapping from the specified id, if there was any.
+         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
+         *                   preserve cross functionality and avoid conflicts.
+         */
+        public void remove(int resourceId) {
+            if (mData == null) {
+                return;
+            }
+            mData.remove(resourceId);
+        }
+
+        /**
+         * Gets the Object mapped from the specified id, or <code>null</code>
+         * if no such data exists.
+         *
+         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
+         *                   to
+         *                   preserve cross functionality and avoid conflicts.
+         */
+        public <T> T get(int resourceId) {
+            if (mData == null) {
+                return null;
+            }
+            return (T) mData.get(resourceId);
+        }
+
+        /**
+         * Adds a mapping from the specified id to the specified value, replacing the previous
+         * mapping from the specified key if there was one.
+         *
+         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
+         *                   preserve cross functionality and avoid conflicts.
+         * @param data       The data you want to associate with the resourceId.
+         */
+        public void put(int resourceId, Object data) {
+            if (mData == null) {
+                mData = new SparseArray<Object>();
+            }
+            mData.put(resourceId, data);
+        }
+
+        /**
+         * If scroll is triggered to make a certain item visible, this value will return the
+         * adapter index of that item.
+         * @return Adapter index of the target item or
+         * {@link RecyclerView#NO_POSITION} if there is no target
+         * position.
+         */
+        public int getTargetScrollPosition() {
+            return mTargetPosition;
+        }
+
+        /**
+         * Returns if current scroll has a target position.
+         * @return true if scroll is being triggered to make a certain position visible
+         * @see #getTargetScrollPosition()
+         */
+        public boolean hasTargetScrollPosition() {
+            return mTargetPosition != RecyclerView.NO_POSITION;
+        }
+
+        /**
+         * @return true if the structure of the data set has changed since the last call to
+         *         onLayoutChildren, false otherwise
+         */
+        public boolean didStructureChange() {
+            return mStructureChanged;
+        }
+
+        /**
+         * Returns the total number of items that can be laid out. Note that this number is not
+         * necessarily equal to the number of items in the adapter, so you should always use this
+         * number for your position calculations and never access the adapter directly.
+         * <p>
+         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
+         * data changes on existing Views. These calculations are used to decide which animations
+         * should be run.
+         * <p>
+         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
+         * present the correct state to LayoutManager in pre-layout pass.
+         * <p>
+         * For example, a newly added item is not included in pre-layout item count because
+         * pre-layout reflects the contents of the adapter before the item is added. Behind the
+         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
+         * LayoutManager does not know about the new item's existence in pre-layout. The item will
+         * be available in second layout pass and will be included in the item count. Similar
+         * adjustments are made for moved and removed items as well.
+         * <p>
+         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
+         *
+         * @return The number of items currently available
+         * @see LayoutManager#getItemCount()
+         */
+        public int getItemCount() {
+            return mInPreLayout
+                    ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
+                    : mItemCount;
+        }
+
+        @Override
+        public String toString() {
+            return "State{"
+                    + "mTargetPosition=" + mTargetPosition
+                    + ", mData=" + mData
+                    + ", mItemCount=" + mItemCount
+                    + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
+                    + ", mDeletedInvisibleItemCountSincePreviousLayout="
+                    + mDeletedInvisibleItemCountSincePreviousLayout
+                    + ", mStructureChanged=" + mStructureChanged
+                    + ", mInPreLayout=" + mInPreLayout
+                    + ", mRunSimpleAnimations=" + mRunSimpleAnimations
+                    + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
+                    + '}';
+        }
+    }
+
+    /**
+     * This class defines the behavior of fling if the developer wishes to handle it.
+     * <p>
+     * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
+     *
+     * @see #setOnFlingListener(OnFlingListener)
+     */
+    public abstract static class OnFlingListener {
+
+        /**
+         * Override this to handle a fling given the velocities in both x and y directions.
+         * Note that this method will only be called if the associated {@link LayoutManager}
+         * supports scrolling and the fling is not handled by nested scrolls first.
+         *
+         * @param velocityX the fling velocity on the X axis
+         * @param velocityY the fling velocity on the Y axis
+         *
+         * @return true if the fling washandled, false otherwise.
+         */
+        public abstract boolean onFling(int velocityX, int velocityY);
+    }
+
+    /**
+     * Internal listener that manages items after animations finish. This is how items are
+     * retained (not recycled) during animations, but allowed to be recycled afterwards.
+     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
+     * method on the animator's listener when it is done animating any item.
+     */
+    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
+
+        ItemAnimatorRestoreListener() {
+        }
+
+        @Override
+        public void onAnimationFinished(ViewHolder item) {
+            item.setIsRecyclable(true);
+            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
+                item.mShadowedHolder = null;
+            }
+            // always null this because an OldViewHolder can never become NewViewHolder w/o being
+            // recycled.
+            item.mShadowingHolder = null;
+            if (!item.shouldBeKeptAsChild()) {
+                if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
+                    removeDetachedView(item.itemView, false);
+                }
+            }
+        }
+    }
+
+    /**
+     * This class defines the animations that take place on items as changes are made
+     * to the adapter.
+     *
+     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
+     * ViewHolder items. The RecyclerView will manage retaining these items while they
+     * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
+     * when a ViewHolder's animation is finished. In other words, there must be a matching
+     * {@link #dispatchAnimationFinished(ViewHolder)} call for each
+     * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
+     * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
+     * animateChange()}
+     * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
+     * and
+     * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+     * animateDisappearance()} call.
+     *
+     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
+     *
+     * @see #setItemAnimator(ItemAnimator)
+     */
+    @SuppressWarnings("UnusedParameters")
+    public abstract static class ItemAnimator {
+
+        /**
+         * The Item represented by this ViewHolder is updated.
+         * <p>
+         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
+         */
+        public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
+
+        /**
+         * The Item represented by this ViewHolder is removed from the adapter.
+         * <p>
+         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
+         */
+        public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
+
+        /**
+         * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
+         * represented by this ViewHolder is invalid.
+         * <p>
+         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
+         */
+        public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
+
+        /**
+         * The position of the Item represented by this ViewHolder has been changed. This flag is
+         * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
+         * any adapter change that may have a side effect on this item. (e.g. The item before this
+         * one has been removed from the Adapter).
+         * <p>
+         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
+         */
+        public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
+
+        /**
+         * This ViewHolder was not laid out but has been added to the layout in pre-layout state
+         * by the {@link LayoutManager}. This means that the item was already in the Adapter but
+         * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
+         * to add new items in pre-layout to specify their virtual location when they are invisible
+         * (e.g. to specify the item should <i>animate in</i> from below the visible area).
+         * <p>
+         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
+         */
+        public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
+                ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
+
+        /**
+         * The set of flags that might be passed to
+         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         */
+        @IntDef(flag = true, value = {
+                FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
+                FLAG_APPEARED_IN_PRE_LAYOUT
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface AdapterChanges {}
+        private ItemAnimatorListener mListener = null;
+        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
+                new ArrayList<ItemAnimatorFinishedListener>();
+
+        private long mAddDuration = 120;
+        private long mRemoveDuration = 120;
+        private long mMoveDuration = 250;
+        private long mChangeDuration = 250;
+
+        /**
+         * Gets the current duration for which all move animations will run.
+         *
+         * @return The current move duration
+         */
+        public long getMoveDuration() {
+            return mMoveDuration;
+        }
+
+        /**
+         * Sets the duration for which all move animations will run.
+         *
+         * @param moveDuration The move duration
+         */
+        public void setMoveDuration(long moveDuration) {
+            mMoveDuration = moveDuration;
+        }
+
+        /**
+         * Gets the current duration for which all add animations will run.
+         *
+         * @return The current add duration
+         */
+        public long getAddDuration() {
+            return mAddDuration;
+        }
+
+        /**
+         * Sets the duration for which all add animations will run.
+         *
+         * @param addDuration The add duration
+         */
+        public void setAddDuration(long addDuration) {
+            mAddDuration = addDuration;
+        }
+
+        /**
+         * Gets the current duration for which all remove animations will run.
+         *
+         * @return The current remove duration
+         */
+        public long getRemoveDuration() {
+            return mRemoveDuration;
+        }
+
+        /**
+         * Sets the duration for which all remove animations will run.
+         *
+         * @param removeDuration The remove duration
+         */
+        public void setRemoveDuration(long removeDuration) {
+            mRemoveDuration = removeDuration;
+        }
+
+        /**
+         * Gets the current duration for which all change animations will run.
+         *
+         * @return The current change duration
+         */
+        public long getChangeDuration() {
+            return mChangeDuration;
+        }
+
+        /**
+         * Sets the duration for which all change animations will run.
+         *
+         * @param changeDuration The change duration
+         */
+        public void setChangeDuration(long changeDuration) {
+            mChangeDuration = changeDuration;
+        }
+
+        /**
+         * Internal only:
+         * Sets the listener that must be called when the animator is finished
+         * animating the item (or immediately if no animation happens). This is set
+         * internally and is not intended to be set by external code.
+         *
+         * @param listener The listener that must be called.
+         */
+        void setListener(ItemAnimatorListener listener) {
+            mListener = listener;
+        }
+
+        /**
+         * Called by the RecyclerView before the layout begins. Item animator should record
+         * necessary information about the View before it is potentially rebound, moved or removed.
+         * <p>
+         * The data returned from this method will be passed to the related <code>animate**</code>
+         * methods.
+         * <p>
+         * Note that this method may be called after pre-layout phase if LayoutManager adds new
+         * Views to the layout in pre-layout pass.
+         * <p>
+         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
+         * the View and the adapter change flags.
+         *
+         * @param state       The current State of RecyclerView which includes some useful data
+         *                    about the layout that will be calculated.
+         * @param viewHolder  The ViewHolder whose information should be recorded.
+         * @param changeFlags Additional information about what changes happened in the Adapter
+         *                    about the Item represented by this ViewHolder. For instance, if
+         *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
+         * @param payloads    The payload list that was previously passed to
+         *                    {@link Adapter#notifyItemChanged(int, Object)} or
+         *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
+         *
+         * @return An ItemHolderInfo instance that preserves necessary information about the
+         * ViewHolder. This object will be passed back to related <code>animate**</code> methods
+         * after layout is complete.
+         *
+         * @see #recordPostLayoutInformation(State, ViewHolder)
+         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         */
+        public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
+                @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
+                @NonNull List<Object> payloads) {
+            return obtainHolderInfo().setFrom(viewHolder);
+        }
+
+        /**
+         * Called by the RecyclerView after the layout is complete. Item animator should record
+         * necessary information about the View's final state.
+         * <p>
+         * The data returned from this method will be passed to the related <code>animate**</code>
+         * methods.
+         * <p>
+         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
+         * the View.
+         *
+         * @param state      The current State of RecyclerView which includes some useful data about
+         *                   the layout that will be calculated.
+         * @param viewHolder The ViewHolder whose information should be recorded.
+         *
+         * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
+         * This object will be passed back to related <code>animate**</code> methods when
+         * RecyclerView decides how items should be animated.
+         *
+         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
+         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         */
+        public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
+                @NonNull ViewHolder viewHolder) {
+            return obtainHolderInfo().setFrom(viewHolder);
+        }
+
+        /**
+         * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
+         * <p>
+         * This means that the View was a child of the LayoutManager when layout started but has
+         * been removed by the LayoutManager. It might have been removed from the adapter or simply
+         * become invisible due to other factors. You can distinguish these two cases by checking
+         * the change flags that were passed to
+         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         * <p>
+         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
+         * animation callback method which will be called by the RecyclerView depends on the
+         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
+         * LayoutManager's decision whether to layout the changed version of a disappearing
+         * ViewHolder or not. RecyclerView will call
+         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
+         * returns {@code false} from
+         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
+         * LayoutManager lays out a new disappearing view that holds the updated information.
+         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
+         * <p>
+         * If LayoutManager supports predictive animations, it might provide a target disappear
+         * location for the View by laying it out in that location. When that happens,
+         * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
+         * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
+         * <p>
+         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
+         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
+         * decides not to animate the view).
+         *
+         * @param viewHolder    The ViewHolder which should be animated
+         * @param preLayoutInfo The information that was returned from
+         *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         * @param postLayoutInfo The information that was returned from
+         *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
+         *                       null if the LayoutManager did not layout the item.
+         *
+         * @return true if a later call to {@link #runPendingAnimations()} is requested,
+         * false otherwise.
+         */
+        public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
+                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
+
+        /**
+         * Called by the RecyclerView when a ViewHolder is added to the layout.
+         * <p>
+         * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
+         * but has  been added by the LayoutManager. It might be newly added to the adapter or
+         * simply become visible due to other factors.
+         * <p>
+         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
+         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
+         * decides not to animate the view).
+         *
+         * @param viewHolder     The ViewHolder which should be animated
+         * @param preLayoutInfo  The information that was returned from
+         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         *                       Might be null if Item was just added to the adapter or
+         *                       LayoutManager does not support predictive animations or it could
+         *                       not predict that this ViewHolder will become visible.
+         * @param postLayoutInfo The information that was returned from {@link
+         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         *
+         * @return true if a later call to {@link #runPendingAnimations()} is requested,
+         * false otherwise.
+         */
+        public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
+                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
+
+        /**
+         * Called by the RecyclerView when a ViewHolder is present in both before and after the
+         * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
+         * for it or a {@link Adapter#notifyDataSetChanged()} call.
+         * <p>
+         * This ViewHolder still represents the same data that it was representing when the layout
+         * started but its position / size may be changed by the LayoutManager.
+         * <p>
+         * If the Item's layout position didn't change, RecyclerView still calls this method because
+         * it does not track this information (or does not necessarily know that an animation is
+         * not required). Your ItemAnimator should handle this case and if there is nothing to
+         * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
+         * <code>false</code>.
+         * <p>
+         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
+         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
+         * decides not to animate the view).
+         *
+         * @param viewHolder     The ViewHolder which should be animated
+         * @param preLayoutInfo  The information that was returned from
+         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         * @param postLayoutInfo The information that was returned from {@link
+         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         *
+         * @return true if a later call to {@link #runPendingAnimations()} is requested,
+         * false otherwise.
+         */
+        public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
+                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
+
+        /**
+         * Called by the RecyclerView when an adapter item is present both before and after the
+         * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
+         * for it. This method may also be called when
+         * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
+         * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
+         * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
+         * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
+         * called for the new ViewHolder and the old one will be recycled.
+         * <p>
+         * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
+         * a good possibility that item contents didn't really change but it is rebound from the
+         * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
+         * screen didn't change and your animator should handle this case as well and avoid creating
+         * unnecessary animations.
+         * <p>
+         * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
+         * previous presentation of the item as-is and supply a new ViewHolder for the updated
+         * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
+         * This is useful if you don't know the contents of the Item and would like
+         * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
+         * <p>
+         * When you are writing a custom item animator for your layout, it might be more performant
+         * and elegant to re-use the same ViewHolder and animate the content changes manually.
+         * <p>
+         * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
+         * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
+         * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
+         * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
+         * which represent the same Item. In that case, only the new ViewHolder is visible
+         * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
+         * <p>
+         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
+         * ViewHolder when their animation is complete
+         * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
+         * animate the view).
+         * <p>
+         *  If oldHolder and newHolder are the same instance, you should call
+         * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
+         * <p>
+         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
+         * animation callback method which will be called by the RecyclerView depends on the
+         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
+         * LayoutManager's decision whether to layout the changed version of a disappearing
+         * ViewHolder or not. RecyclerView will call
+         * {@code animateChange} instead of
+         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
+         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
+         * LayoutManager lays out a new disappearing view that holds the updated information.
+         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
+         *
+         * @param oldHolder     The ViewHolder before the layout is started, might be the same
+         *                      instance with newHolder.
+         * @param newHolder     The ViewHolder after the layout is finished, might be the same
+         *                      instance with oldHolder.
+         * @param preLayoutInfo  The information that was returned from
+         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         * @param postLayoutInfo The information that was returned from {@link
+         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
+         *
+         * @return true if a later call to {@link #runPendingAnimations()} is requested,
+         * false otherwise.
+         */
+        public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
+                @NonNull ViewHolder newHolder,
+                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
+
+        @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
+            int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
+            if (viewHolder.isInvalid()) {
+                return FLAG_INVALIDATED;
+            }
+            if ((flags & FLAG_INVALIDATED) == 0) {
+                final int oldPos = viewHolder.getOldPosition();
+                final int pos = viewHolder.getAdapterPosition();
+                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
+                    flags |= FLAG_MOVED;
+                }
+            }
+            return flags;
+        }
+
+        /**
+         * Called when there are pending animations waiting to be started. This state
+         * is governed by the return values from
+         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateAppearance()},
+         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateChange()}
+         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animatePersistence()}, and
+         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
+         * called later to start the associated animations. runPendingAnimations() will be scheduled
+         * to be run on the next frame.
+         */
+        public abstract void runPendingAnimations();
+
+        /**
+         * Method called when an animation on a view should be ended immediately.
+         * This could happen when other events, like scrolling, occur, so that
+         * animating views can be quickly put into their proper end locations.
+         * Implementations should ensure that any animations running on the item
+         * are canceled and affected properties are set to their end values.
+         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
+         * animation since the animations are effectively done when this method is called.
+         *
+         * @param item The item for which an animation should be stopped.
+         */
+        public abstract void endAnimation(ViewHolder item);
+
+        /**
+         * Method called when all item animations should be ended immediately.
+         * This could happen when other events, like scrolling, occur, so that
+         * animating views can be quickly put into their proper end locations.
+         * Implementations should ensure that any animations running on any items
+         * are canceled and affected properties are set to their end values.
+         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
+         * animation since the animations are effectively done when this method is called.
+         */
+        public abstract void endAnimations();
+
+        /**
+         * Method which returns whether there are any item animations currently running.
+         * This method can be used to determine whether to delay other actions until
+         * animations end.
+         *
+         * @return true if there are any item animations currently running, false otherwise.
+         */
+        public abstract boolean isRunning();
+
+        /**
+         * Method to be called by subclasses when an animation is finished.
+         * <p>
+         * For each call RecyclerView makes to
+         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateAppearance()},
+         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animatePersistence()}, or
+         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateDisappearance()}, there
+         * should
+         * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
+         * <p>
+         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
+         * and <code>newHolder</code>  (if they are not the same instance).
+         *
+         * @param viewHolder The ViewHolder whose animation is finished.
+         * @see #onAnimationFinished(ViewHolder)
+         */
+        public final void dispatchAnimationFinished(ViewHolder viewHolder) {
+            onAnimationFinished(viewHolder);
+            if (mListener != null) {
+                mListener.onAnimationFinished(viewHolder);
+            }
+        }
+
+        /**
+         * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
+         * ItemAnimator.
+         *
+         * @param viewHolder The ViewHolder whose animation is finished. There might still be other
+         *                   animations running on this ViewHolder.
+         * @see #dispatchAnimationFinished(ViewHolder)
+         */
+        public void onAnimationFinished(ViewHolder viewHolder) {
+        }
+
+        /**
+         * Method to be called by subclasses when an animation is started.
+         * <p>
+         * For each call RecyclerView makes to
+         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateAppearance()},
+         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animatePersistence()}, or
+         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateDisappearance()}, there should be a matching
+         * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
+         * <p>
+         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
+         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
+         * and <code>newHolder</code> (if they are not the same instance).
+         * <p>
+         * If your ItemAnimator decides not to animate a ViewHolder, it should call
+         * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
+         * {@link #dispatchAnimationStarted(ViewHolder)}.
+         *
+         * @param viewHolder The ViewHolder whose animation is starting.
+         * @see #onAnimationStarted(ViewHolder)
+         */
+        public final void dispatchAnimationStarted(ViewHolder viewHolder) {
+            onAnimationStarted(viewHolder);
+        }
+
+        /**
+         * Called when a new animation is started on the given ViewHolder.
+         *
+         * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
+         *                   might already be animating and this might be another animation.
+         * @see #dispatchAnimationStarted(ViewHolder)
+         */
+        public void onAnimationStarted(ViewHolder viewHolder) {
+
+        }
+
+        /**
+         * Like {@link #isRunning()}, this method returns whether there are any item
+         * animations currently running. Additionally, the listener passed in will be called
+         * when there are no item animations running, either immediately (before the method
+         * returns) if no animations are currently running, or when the currently running
+         * animations are {@link #dispatchAnimationsFinished() finished}.
+         *
+         * <p>Note that the listener is transient - it is either called immediately and not
+         * stored at all, or stored only until it is called when running animations
+         * are finished sometime later.</p>
+         *
+         * @param listener A listener to be called immediately if no animations are running
+         * or later when currently-running animations have finished. A null listener is
+         * equivalent to calling {@link #isRunning()}.
+         * @return true if there are any item animations currently running, false otherwise.
+         */
+        public final boolean isRunning(ItemAnimatorFinishedListener listener) {
+            boolean running = isRunning();
+            if (listener != null) {
+                if (!running) {
+                    listener.onAnimationsFinished();
+                } else {
+                    mFinishedListeners.add(listener);
+                }
+            }
+            return running;
+        }
+
+        /**
+         * When an item is changed, ItemAnimator can decide whether it wants to re-use
+         * the same ViewHolder for animations or RecyclerView should create a copy of the
+         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
+         * <p>
+         * Note that this method will only be called if the {@link ViewHolder} still has the same
+         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
+         * both {@link ViewHolder}s in the
+         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
+         * <p>
+         * If your application is using change payloads, you can override
+         * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
+         *
+         * @param viewHolder The ViewHolder which represents the changed item's old content.
+         *
+         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
+         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
+         *         ItemAnimator to animate. Default implementation returns <code>true</code>.
+         *
+         * @see #canReuseUpdatedViewHolder(ViewHolder, List)
+         */
+        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
+            return true;
+        }
+
+        /**
+         * When an item is changed, ItemAnimator can decide whether it wants to re-use
+         * the same ViewHolder for animations or RecyclerView should create a copy of the
+         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
+         * <p>
+         * Note that this method will only be called if the {@link ViewHolder} still has the same
+         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
+         * both {@link ViewHolder}s in the
+         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
+         *
+         * @param viewHolder The ViewHolder which represents the changed item's old content.
+         * @param payloads A non-null list of merged payloads that were sent with change
+         *                 notifications. Can be empty if the adapter is invalidated via
+         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
+         *                 payloads will be passed into
+         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
+         *                 method <b>if</b> this method returns <code>true</code>.
+         *
+         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
+         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
+         *         ItemAnimator to animate. Default implementation calls
+         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
+         *
+         * @see #canReuseUpdatedViewHolder(ViewHolder)
+         */
+        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
+                @NonNull List<Object> payloads) {
+            return canReuseUpdatedViewHolder(viewHolder);
+        }
+
+        /**
+         * This method should be called by ItemAnimator implementations to notify
+         * any listeners that all pending and active item animations are finished.
+         */
+        public final void dispatchAnimationsFinished() {
+            final int count = mFinishedListeners.size();
+            for (int i = 0; i < count; ++i) {
+                mFinishedListeners.get(i).onAnimationsFinished();
+            }
+            mFinishedListeners.clear();
+        }
+
+        /**
+         * Returns a new {@link ItemHolderInfo} which will be used to store information about the
+         * ViewHolder. This information will later be passed into <code>animate**</code> methods.
+         * <p>
+         * You can override this method if you want to extend {@link ItemHolderInfo} and provide
+         * your own instances.
+         *
+         * @return A new {@link ItemHolderInfo}.
+         */
+        public ItemHolderInfo obtainHolderInfo() {
+            return new ItemHolderInfo();
+        }
+
+        /**
+         * The interface to be implemented by listeners to animation events from this
+         * ItemAnimator. This is used internally and is not intended for developers to
+         * create directly.
+         */
+        interface ItemAnimatorListener {
+            void onAnimationFinished(ViewHolder item);
+        }
+
+        /**
+         * This interface is used to inform listeners when all pending or running animations
+         * in an ItemAnimator are finished. This can be used, for example, to delay an action
+         * in a data set until currently-running animations are complete.
+         *
+         * @see #isRunning(ItemAnimatorFinishedListener)
+         */
+        public interface ItemAnimatorFinishedListener {
+            /**
+             * Notifies when all pending or running animations in an ItemAnimator are finished.
+             */
+            void onAnimationsFinished();
+        }
+
+        /**
+         * A simple data structure that holds information about an item's bounds.
+         * This information is used in calculating item animations. Default implementation of
+         * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
+         * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
+         * structure. You can extend this class if you would like to keep more information about
+         * the Views.
+         * <p>
+         * If you want to provide your own implementation but still use `super` methods to record
+         * basic information, you can override {@link #obtainHolderInfo()} to provide your own
+         * instances.
+         */
+        public static class ItemHolderInfo {
+
+            /**
+             * The left edge of the View (excluding decorations)
+             */
+            public int left;
+
+            /**
+             * The top edge of the View (excluding decorations)
+             */
+            public int top;
+
+            /**
+             * The right edge of the View (excluding decorations)
+             */
+            public int right;
+
+            /**
+             * The bottom edge of the View (excluding decorations)
+             */
+            public int bottom;
+
+            /**
+             * The change flags that were passed to
+             * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
+             */
+            @AdapterChanges
+            public int changeFlags;
+
+            public ItemHolderInfo() {
+            }
+
+            /**
+             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
+             * the given ViewHolder. Clears all {@link #changeFlags}.
+             *
+             * @param holder The ViewHolder whose bounds should be copied.
+             * @return This {@link ItemHolderInfo}
+             */
+            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
+                return setFrom(holder, 0);
+            }
+
+            /**
+             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
+             * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
+             *
+             * @param holder The ViewHolder whose bounds should be copied.
+             * @param flags  The adapter change flags that were passed into
+             *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
+             *               List)}.
+             * @return This {@link ItemHolderInfo}
+             */
+            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
+                    @AdapterChanges int flags) {
+                final View view = holder.itemView;
+                this.left = view.getLeft();
+                this.top = view.getTop();
+                this.right = view.getRight();
+                this.bottom = view.getBottom();
+                return this;
+            }
+        }
+    }
+
+    @Override
+    protected int getChildDrawingOrder(int childCount, int i) {
+        if (mChildDrawingOrderCallback == null) {
+            return super.getChildDrawingOrder(childCount, i);
+        } else {
+            return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
+        }
+    }
+
+    /**
+     * A callback interface that can be used to alter the drawing order of RecyclerView children.
+     * <p>
+     * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
+     * that applies to that method also applies to this callback. For example, changing the drawing
+     * order of two views will not have any effect if their elevation values are different since
+     * elevation overrides the result of this callback.
+     */
+    public interface ChildDrawingOrderCallback {
+        /**
+         * Returns the index of the child to draw for this iteration. Override this
+         * if you want to change the drawing order of children. By default, it
+         * returns i.
+         *
+         * @param i The current iteration.
+         * @return The index of the child to draw this iteration.
+         *
+         * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
+         */
+        int onGetChildDrawingOrder(int childCount, int i);
+    }
+}
diff --git a/core/java/com/android/internal/widget/RecyclerViewAccessibilityDelegate.java b/core/java/com/android/internal/widget/RecyclerViewAccessibilityDelegate.java
new file mode 100644
index 0000000..282da64
--- /dev/null
+++ b/core/java/com/android/internal/widget/RecyclerViewAccessibilityDelegate.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+/**
+ * The AccessibilityDelegate used by RecyclerView.
+ * <p>
+ * This class handles basic accessibility actions and delegates them to LayoutManager.
+ */
+public class RecyclerViewAccessibilityDelegate extends AccessibilityDelegate {
+    final RecyclerView mRecyclerView;
+
+
+    public RecyclerViewAccessibilityDelegate(RecyclerView recyclerView) {
+        mRecyclerView = recyclerView;
+    }
+
+    boolean shouldIgnore() {
+        return mRecyclerView.hasPendingAdapterUpdates();
+    }
+
+    @Override
+    public boolean performAccessibilityAction(View host, int action, Bundle args) {
+        if (super.performAccessibilityAction(host, action, args)) {
+            return true;
+        }
+        if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
+            return mRecyclerView.getLayoutManager().performAccessibilityAction(action, args);
+        }
+
+        return false;
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(host, info);
+        info.setClassName(RecyclerView.class.getName());
+        if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
+            mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(info);
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(host, event);
+        event.setClassName(RecyclerView.class.getName());
+        if (host instanceof RecyclerView && !shouldIgnore()) {
+            RecyclerView rv = (RecyclerView) host;
+            if (rv.getLayoutManager() != null) {
+                rv.getLayoutManager().onInitializeAccessibilityEvent(event);
+            }
+        }
+    }
+
+    /**
+     * Gets the AccessibilityDelegate for an individual item in the RecyclerView.
+     * A basic item delegate is provided by default, but you can override this
+     * method to provide a custom per-item delegate.
+     */
+    public AccessibilityDelegate getItemDelegate() {
+        return mItemDelegate;
+    }
+
+    final AccessibilityDelegate mItemDelegate = new AccessibilityDelegate() {
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
+                mRecyclerView.getLayoutManager()
+                        .onInitializeAccessibilityNodeInfoForItem(host, info);
+            }
+        }
+
+        @Override
+        public boolean performAccessibilityAction(View host, int action, Bundle args) {
+            if (super.performAccessibilityAction(host, action, args)) {
+                return true;
+            }
+            if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
+                return mRecyclerView.getLayoutManager()
+                        .performAccessibilityActionForItem(host, action, args);
+            }
+            return false;
+        }
+    };
+}
+
diff --git a/core/java/com/android/internal/widget/ScrollbarHelper.java b/core/java/com/android/internal/widget/ScrollbarHelper.java
new file mode 100644
index 0000000..ae34e4c
--- /dev/null
+++ b/core/java/com/android/internal/widget/ScrollbarHelper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.view.View;
+
+/**
+ * A helper class to do scroll offset calculations.
+ */
+class ScrollbarHelper {
+
+    /**
+     * @param startChild View closest to start of the list. (top or left)
+     * @param endChild   View closest to end of the list (bottom or right)
+     */
+    static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
+            View startChild, View endChild, RecyclerView.LayoutManager lm,
+            boolean smoothScrollbarEnabled, boolean reverseLayout) {
+        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
+                || endChild == null) {
+            return 0;
+        }
+        final int minPosition = Math.min(lm.getPosition(startChild),
+                lm.getPosition(endChild));
+        final int maxPosition = Math.max(lm.getPosition(startChild),
+                lm.getPosition(endChild));
+        final int itemsBefore = reverseLayout
+                ? Math.max(0, state.getItemCount() - maxPosition - 1)
+                : Math.max(0, minPosition);
+        if (!smoothScrollbarEnabled) {
+            return itemsBefore;
+        }
+        final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild)
+                - orientation.getDecoratedStart(startChild));
+        final int itemRange = Math.abs(lm.getPosition(startChild)
+                - lm.getPosition(endChild)) + 1;
+        final float avgSizePerRow = (float) laidOutArea / itemRange;
+
+        return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
+                - orientation.getDecoratedStart(startChild)));
+    }
+
+    /**
+     * @param startChild View closest to start of the list. (top or left)
+     * @param endChild   View closest to end of the list (bottom or right)
+     */
+    static int computeScrollExtent(RecyclerView.State state, OrientationHelper orientation,
+            View startChild, View endChild, RecyclerView.LayoutManager lm,
+            boolean smoothScrollbarEnabled) {
+        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
+                || endChild == null) {
+            return 0;
+        }
+        if (!smoothScrollbarEnabled) {
+            return Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
+        }
+        final int extend = orientation.getDecoratedEnd(endChild)
+                - orientation.getDecoratedStart(startChild);
+        return Math.min(orientation.getTotalSpace(), extend);
+    }
+
+    /**
+     * @param startChild View closest to start of the list. (top or left)
+     * @param endChild   View closest to end of the list (bottom or right)
+     */
+    static int computeScrollRange(RecyclerView.State state, OrientationHelper orientation,
+            View startChild, View endChild, RecyclerView.LayoutManager lm,
+            boolean smoothScrollbarEnabled) {
+        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
+                || endChild == null) {
+            return 0;
+        }
+        if (!smoothScrollbarEnabled) {
+            return state.getItemCount();
+        }
+        // smooth scrollbar enabled. try to estimate better.
+        final int laidOutArea = orientation.getDecoratedEnd(endChild)
+                - orientation.getDecoratedStart(startChild);
+        final int laidOutRange = Math.abs(lm.getPosition(startChild)
+                - lm.getPosition(endChild))
+                + 1;
+        // estimate a size for full list.
+        return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
+    }
+}
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 4466575..311bfac 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -471,7 +471,7 @@
                 if (mIconView != null) {
                     mIconView.setContentDescription(tab.getContentDescription());
                 }
-                setTooltip(hasText? null : tab.getContentDescription());
+                setTooltipText(hasText? null : tab.getContentDescription());
             }
         }
 
diff --git a/core/java/com/android/internal/widget/ScrollingView.java b/core/java/com/android/internal/widget/ScrollingView.java
new file mode 100644
index 0000000..a0205e7
--- /dev/null
+++ b/core/java/com/android/internal/widget/ScrollingView.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+/**
+ * An interface that can be implemented by Views to provide scroll related APIs.
+ */
+public interface ScrollingView {
+    /**
+     * <p>Compute the horizontal range that the horizontal scrollbar
+     * represents.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollExtent()} and
+     * {@link #computeHorizontalScrollOffset()}.</p>
+     *
+     * <p>The default range is the drawing width of this view.</p>
+     *
+     * @return the total horizontal range represented by the horizontal
+     *         scrollbar
+     *
+     * @see #computeHorizontalScrollExtent()
+     * @see #computeHorizontalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeHorizontalScrollRange();
+
+    /**
+     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb
+     * within the horizontal range. This value is used to compute the position
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollRange()} and
+     * {@link #computeHorizontalScrollExtent()}.</p>
+     *
+     * <p>The default offset is the scroll offset of this view.</p>
+     *
+     * @return the horizontal offset of the scrollbar's thumb
+     *
+     * @see #computeHorizontalScrollRange()
+     * @see #computeHorizontalScrollExtent()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeHorizontalScrollOffset();
+
+    /**
+     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb
+     * within the horizontal range. This value is used to compute the length
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollRange()} and
+     * {@link #computeHorizontalScrollOffset()}.</p>
+     *
+     * <p>The default extent is the drawing width of this view.</p>
+     *
+     * @return the horizontal extent of the scrollbar's thumb
+     *
+     * @see #computeHorizontalScrollRange()
+     * @see #computeHorizontalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeHorizontalScrollExtent();
+
+    /**
+     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeVerticalScrollExtent()} and
+     * {@link #computeVerticalScrollOffset()}.</p>
+     *
+     * @return the total vertical range represented by the vertical scrollbar
+     *
+     * <p>The default range is the drawing height of this view.</p>
+     *
+     * @see #computeVerticalScrollExtent()
+     * @see #computeVerticalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeVerticalScrollRange();
+
+    /**
+     * <p>Compute the vertical offset of the vertical scrollbar's thumb
+     * within the horizontal range. This value is used to compute the position
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeVerticalScrollRange()} and
+     * {@link #computeVerticalScrollExtent()}.</p>
+     *
+     * <p>The default offset is the scroll offset of this view.</p>
+     *
+     * @return the vertical offset of the scrollbar's thumb
+     *
+     * @see #computeVerticalScrollRange()
+     * @see #computeVerticalScrollExtent()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeVerticalScrollOffset();
+
+    /**
+     * <p>Compute the vertical extent of the vertical scrollbar's thumb
+     * within the vertical range. This value is used to compute the length
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeVerticalScrollRange()} and
+     * {@link #computeVerticalScrollOffset()}.</p>
+     *
+     * <p>The default extent is the drawing height of this view.</p>
+     *
+     * @return the vertical extent of the scrollbar's thumb
+     *
+     * @see #computeVerticalScrollRange()
+     * @see #computeVerticalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    int computeVerticalScrollExtent();
+}
diff --git a/core/java/com/android/internal/widget/SimpleItemAnimator.java b/core/java/com/android/internal/widget/SimpleItemAnimator.java
new file mode 100644
index 0000000..f4cc753
--- /dev/null
+++ b/core/java/com/android/internal/widget/SimpleItemAnimator.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import android.view.View;
+
+import com.android.internal.widget.RecyclerView.Adapter;
+import com.android.internal.widget.RecyclerView.ViewHolder;
+
+/**
+ * A wrapper class for ItemAnimator that records View bounds and decides whether it should run
+ * move, change, add or remove animations. This class also replicates the original ItemAnimator
+ * API.
+ * <p>
+ * It uses {@link ItemHolderInfo} to track the bounds information of the Views. If you would like
+ * to
+ * extend this class, you can override {@link #obtainHolderInfo()} method to provide your own info
+ * class that extends {@link ItemHolderInfo}.
+ */
+public abstract class SimpleItemAnimator extends RecyclerView.ItemAnimator {
+
+    private static final boolean DEBUG = false;
+
+    private static final String TAG = "SimpleItemAnimator";
+
+    boolean mSupportsChangeAnimations = true;
+
+    /**
+     * Returns whether this ItemAnimator supports animations of change events.
+     *
+     * @return true if change animations are supported, false otherwise
+     */
+    @SuppressWarnings("unused")
+    public boolean getSupportsChangeAnimations() {
+        return mSupportsChangeAnimations;
+    }
+
+    /**
+     * Sets whether this ItemAnimator supports animations of item change events.
+     * If you set this property to false, actions on the data set which change the
+     * contents of items will not be animated. What those animations do is left
+     * up to the discretion of the ItemAnimator subclass, in its
+     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} implementation.
+     * The value of this property is true by default.
+     *
+     * @param supportsChangeAnimations true if change animations are supported by
+     *                                 this ItemAnimator, false otherwise. If the property is false,
+     *                                 the ItemAnimator
+     *                                 will not receive a call to
+     *                                 {@link #animateChange(ViewHolder, ViewHolder, int, int, int,
+     *                                 int)} when changes occur.
+     * @see Adapter#notifyItemChanged(int)
+     * @see Adapter#notifyItemRangeChanged(int, int)
+     */
+    public void setSupportsChangeAnimations(boolean supportsChangeAnimations) {
+        mSupportsChangeAnimations = supportsChangeAnimations;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return True if change animations are not supported or the ViewHolder is invalid,
+     * false otherwise.
+     *
+     * @see #setSupportsChangeAnimations(boolean)
+     */
+    @Override
+    public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
+        return !mSupportsChangeAnimations || viewHolder.isInvalid();
+    }
+
+    @Override
+    public boolean animateDisappearance(@NonNull ViewHolder viewHolder,
+            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
+        int oldLeft = preLayoutInfo.left;
+        int oldTop = preLayoutInfo.top;
+        View disappearingItemView = viewHolder.itemView;
+        int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;
+        int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;
+        if (!viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
+            disappearingItemView.layout(newLeft, newTop,
+                    newLeft + disappearingItemView.getWidth(),
+                    newTop + disappearingItemView.getHeight());
+            if (DEBUG) {
+                Log.d(TAG, "DISAPPEARING: " + viewHolder + " with view " + disappearingItemView);
+            }
+            return animateMove(viewHolder, oldLeft, oldTop, newLeft, newTop);
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "REMOVED: " + viewHolder + " with view " + disappearingItemView);
+            }
+            return animateRemove(viewHolder);
+        }
+    }
+
+    @Override
+    public boolean animateAppearance(@NonNull ViewHolder viewHolder,
+            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
+        if (preLayoutInfo != null && (preLayoutInfo.left != postLayoutInfo.left
+                || preLayoutInfo.top != postLayoutInfo.top)) {
+            // slide items in if before/after locations differ
+            if (DEBUG) {
+                Log.d(TAG, "APPEARING: " + viewHolder + " with view " + viewHolder);
+            }
+            return animateMove(viewHolder, preLayoutInfo.left, preLayoutInfo.top,
+                    postLayoutInfo.left, postLayoutInfo.top);
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "ADDED: " + viewHolder + " with view " + viewHolder);
+            }
+            return animateAdd(viewHolder);
+        }
+    }
+
+    @Override
+    public boolean animatePersistence(@NonNull ViewHolder viewHolder,
+            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
+        if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
+            if (DEBUG) {
+                Log.d(TAG, "PERSISTENT: " + viewHolder
+                        + " with view " + viewHolder.itemView);
+            }
+            return animateMove(viewHolder,
+                    preInfo.left, preInfo.top, postInfo.left, postInfo.top);
+        }
+        dispatchMoveFinished(viewHolder);
+        return false;
+    }
+
+    @Override
+    public boolean animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
+            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
+        if (DEBUG) {
+            Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
+        }
+        final int fromLeft = preInfo.left;
+        final int fromTop = preInfo.top;
+        final int toLeft, toTop;
+        if (newHolder.shouldIgnore()) {
+            toLeft = preInfo.left;
+            toTop = preInfo.top;
+        } else {
+            toLeft = postInfo.left;
+            toTop = postInfo.top;
+        }
+        return animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft, toTop);
+    }
+
+    /**
+     * Called when an item is removed from the RecyclerView. Implementors can choose
+     * whether and how to animate that change, but must always call
+     * {@link #dispatchRemoveFinished(ViewHolder)} when done, either
+     * immediately (if no animation will occur) or after the animation actually finishes.
+     * The return value indicates whether an animation has been set up and whether the
+     * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
+     * next opportunity. This mechanism allows ItemAnimator to set up individual animations
+     * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
+     * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
+     * {@link #animateRemove(ViewHolder) animateRemove()}, and
+     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
+     * then start the animations together in the later call to {@link #runPendingAnimations()}.
+     *
+     * <p>This method may also be called for disappearing items which continue to exist in the
+     * RecyclerView, but for which the system does not have enough information to animate
+     * them out of view. In that case, the default animation for removing items is run
+     * on those items as well.</p>
+     *
+     * @param holder The item that is being removed.
+     * @return true if a later call to {@link #runPendingAnimations()} is requested,
+     * false otherwise.
+     */
+    public abstract boolean animateRemove(ViewHolder holder);
+
+    /**
+     * Called when an item is added to the RecyclerView. Implementors can choose
+     * whether and how to animate that change, but must always call
+     * {@link #dispatchAddFinished(ViewHolder)} when done, either
+     * immediately (if no animation will occur) or after the animation actually finishes.
+     * The return value indicates whether an animation has been set up and whether the
+     * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
+     * next opportunity. This mechanism allows ItemAnimator to set up individual animations
+     * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
+     * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
+     * {@link #animateRemove(ViewHolder) animateRemove()}, and
+     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
+     * then start the animations together in the later call to {@link #runPendingAnimations()}.
+     *
+     * <p>This method may also be called for appearing items which were already in the
+     * RecyclerView, but for which the system does not have enough information to animate
+     * them into view. In that case, the default animation for adding items is run
+     * on those items as well.</p>
+     *
+     * @param holder The item that is being added.
+     * @return true if a later call to {@link #runPendingAnimations()} is requested,
+     * false otherwise.
+     */
+    public abstract boolean animateAdd(ViewHolder holder);
+
+    /**
+     * Called when an item is moved in the RecyclerView. Implementors can choose
+     * whether and how to animate that change, but must always call
+     * {@link #dispatchMoveFinished(ViewHolder)} when done, either
+     * immediately (if no animation will occur) or after the animation actually finishes.
+     * The return value indicates whether an animation has been set up and whether the
+     * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
+     * next opportunity. This mechanism allows ItemAnimator to set up individual animations
+     * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
+     * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
+     * {@link #animateRemove(ViewHolder) animateRemove()}, and
+     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
+     * then start the animations together in the later call to {@link #runPendingAnimations()}.
+     *
+     * @param holder The item that is being moved.
+     * @return true if a later call to {@link #runPendingAnimations()} is requested,
+     * false otherwise.
+     */
+    public abstract boolean animateMove(ViewHolder holder, int fromX, int fromY,
+            int toX, int toY);
+
+    /**
+     * Called when an item is changed in the RecyclerView, as indicated by a call to
+     * {@link Adapter#notifyItemChanged(int)} or
+     * {@link Adapter#notifyItemRangeChanged(int, int)}.
+     * <p>
+     * Implementers can choose whether and how to animate changes, but must always call
+     * {@link #dispatchChangeFinished(ViewHolder, boolean)} for each non-null distinct ViewHolder,
+     * either immediately (if no animation will occur) or after the animation actually finishes.
+     * If the {@code oldHolder} is the same ViewHolder as the {@code newHolder}, you must call
+     * {@link #dispatchChangeFinished(ViewHolder, boolean)} once and only once. In that case, the
+     * second parameter of {@code dispatchChangeFinished} is ignored.
+     * <p>
+     * The return value indicates whether an animation has been set up and whether the
+     * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
+     * next opportunity. This mechanism allows ItemAnimator to set up individual animations
+     * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
+     * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
+     * {@link #animateRemove(ViewHolder) animateRemove()}, and
+     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
+     * then start the animations together in the later call to {@link #runPendingAnimations()}.
+     *
+     * @param oldHolder The original item that changed.
+     * @param newHolder The new item that was created with the changed content. Might be null
+     * @param fromLeft  Left of the old view holder
+     * @param fromTop   Top of the old view holder
+     * @param toLeft    Left of the new view holder
+     * @param toTop     Top of the new view holder
+     * @return true if a later call to {@link #runPendingAnimations()} is requested,
+     * false otherwise.
+     */
+    public abstract boolean animateChange(ViewHolder oldHolder,
+            ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop);
+
+    /**
+     * Method to be called by subclasses when a remove animation is done.
+     *
+     * @param item The item which has been removed
+     * @see RecyclerView.ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo,
+     * ItemHolderInfo)
+     */
+    public final void dispatchRemoveFinished(ViewHolder item) {
+        onRemoveFinished(item);
+        dispatchAnimationFinished(item);
+    }
+
+    /**
+     * Method to be called by subclasses when a move animation is done.
+     *
+     * @param item The item which has been moved
+     * @see RecyclerView.ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo,
+     * ItemHolderInfo)
+     * @see RecyclerView.ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+     * @see RecyclerView.ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
+     */
+    public final void dispatchMoveFinished(ViewHolder item) {
+        onMoveFinished(item);
+        dispatchAnimationFinished(item);
+    }
+
+    /**
+     * Method to be called by subclasses when an add animation is done.
+     *
+     * @param item The item which has been added
+     */
+    public final void dispatchAddFinished(ViewHolder item) {
+        onAddFinished(item);
+        dispatchAnimationFinished(item);
+    }
+
+    /**
+     * Method to be called by subclasses when a change animation is done.
+     *
+     * @param item    The item which has been changed (this method must be called for
+     *                each non-null ViewHolder passed into
+     *                {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
+     * @param oldItem true if this is the old item that was changed, false if
+     *                it is the new item that replaced the old item.
+     * @see #animateChange(ViewHolder, ViewHolder, int, int, int, int)
+     */
+    public final void dispatchChangeFinished(ViewHolder item, boolean oldItem) {
+        onChangeFinished(item, oldItem);
+        dispatchAnimationFinished(item);
+    }
+
+    /**
+     * Method to be called by subclasses when a remove animation is being started.
+     *
+     * @param item The item being removed
+     */
+    public final void dispatchRemoveStarting(ViewHolder item) {
+        onRemoveStarting(item);
+    }
+
+    /**
+     * Method to be called by subclasses when a move animation is being started.
+     *
+     * @param item The item being moved
+     */
+    public final void dispatchMoveStarting(ViewHolder item) {
+        onMoveStarting(item);
+    }
+
+    /**
+     * Method to be called by subclasses when an add animation is being started.
+     *
+     * @param item The item being added
+     */
+    public final void dispatchAddStarting(ViewHolder item) {
+        onAddStarting(item);
+    }
+
+    /**
+     * Method to be called by subclasses when a change animation is being started.
+     *
+     * @param item    The item which has been changed (this method must be called for
+     *                each non-null ViewHolder passed into
+     *                {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
+     * @param oldItem true if this is the old item that was changed, false if
+     *                it is the new item that replaced the old item.
+     */
+    public final void dispatchChangeStarting(ViewHolder item, boolean oldItem) {
+        onChangeStarting(item, oldItem);
+    }
+
+    /**
+     * Called when a remove animation is being started on the given ViewHolder.
+     * The default implementation does nothing. Subclasses may wish to override
+     * this method to handle any ViewHolder-specific operations linked to animation
+     * lifecycles.
+     *
+     * @param item The ViewHolder being animated.
+     */
+    @SuppressWarnings("UnusedParameters")
+    public void onRemoveStarting(ViewHolder item) {
+    }
+
+    /**
+     * Called when a remove animation has ended on the given ViewHolder.
+     * The default implementation does nothing. Subclasses may wish to override
+     * this method to handle any ViewHolder-specific operations linked to animation
+     * lifecycles.
+     *
+     * @param item The ViewHolder being animated.
+     */
+    public void onRemoveFinished(ViewHolder item) {
+    }
+
+    /**
+     * Called when an add animation is being started on the given ViewHolder.
+     * The default implementation does nothing. Subclasses may wish to override
+     * this method to handle any ViewHolder-specific operations linked to animation
+     * lifecycles.
+     *
+     * @param item The ViewHolder being animated.
+     */
+    @SuppressWarnings("UnusedParameters")
+    public void onAddStarting(ViewHolder item) {
+    }
+
+    /**
+     * Called when an add animation has ended on the given ViewHolder.
+     * The default implementation does nothing. Subclasses may wish to override
+     * this method to handle any ViewHolder-specific operations linked to animation
+     * lifecycles.
+     *
+     * @param item The ViewHolder being animated.
+     */
+    public void onAddFinished(ViewHolder item) {
+    }
+
+    /**
+     * Called when a move animation is being started on the given ViewHolder.
+     * The default implementation does nothing. Subclasses may wish to override
+     * this method to handle any ViewHolder-specific operations linked to animation
+     * lifecycles.
+     *
+     * @param item The ViewHolder being animated.
+     */
+    @SuppressWarnings("UnusedParameters")
+    public void onMoveStarting(ViewHolder item) {
+    }
+
+    /**
+     * Called when a move animation has ended on the given ViewHolder.
+     * The default implementation does nothing. Subclasses may wish to override
+     * this method to handle any ViewHolder-specific operations linked to animation
+     * lifecycles.
+     *
+     * @param item The ViewHolder being animated.
+     */
+    public void onMoveFinished(ViewHolder item) {
+    }
+
+    /**
+     * Called when a change animation is being started on the given ViewHolder.
+     * The default implementation does nothing. Subclasses may wish to override
+     * this method to handle any ViewHolder-specific operations linked to animation
+     * lifecycles.
+     *
+     * @param item    The ViewHolder being animated.
+     * @param oldItem true if this is the old item that was changed, false if
+     *                it is the new item that replaced the old item.
+     */
+    @SuppressWarnings("UnusedParameters")
+    public void onChangeStarting(ViewHolder item, boolean oldItem) {
+    }
+
+    /**
+     * Called when a change animation has ended on the given ViewHolder.
+     * The default implementation does nothing. Subclasses may wish to override
+     * this method to handle any ViewHolder-specific operations linked to animation
+     * lifecycles.
+     *
+     * @param item    The ViewHolder being animated.
+     * @param oldItem true if this is the old item that was changed, false if
+     *                it is the new item that replaced the old item.
+     */
+    public void onChangeFinished(ViewHolder item, boolean oldItem) {
+    }
+}
+
diff --git a/core/java/com/android/internal/widget/ViewInfoStore.java b/core/java/com/android/internal/widget/ViewInfoStore.java
new file mode 100644
index 0000000..6784a85
--- /dev/null
+++ b/core/java/com/android/internal/widget/ViewInfoStore.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+import android.util.Pools;
+
+import static com.android.internal.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
+import static com.android.internal.widget.RecyclerView.ViewHolder;
+import static com.android.internal.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
+import static com.android.internal.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_AND_DISAPPEAR;
+import static com.android.internal.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_PRE_AND_POST;
+import static com.android.internal.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED;
+import static com.android.internal.widget.ViewInfoStore.InfoRecord.FLAG_POST;
+import static com.android.internal.widget.ViewInfoStore.InfoRecord.FLAG_PRE;
+import static com.android.internal.widget.ViewInfoStore.InfoRecord.FLAG_PRE_AND_POST;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * This class abstracts all tracking for Views to run animations.
+ */
+class ViewInfoStore {
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * View data records for pre-layout
+     */
+    @VisibleForTesting
+    final ArrayMap<ViewHolder, InfoRecord> mLayoutHolderMap = new ArrayMap<>();
+
+    @VisibleForTesting
+    final LongSparseArray<ViewHolder> mOldChangedHolders = new LongSparseArray<>();
+
+    /**
+     * Clears the state and all existing tracking data
+     */
+    void clear() {
+        mLayoutHolderMap.clear();
+        mOldChangedHolders.clear();
+    }
+
+    /**
+     * Adds the item information to the prelayout tracking
+     * @param holder The ViewHolder whose information is being saved
+     * @param info The information to save
+     */
+    void addToPreLayout(ViewHolder holder, ItemHolderInfo info) {
+        InfoRecord record = mLayoutHolderMap.get(holder);
+        if (record == null) {
+            record = InfoRecord.obtain();
+            mLayoutHolderMap.put(holder, record);
+        }
+        record.preInfo = info;
+        record.flags |= FLAG_PRE;
+    }
+
+    boolean isDisappearing(ViewHolder holder) {
+        final InfoRecord record = mLayoutHolderMap.get(holder);
+        return record != null && ((record.flags & FLAG_DISAPPEARED) != 0);
+    }
+
+    /**
+     * Finds the ItemHolderInfo for the given ViewHolder in preLayout list and removes it.
+     *
+     * @param vh The ViewHolder whose information is being queried
+     * @return The ItemHolderInfo for the given ViewHolder or null if it does not exist
+     */
+    @Nullable
+    ItemHolderInfo popFromPreLayout(ViewHolder vh) {
+        return popFromLayoutStep(vh, FLAG_PRE);
+    }
+
+    /**
+     * Finds the ItemHolderInfo for the given ViewHolder in postLayout list and removes it.
+     *
+     * @param vh The ViewHolder whose information is being queried
+     * @return The ItemHolderInfo for the given ViewHolder or null if it does not exist
+     */
+    @Nullable
+    ItemHolderInfo popFromPostLayout(ViewHolder vh) {
+        return popFromLayoutStep(vh, FLAG_POST);
+    }
+
+    private ItemHolderInfo popFromLayoutStep(ViewHolder vh, int flag) {
+        int index = mLayoutHolderMap.indexOfKey(vh);
+        if (index < 0) {
+            return null;
+        }
+        final InfoRecord record = mLayoutHolderMap.valueAt(index);
+        if (record != null && (record.flags & flag) != 0) {
+            record.flags &= ~flag;
+            final ItemHolderInfo info;
+            if (flag == FLAG_PRE) {
+                info = record.preInfo;
+            } else if (flag == FLAG_POST) {
+                info = record.postInfo;
+            } else {
+                throw new IllegalArgumentException("Must provide flag PRE or POST");
+            }
+            // if not pre-post flag is left, clear.
+            if ((record.flags & (FLAG_PRE | FLAG_POST)) == 0) {
+                mLayoutHolderMap.removeAt(index);
+                InfoRecord.recycle(record);
+            }
+            return info;
+        }
+        return null;
+    }
+
+    /**
+     * Adds the given ViewHolder to the oldChangeHolders list
+     * @param key The key to identify the ViewHolder.
+     * @param holder The ViewHolder to store
+     */
+    void addToOldChangeHolders(long key, ViewHolder holder) {
+        mOldChangedHolders.put(key, holder);
+    }
+
+    /**
+     * Adds the given ViewHolder to the appeared in pre layout list. These are Views added by the
+     * LayoutManager during a pre-layout pass. We distinguish them from other views that were
+     * already in the pre-layout so that ItemAnimator can choose to run a different animation for
+     * them.
+     *
+     * @param holder The ViewHolder to store
+     * @param info The information to save
+     */
+    void addToAppearedInPreLayoutHolders(ViewHolder holder, ItemHolderInfo info) {
+        InfoRecord record = mLayoutHolderMap.get(holder);
+        if (record == null) {
+            record = InfoRecord.obtain();
+            mLayoutHolderMap.put(holder, record);
+        }
+        record.flags |= FLAG_APPEAR;
+        record.preInfo = info;
+    }
+
+    /**
+     * Checks whether the given ViewHolder is in preLayout list
+     * @param viewHolder The ViewHolder to query
+     *
+     * @return True if the ViewHolder is present in preLayout, false otherwise
+     */
+    boolean isInPreLayout(ViewHolder viewHolder) {
+        final InfoRecord record = mLayoutHolderMap.get(viewHolder);
+        return record != null && (record.flags & FLAG_PRE) != 0;
+    }
+
+    /**
+     * Queries the oldChangeHolder list for the given key. If they are not tracked, simply returns
+     * null.
+     * @param key The key to be used to find the ViewHolder.
+     *
+     * @return A ViewHolder if exists or null if it does not exist.
+     */
+    ViewHolder getFromOldChangeHolders(long key) {
+        return mOldChangedHolders.get(key);
+    }
+
+    /**
+     * Adds the item information to the post layout list
+     * @param holder The ViewHolder whose information is being saved
+     * @param info The information to save
+     */
+    void addToPostLayout(ViewHolder holder, ItemHolderInfo info) {
+        InfoRecord record = mLayoutHolderMap.get(holder);
+        if (record == null) {
+            record = InfoRecord.obtain();
+            mLayoutHolderMap.put(holder, record);
+        }
+        record.postInfo = info;
+        record.flags |= FLAG_POST;
+    }
+
+    /**
+     * A ViewHolder might be added by the LayoutManager just to animate its disappearance.
+     * This list holds such items so that we can animate / recycle these ViewHolders properly.
+     *
+     * @param holder The ViewHolder which disappeared during a layout.
+     */
+    void addToDisappearedInLayout(ViewHolder holder) {
+        InfoRecord record = mLayoutHolderMap.get(holder);
+        if (record == null) {
+            record = InfoRecord.obtain();
+            mLayoutHolderMap.put(holder, record);
+        }
+        record.flags |= FLAG_DISAPPEARED;
+    }
+
+    /**
+     * Removes a ViewHolder from disappearing list.
+     * @param holder The ViewHolder to be removed from the disappearing list.
+     */
+    void removeFromDisappearedInLayout(ViewHolder holder) {
+        InfoRecord record = mLayoutHolderMap.get(holder);
+        if (record == null) {
+            return;
+        }
+        record.flags &= ~FLAG_DISAPPEARED;
+    }
+
+    void process(ProcessCallback callback) {
+        for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) {
+            final ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
+            final InfoRecord record = mLayoutHolderMap.removeAt(index);
+            if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
+                // Appeared then disappeared. Not useful for animations.
+                callback.unused(viewHolder);
+            } else if ((record.flags & FLAG_DISAPPEARED) != 0) {
+                // Set as "disappeared" by the LayoutManager (addDisappearingView)
+                if (record.preInfo == null) {
+                    // similar to appear disappear but happened between different layout passes.
+                    // this can happen when the layout manager is using auto-measure
+                    callback.unused(viewHolder);
+                } else {
+                    callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
+                }
+            } else if ((record.flags & FLAG_APPEAR_PRE_AND_POST) == FLAG_APPEAR_PRE_AND_POST) {
+                // Appeared in the layout but not in the adapter (e.g. entered the viewport)
+                callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
+            } else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) {
+                // Persistent in both passes. Animate persistence
+                callback.processPersistent(viewHolder, record.preInfo, record.postInfo);
+            } else if ((record.flags & FLAG_PRE) != 0) {
+                // Was in pre-layout, never been added to post layout
+                callback.processDisappeared(viewHolder, record.preInfo, null);
+            } else if ((record.flags & FLAG_POST) != 0) {
+                // Was not in pre-layout, been added to post layout
+                callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
+            } else if ((record.flags & FLAG_APPEAR) != 0) {
+                // Scrap view. RecyclerView will handle removing/recycling this.
+            } else if (DEBUG) {
+                throw new IllegalStateException("record without any reasonable flag combination:/");
+            }
+            InfoRecord.recycle(record);
+        }
+    }
+
+    /**
+     * Removes the ViewHolder from all list
+     * @param holder The ViewHolder which we should stop tracking
+     */
+    void removeViewHolder(ViewHolder holder) {
+        for (int i = mOldChangedHolders.size() - 1; i >= 0; i--) {
+            if (holder == mOldChangedHolders.valueAt(i)) {
+                mOldChangedHolders.removeAt(i);
+                break;
+            }
+        }
+        final InfoRecord info = mLayoutHolderMap.remove(holder);
+        if (info != null) {
+            InfoRecord.recycle(info);
+        }
+    }
+
+    void onDetach() {
+        InfoRecord.drainCache();
+    }
+
+    public void onViewDetached(ViewHolder viewHolder) {
+        removeFromDisappearedInLayout(viewHolder);
+    }
+
+    interface ProcessCallback {
+        void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
+                @Nullable ItemHolderInfo postInfo);
+        void processAppeared(ViewHolder viewHolder, @Nullable ItemHolderInfo preInfo,
+                ItemHolderInfo postInfo);
+        void processPersistent(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
+                @NonNull ItemHolderInfo postInfo);
+        void unused(ViewHolder holder);
+    }
+
+    static class InfoRecord {
+        // disappearing list
+        static final int FLAG_DISAPPEARED = 1;
+        // appear in pre layout list
+        static final int FLAG_APPEAR = 1 << 1;
+        // pre layout, this is necessary to distinguish null item info
+        static final int FLAG_PRE = 1 << 2;
+        // post layout, this is necessary to distinguish null item info
+        static final int FLAG_POST = 1 << 3;
+        static final int FLAG_APPEAR_AND_DISAPPEAR = FLAG_APPEAR | FLAG_DISAPPEARED;
+        static final int FLAG_PRE_AND_POST = FLAG_PRE | FLAG_POST;
+        static final int FLAG_APPEAR_PRE_AND_POST = FLAG_APPEAR | FLAG_PRE | FLAG_POST;
+        int flags;
+        @Nullable ItemHolderInfo preInfo;
+        @Nullable ItemHolderInfo postInfo;
+        static Pools.Pool<InfoRecord> sPool = new Pools.SimplePool<>(20);
+
+        private InfoRecord() {
+        }
+
+        static InfoRecord obtain() {
+            InfoRecord record = sPool.acquire();
+            return record == null ? new InfoRecord() : record;
+        }
+
+        static void recycle(InfoRecord record) {
+            record.flags = 0;
+            record.preInfo = null;
+            record.postInfo = null;
+            sPool.release(record);
+        }
+
+        static void drainCache() {
+            //noinspection StatementWithEmptyBody
+            while (sPool.acquire() != null);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/helper/ItemTouchHelper.java b/core/java/com/android/internal/widget/helper/ItemTouchHelper.java
new file mode 100644
index 0000000..9636ed8
--- /dev/null
+++ b/core/java/com/android/internal/widget/helper/ItemTouchHelper.java
@@ -0,0 +1,2391 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.helper;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
+import android.view.animation.Interpolator;
+
+import com.android.internal.R;
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+import com.android.internal.widget.RecyclerView.OnItemTouchListener;
+import com.android.internal.widget.RecyclerView.ViewHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
+ * <p>
+ * It works with a RecyclerView and a Callback class, which configures what type of interactions
+ * are enabled and also receives events when user performs these actions.
+ * <p>
+ * Depending on which functionality you support, you should override
+ * {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or
+ * {@link Callback#onSwiped(ViewHolder, int)}.
+ * <p>
+ * This class is designed to work with any LayoutManager but for certain situations, it can be
+ * optimized for your custom LayoutManager by extending methods in the
+ * {@link ItemTouchHelper.Callback} class or implementing {@link ItemTouchHelper.ViewDropHandler}
+ * interface in your LayoutManager.
+ * <p>
+ * By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. On
+ * platforms older than Honeycomb, ItemTouchHelper uses canvas translations and View's visibility
+ * property to move items in response to touch events. You can customize these behaviors by
+ * overriding {@link Callback#onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int,
+ * boolean)}
+ * or {@link Callback#onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
+ * boolean)}.
+ * <p/>
+ * Most of the time, you only need to override <code>onChildDraw</code> but due to limitations of
+ * platform prior to Honeycomb, you may need to implement <code>onChildDrawOver</code> as well.
+ */
+public class ItemTouchHelper extends RecyclerView.ItemDecoration
+        implements RecyclerView.OnChildAttachStateChangeListener {
+
+    /**
+     * Up direction, used for swipe & drag control.
+     */
+    public static final int UP = 1;
+
+    /**
+     * Down direction, used for swipe & drag control.
+     */
+    public static final int DOWN = 1 << 1;
+
+    /**
+     * Left direction, used for swipe & drag control.
+     */
+    public static final int LEFT = 1 << 2;
+
+    /**
+     * Right direction, used for swipe & drag control.
+     */
+    public static final int RIGHT = 1 << 3;
+
+    // If you change these relative direction values, update Callback#convertToAbsoluteDirection,
+    // Callback#convertToRelativeDirection.
+    /**
+     * Horizontal start direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout
+     * direction. Used for swipe & drag control.
+     */
+    public static final int START = LEFT << 2;
+
+    /**
+     * Horizontal end direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout
+     * direction. Used for swipe & drag control.
+     */
+    public static final int END = RIGHT << 2;
+
+    /**
+     * ItemTouchHelper is in idle state. At this state, either there is no related motion event by
+     * the user or latest motion events have not yet triggered a swipe or drag.
+     */
+    public static final int ACTION_STATE_IDLE = 0;
+
+    /**
+     * A View is currently being swiped.
+     */
+    public static final int ACTION_STATE_SWIPE = 1;
+
+    /**
+     * A View is currently being dragged.
+     */
+    public static final int ACTION_STATE_DRAG = 2;
+
+    /**
+     * Animation type for views which are swiped successfully.
+     */
+    public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 1 << 1;
+
+    /**
+     * Animation type for views which are not completely swiped thus will animate back to their
+     * original position.
+     */
+    public static final int ANIMATION_TYPE_SWIPE_CANCEL = 1 << 2;
+
+    /**
+     * Animation type for views that were dragged and now will animate to their final position.
+     */
+    public static final int ANIMATION_TYPE_DRAG = 1 << 3;
+
+    static final String TAG = "ItemTouchHelper";
+
+    static final boolean DEBUG = false;
+
+    static final int ACTIVE_POINTER_ID_NONE = -1;
+
+    static final int DIRECTION_FLAG_COUNT = 8;
+
+    private static final int ACTION_MODE_IDLE_MASK = (1 << DIRECTION_FLAG_COUNT) - 1;
+
+    static final int ACTION_MODE_SWIPE_MASK = ACTION_MODE_IDLE_MASK << DIRECTION_FLAG_COUNT;
+
+    static final int ACTION_MODE_DRAG_MASK = ACTION_MODE_SWIPE_MASK << DIRECTION_FLAG_COUNT;
+
+    /**
+     * The unit we are using to track velocity
+     */
+    private static final int PIXELS_PER_SECOND = 1000;
+
+    /**
+     * Views, whose state should be cleared after they are detached from RecyclerView.
+     * This is necessary after swipe dismissing an item. We wait until animator finishes its job
+     * to clean these views.
+     */
+    final List<View> mPendingCleanup = new ArrayList<View>();
+
+    /**
+     * Re-use array to calculate dx dy for a ViewHolder
+     */
+    private final float[] mTmpPosition = new float[2];
+
+    /**
+     * Currently selected view holder
+     */
+    ViewHolder mSelected = null;
+
+    /**
+     * The reference coordinates for the action start. For drag & drop, this is the time long
+     * press is completed vs for swipe, this is the initial touch point.
+     */
+    float mInitialTouchX;
+
+    float mInitialTouchY;
+
+    /**
+     * Set when ItemTouchHelper is assigned to a RecyclerView.
+     */
+    float mSwipeEscapeVelocity;
+
+    /**
+     * Set when ItemTouchHelper is assigned to a RecyclerView.
+     */
+    float mMaxSwipeVelocity;
+
+    /**
+     * The diff between the last event and initial touch.
+     */
+    float mDx;
+
+    float mDy;
+
+    /**
+     * The coordinates of the selected view at the time it is selected. We record these values
+     * when action starts so that we can consistently position it even if LayoutManager moves the
+     * View.
+     */
+    float mSelectedStartX;
+
+    float mSelectedStartY;
+
+    /**
+     * The pointer we are tracking.
+     */
+    int mActivePointerId = ACTIVE_POINTER_ID_NONE;
+
+    /**
+     * Developer callback which controls the behavior of ItemTouchHelper.
+     */
+    Callback mCallback;
+
+    /**
+     * Current mode.
+     */
+    int mActionState = ACTION_STATE_IDLE;
+
+    /**
+     * The direction flags obtained from unmasking
+     * {@link Callback#getAbsoluteMovementFlags(RecyclerView, ViewHolder)} for the current
+     * action state.
+     */
+    int mSelectedFlags;
+
+    /**
+     * When a View is dragged or swiped and needs to go back to where it was, we create a Recover
+     * Animation and animate it to its location using this custom Animator, instead of using
+     * framework Animators.
+     * Using framework animators has the side effect of clashing with ItemAnimator, creating
+     * jumpy UIs.
+     */
+    List<RecoverAnimation> mRecoverAnimations = new ArrayList<RecoverAnimation>();
+
+    private int mSlop;
+
+    RecyclerView mRecyclerView;
+
+    /**
+     * When user drags a view to the edge, we start scrolling the LayoutManager as long as View
+     * is partially out of bounds.
+     */
+    final Runnable mScrollRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (mSelected != null && scrollIfNecessary()) {
+                if (mSelected != null) { //it might be lost during scrolling
+                    moveIfNecessary(mSelected);
+                }
+                mRecyclerView.removeCallbacks(mScrollRunnable);
+                mRecyclerView.postOnAnimation(this);
+            }
+        }
+    };
+
+    /**
+     * Used for detecting fling swipe
+     */
+    VelocityTracker mVelocityTracker;
+
+    //re-used list for selecting a swap target
+    private List<ViewHolder> mSwapTargets;
+
+    //re used for for sorting swap targets
+    private List<Integer> mDistances;
+
+    /**
+     * If drag & drop is supported, we use child drawing order to bring them to front.
+     */
+    private RecyclerView.ChildDrawingOrderCallback mChildDrawingOrderCallback = null;
+
+    /**
+     * This keeps a reference to the child dragged by the user. Even after user stops dragging,
+     * until view reaches its final position (end of recover animation), we keep a reference so
+     * that it can be drawn above other children.
+     */
+    View mOverdrawChild = null;
+
+    /**
+     * We cache the position of the overdraw child to avoid recalculating it each time child
+     * position callback is called. This value is invalidated whenever a child is attached or
+     * detached.
+     */
+    int mOverdrawChildPosition = -1;
+
+    /**
+     * Used to detect long press.
+     */
+    GestureDetector mGestureDetector;
+
+    private final OnItemTouchListener mOnItemTouchListener = new OnItemTouchListener() {
+        @Override
+        public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) {
+            mGestureDetector.onTouchEvent(event);
+            if (DEBUG) {
+                Log.d(TAG, "intercept: x:" + event.getX() + ",y:" + event.getY() + ", " + event);
+            }
+            final int action = event.getActionMasked();
+            if (action == MotionEvent.ACTION_DOWN) {
+                mActivePointerId = event.getPointerId(0);
+                mInitialTouchX = event.getX();
+                mInitialTouchY = event.getY();
+                obtainVelocityTracker();
+                if (mSelected == null) {
+                    final RecoverAnimation animation = findAnimation(event);
+                    if (animation != null) {
+                        mInitialTouchX -= animation.mX;
+                        mInitialTouchY -= animation.mY;
+                        endRecoverAnimation(animation.mViewHolder, true);
+                        if (mPendingCleanup.remove(animation.mViewHolder.itemView)) {
+                            mCallback.clearView(mRecyclerView, animation.mViewHolder);
+                        }
+                        select(animation.mViewHolder, animation.mActionState);
+                        updateDxDy(event, mSelectedFlags, 0);
+                    }
+                }
+            } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+                mActivePointerId = ACTIVE_POINTER_ID_NONE;
+                select(null, ACTION_STATE_IDLE);
+            } else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) {
+                // in a non scroll orientation, if distance change is above threshold, we
+                // can select the item
+                final int index = event.findPointerIndex(mActivePointerId);
+                if (DEBUG) {
+                    Log.d(TAG, "pointer index " + index);
+                }
+                if (index >= 0) {
+                    checkSelectForSwipe(action, event, index);
+                }
+            }
+            if (mVelocityTracker != null) {
+                mVelocityTracker.addMovement(event);
+            }
+            return mSelected != null;
+        }
+
+        @Override
+        public void onTouchEvent(RecyclerView recyclerView, MotionEvent event) {
+            mGestureDetector.onTouchEvent(event);
+            if (DEBUG) {
+                Log.d(TAG,
+                        "on touch: x:" + mInitialTouchX + ",y:" + mInitialTouchY + ", :" + event);
+            }
+            if (mVelocityTracker != null) {
+                mVelocityTracker.addMovement(event);
+            }
+            if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {
+                return;
+            }
+            final int action = event.getActionMasked();
+            final int activePointerIndex = event.findPointerIndex(mActivePointerId);
+            if (activePointerIndex >= 0) {
+                checkSelectForSwipe(action, event, activePointerIndex);
+            }
+            ViewHolder viewHolder = mSelected;
+            if (viewHolder == null) {
+                return;
+            }
+            switch (action) {
+                case MotionEvent.ACTION_MOVE: {
+                    // Find the index of the active pointer and fetch its position
+                    if (activePointerIndex >= 0) {
+                        updateDxDy(event, mSelectedFlags, activePointerIndex);
+                        moveIfNecessary(viewHolder);
+                        mRecyclerView.removeCallbacks(mScrollRunnable);
+                        mScrollRunnable.run();
+                        mRecyclerView.invalidate();
+                    }
+                    break;
+                }
+                case MotionEvent.ACTION_CANCEL:
+                    if (mVelocityTracker != null) {
+                        mVelocityTracker.clear();
+                    }
+                    // fall through
+                case MotionEvent.ACTION_UP:
+                    select(null, ACTION_STATE_IDLE);
+                    mActivePointerId = ACTIVE_POINTER_ID_NONE;
+                    break;
+                case MotionEvent.ACTION_POINTER_UP: {
+                    final int pointerIndex = event.getActionIndex();
+                    final int pointerId = event.getPointerId(pointerIndex);
+                    if (pointerId == mActivePointerId) {
+                        // This was our active pointer going up. Choose a new
+                        // active pointer and adjust accordingly.
+                        final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+                        mActivePointerId = event.getPointerId(newPointerIndex);
+                        updateDxDy(event, mSelectedFlags, pointerIndex);
+                    }
+                    break;
+                }
+            }
+        }
+
+        @Override
+        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+            if (!disallowIntercept) {
+                return;
+            }
+            select(null, ACTION_STATE_IDLE);
+        }
+    };
+
+    /**
+     * Temporary rect instance that is used when we need to lookup Item decorations.
+     */
+    private Rect mTmpRect;
+
+    /**
+     * When user started to drag scroll. Reset when we don't scroll
+     */
+    private long mDragScrollStartTimeInMs;
+
+    /**
+     * Creates an ItemTouchHelper that will work with the given Callback.
+     * <p>
+     * You can attach ItemTouchHelper to a RecyclerView via
+     * {@link #attachToRecyclerView(RecyclerView)}. Upon attaching, it will add an item decoration,
+     * an onItemTouchListener and a Child attach / detach listener to the RecyclerView.
+     *
+     * @param callback The Callback which controls the behavior of this touch helper.
+     */
+    public ItemTouchHelper(Callback callback) {
+        mCallback = callback;
+    }
+
+    private static boolean hitTest(View child, float x, float y, float left, float top) {
+        return x >= left
+                && x <= left + child.getWidth()
+                && y >= top
+                && y <= top + child.getHeight();
+    }
+
+    /**
+     * Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already
+     * attached to a RecyclerView, it will first detach from the previous one. You can call this
+     * method with {@code null} to detach it from the current RecyclerView.
+     *
+     * @param recyclerView The RecyclerView instance to which you want to add this helper or
+     *                     {@code null} if you want to remove ItemTouchHelper from the current
+     *                     RecyclerView.
+     */
+    public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
+        if (mRecyclerView == recyclerView) {
+            return; // nothing to do
+        }
+        if (mRecyclerView != null) {
+            destroyCallbacks();
+        }
+        mRecyclerView = recyclerView;
+        if (mRecyclerView != null) {
+            final Resources resources = recyclerView.getResources();
+            mSwipeEscapeVelocity = resources
+                    .getDimension(R.dimen.item_touch_helper_swipe_escape_velocity);
+            mMaxSwipeVelocity = resources
+                    .getDimension(R.dimen.item_touch_helper_swipe_escape_max_velocity);
+            setupCallbacks();
+        }
+    }
+
+    private void setupCallbacks() {
+        ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());
+        mSlop = vc.getScaledTouchSlop();
+        mRecyclerView.addItemDecoration(this);
+        mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);
+        mRecyclerView.addOnChildAttachStateChangeListener(this);
+        initGestureDetector();
+    }
+
+    private void destroyCallbacks() {
+        mRecyclerView.removeItemDecoration(this);
+        mRecyclerView.removeOnItemTouchListener(mOnItemTouchListener);
+        mRecyclerView.removeOnChildAttachStateChangeListener(this);
+        // clean all attached
+        final int recoverAnimSize = mRecoverAnimations.size();
+        for (int i = recoverAnimSize - 1; i >= 0; i--) {
+            final RecoverAnimation recoverAnimation = mRecoverAnimations.get(0);
+            mCallback.clearView(mRecyclerView, recoverAnimation.mViewHolder);
+        }
+        mRecoverAnimations.clear();
+        mOverdrawChild = null;
+        mOverdrawChildPosition = -1;
+        releaseVelocityTracker();
+    }
+
+    private void initGestureDetector() {
+        if (mGestureDetector != null) {
+            return;
+        }
+        mGestureDetector = new GestureDetector(mRecyclerView.getContext(),
+                new ItemTouchHelperGestureListener());
+    }
+
+    private void getSelectedDxDy(float[] outPosition) {
+        if ((mSelectedFlags & (LEFT | RIGHT)) != 0) {
+            outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft();
+        } else {
+            outPosition[0] = mSelected.itemView.getTranslationX();
+        }
+        if ((mSelectedFlags & (UP | DOWN)) != 0) {
+            outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop();
+        } else {
+            outPosition[1] = mSelected.itemView.getTranslationY();
+        }
+    }
+
+    @Override
+    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
+        float dx = 0, dy = 0;
+        if (mSelected != null) {
+            getSelectedDxDy(mTmpPosition);
+            dx = mTmpPosition[0];
+            dy = mTmpPosition[1];
+        }
+        mCallback.onDrawOver(c, parent, mSelected,
+                mRecoverAnimations, mActionState, dx, dy);
+    }
+
+    @Override
+    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+        // we don't know if RV changed something so we should invalidate this index.
+        mOverdrawChildPosition = -1;
+        float dx = 0, dy = 0;
+        if (mSelected != null) {
+            getSelectedDxDy(mTmpPosition);
+            dx = mTmpPosition[0];
+            dy = mTmpPosition[1];
+        }
+        mCallback.onDraw(c, parent, mSelected,
+                mRecoverAnimations, mActionState, dx, dy);
+    }
+
+    /**
+     * Starts dragging or swiping the given View. Call with null if you want to clear it.
+     *
+     * @param selected    The ViewHolder to drag or swipe. Can be null if you want to cancel the
+     *                    current action
+     * @param actionState The type of action
+     */
+    void select(ViewHolder selected, int actionState) {
+        if (selected == mSelected && actionState == mActionState) {
+            return;
+        }
+        mDragScrollStartTimeInMs = Long.MIN_VALUE;
+        final int prevActionState = mActionState;
+        // prevent duplicate animations
+        endRecoverAnimation(selected, true);
+        mActionState = actionState;
+        if (actionState == ACTION_STATE_DRAG) {
+            // we remove after animation is complete. this means we only elevate the last drag
+            // child but that should perform good enough as it is very hard to start dragging a
+            // new child before the previous one settles.
+            mOverdrawChild = selected.itemView;
+            addChildDrawingOrderCallback();
+        }
+        int actionStateMask = (1 << (DIRECTION_FLAG_COUNT + DIRECTION_FLAG_COUNT * actionState))
+                - 1;
+        boolean preventLayout = false;
+
+        if (mSelected != null) {
+            final ViewHolder prevSelected = mSelected;
+            if (prevSelected.itemView.getParent() != null) {
+                final int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0
+                        : swipeIfNecessary(prevSelected);
+                releaseVelocityTracker();
+                // find where we should animate to
+                final float targetTranslateX, targetTranslateY;
+                int animationType;
+                switch (swipeDir) {
+                    case LEFT:
+                    case RIGHT:
+                    case START:
+                    case END:
+                        targetTranslateY = 0;
+                        targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();
+                        break;
+                    case UP:
+                    case DOWN:
+                        targetTranslateX = 0;
+                        targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();
+                        break;
+                    default:
+                        targetTranslateX = 0;
+                        targetTranslateY = 0;
+                }
+                if (prevActionState == ACTION_STATE_DRAG) {
+                    animationType = ANIMATION_TYPE_DRAG;
+                } else if (swipeDir > 0) {
+                    animationType = ANIMATION_TYPE_SWIPE_SUCCESS;
+                } else {
+                    animationType = ANIMATION_TYPE_SWIPE_CANCEL;
+                }
+                getSelectedDxDy(mTmpPosition);
+                final float currentTranslateX = mTmpPosition[0];
+                final float currentTranslateY = mTmpPosition[1];
+                final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType,
+                        prevActionState, currentTranslateX, currentTranslateY,
+                        targetTranslateX, targetTranslateY) {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        super.onAnimationEnd(animation);
+                        if (this.mOverridden) {
+                            return;
+                        }
+                        if (swipeDir <= 0) {
+                            // this is a drag or failed swipe. recover immediately
+                            mCallback.clearView(mRecyclerView, prevSelected);
+                            // full cleanup will happen on onDrawOver
+                        } else {
+                            // wait until remove animation is complete.
+                            mPendingCleanup.add(prevSelected.itemView);
+                            mIsPendingCleanup = true;
+                            if (swipeDir > 0) {
+                                // Animation might be ended by other animators during a layout.
+                                // We defer callback to avoid editing adapter during a layout.
+                                postDispatchSwipe(this, swipeDir);
+                            }
+                        }
+                        // removed from the list after it is drawn for the last time
+                        if (mOverdrawChild == prevSelected.itemView) {
+                            removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
+                        }
+                    }
+                };
+                final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType,
+                        targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY);
+                rv.setDuration(duration);
+                mRecoverAnimations.add(rv);
+                rv.start();
+                preventLayout = true;
+            } else {
+                removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
+                mCallback.clearView(mRecyclerView, prevSelected);
+            }
+            mSelected = null;
+        }
+        if (selected != null) {
+            mSelectedFlags =
+                    (mCallback.getAbsoluteMovementFlags(mRecyclerView, selected) & actionStateMask)
+                            >> (mActionState * DIRECTION_FLAG_COUNT);
+            mSelectedStartX = selected.itemView.getLeft();
+            mSelectedStartY = selected.itemView.getTop();
+            mSelected = selected;
+
+            if (actionState == ACTION_STATE_DRAG) {
+                mSelected.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+            }
+        }
+        final ViewParent rvParent = mRecyclerView.getParent();
+        if (rvParent != null) {
+            rvParent.requestDisallowInterceptTouchEvent(mSelected != null);
+        }
+        if (!preventLayout) {
+            mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout();
+        }
+        mCallback.onSelectedChanged(mSelected, mActionState);
+        mRecyclerView.invalidate();
+    }
+
+    void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir) {
+        // wait until animations are complete.
+        mRecyclerView.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mRecyclerView != null && mRecyclerView.isAttachedToWindow()
+                        && !anim.mOverridden
+                        && anim.mViewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {
+                    final RecyclerView.ItemAnimator animator = mRecyclerView.getItemAnimator();
+                    // if animator is running or we have other active recover animations, we try
+                    // not to call onSwiped because DefaultItemAnimator is not good at merging
+                    // animations. Instead, we wait and batch.
+                    if ((animator == null || !animator.isRunning(null))
+                            && !hasRunningRecoverAnim()) {
+                        mCallback.onSwiped(anim.mViewHolder, swipeDir);
+                    } else {
+                        mRecyclerView.post(this);
+                    }
+                }
+            }
+        });
+    }
+
+    boolean hasRunningRecoverAnim() {
+        final int size = mRecoverAnimations.size();
+        for (int i = 0; i < size; i++) {
+            if (!mRecoverAnimations.get(i).mEnded) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * If user drags the view to the edge, trigger a scroll if necessary.
+     */
+    boolean scrollIfNecessary() {
+        if (mSelected == null) {
+            mDragScrollStartTimeInMs = Long.MIN_VALUE;
+            return false;
+        }
+        final long now = System.currentTimeMillis();
+        final long scrollDuration = mDragScrollStartTimeInMs
+                == Long.MIN_VALUE ? 0 : now - mDragScrollStartTimeInMs;
+        RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
+        if (mTmpRect == null) {
+            mTmpRect = new Rect();
+        }
+        int scrollX = 0;
+        int scrollY = 0;
+        lm.calculateItemDecorationsForChild(mSelected.itemView, mTmpRect);
+        if (lm.canScrollHorizontally()) {
+            int curX = (int) (mSelectedStartX + mDx);
+            final int leftDiff = curX - mTmpRect.left - mRecyclerView.getPaddingLeft();
+            if (mDx < 0 && leftDiff < 0) {
+                scrollX = leftDiff;
+            } else if (mDx > 0) {
+                final int rightDiff =
+                        curX + mSelected.itemView.getWidth() + mTmpRect.right
+                                - (mRecyclerView.getWidth() - mRecyclerView.getPaddingRight());
+                if (rightDiff > 0) {
+                    scrollX = rightDiff;
+                }
+            }
+        }
+        if (lm.canScrollVertically()) {
+            int curY = (int) (mSelectedStartY + mDy);
+            final int topDiff = curY - mTmpRect.top - mRecyclerView.getPaddingTop();
+            if (mDy < 0 && topDiff < 0) {
+                scrollY = topDiff;
+            } else if (mDy > 0) {
+                final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom
+                        - (mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());
+                if (bottomDiff > 0) {
+                    scrollY = bottomDiff;
+                }
+            }
+        }
+        if (scrollX != 0) {
+            scrollX = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,
+                    mSelected.itemView.getWidth(), scrollX,
+                    mRecyclerView.getWidth(), scrollDuration);
+        }
+        if (scrollY != 0) {
+            scrollY = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,
+                    mSelected.itemView.getHeight(), scrollY,
+                    mRecyclerView.getHeight(), scrollDuration);
+        }
+        if (scrollX != 0 || scrollY != 0) {
+            if (mDragScrollStartTimeInMs == Long.MIN_VALUE) {
+                mDragScrollStartTimeInMs = now;
+            }
+            mRecyclerView.scrollBy(scrollX, scrollY);
+            return true;
+        }
+        mDragScrollStartTimeInMs = Long.MIN_VALUE;
+        return false;
+    }
+
+    private List<ViewHolder> findSwapTargets(ViewHolder viewHolder) {
+        if (mSwapTargets == null) {
+            mSwapTargets = new ArrayList<ViewHolder>();
+            mDistances = new ArrayList<Integer>();
+        } else {
+            mSwapTargets.clear();
+            mDistances.clear();
+        }
+        final int margin = mCallback.getBoundingBoxMargin();
+        final int left = Math.round(mSelectedStartX + mDx) - margin;
+        final int top = Math.round(mSelectedStartY + mDy) - margin;
+        final int right = left + viewHolder.itemView.getWidth() + 2 * margin;
+        final int bottom = top + viewHolder.itemView.getHeight() + 2 * margin;
+        final int centerX = (left + right) / 2;
+        final int centerY = (top + bottom) / 2;
+        final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
+        final int childCount = lm.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View other = lm.getChildAt(i);
+            if (other == viewHolder.itemView) {
+                continue; //myself!
+            }
+            if (other.getBottom() < top || other.getTop() > bottom
+                    || other.getRight() < left || other.getLeft() > right) {
+                continue;
+            }
+            final ViewHolder otherVh = mRecyclerView.getChildViewHolder(other);
+            if (mCallback.canDropOver(mRecyclerView, mSelected, otherVh)) {
+                // find the index to add
+                final int dx = Math.abs(centerX - (other.getLeft() + other.getRight()) / 2);
+                final int dy = Math.abs(centerY - (other.getTop() + other.getBottom()) / 2);
+                final int dist = dx * dx + dy * dy;
+
+                int pos = 0;
+                final int cnt = mSwapTargets.size();
+                for (int j = 0; j < cnt; j++) {
+                    if (dist > mDistances.get(j)) {
+                        pos++;
+                    } else {
+                        break;
+                    }
+                }
+                mSwapTargets.add(pos, otherVh);
+                mDistances.add(pos, dist);
+            }
+        }
+        return mSwapTargets;
+    }
+
+    /**
+     * Checks if we should swap w/ another view holder.
+     */
+    void moveIfNecessary(ViewHolder viewHolder) {
+        if (mRecyclerView.isLayoutRequested()) {
+            return;
+        }
+        if (mActionState != ACTION_STATE_DRAG) {
+            return;
+        }
+
+        final float threshold = mCallback.getMoveThreshold(viewHolder);
+        final int x = (int) (mSelectedStartX + mDx);
+        final int y = (int) (mSelectedStartY + mDy);
+        if (Math.abs(y - viewHolder.itemView.getTop()) < viewHolder.itemView.getHeight() * threshold
+                && Math.abs(x - viewHolder.itemView.getLeft())
+                < viewHolder.itemView.getWidth() * threshold) {
+            return;
+        }
+        List<ViewHolder> swapTargets = findSwapTargets(viewHolder);
+        if (swapTargets.size() == 0) {
+            return;
+        }
+        // may swap.
+        ViewHolder target = mCallback.chooseDropTarget(viewHolder, swapTargets, x, y);
+        if (target == null) {
+            mSwapTargets.clear();
+            mDistances.clear();
+            return;
+        }
+        final int toPosition = target.getAdapterPosition();
+        final int fromPosition = viewHolder.getAdapterPosition();
+        if (mCallback.onMove(mRecyclerView, viewHolder, target)) {
+            // keep target visible
+            mCallback.onMoved(mRecyclerView, viewHolder, fromPosition,
+                    target, toPosition, x, y);
+        }
+    }
+
+    @Override
+    public void onChildViewAttachedToWindow(View view) {
+    }
+
+    @Override
+    public void onChildViewDetachedFromWindow(View view) {
+        removeChildDrawingOrderCallbackIfNecessary(view);
+        final ViewHolder holder = mRecyclerView.getChildViewHolder(view);
+        if (holder == null) {
+            return;
+        }
+        if (mSelected != null && holder == mSelected) {
+            select(null, ACTION_STATE_IDLE);
+        } else {
+            endRecoverAnimation(holder, false); // this may push it into pending cleanup list.
+            if (mPendingCleanup.remove(holder.itemView)) {
+                mCallback.clearView(mRecyclerView, holder);
+            }
+        }
+    }
+
+    /**
+     * Returns the animation type or 0 if cannot be found.
+     */
+    int endRecoverAnimation(ViewHolder viewHolder, boolean override) {
+        final int recoverAnimSize = mRecoverAnimations.size();
+        for (int i = recoverAnimSize - 1; i >= 0; i--) {
+            final RecoverAnimation anim = mRecoverAnimations.get(i);
+            if (anim.mViewHolder == viewHolder) {
+                anim.mOverridden |= override;
+                if (!anim.mEnded) {
+                    anim.cancel();
+                }
+                mRecoverAnimations.remove(i);
+                return anim.mAnimationType;
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+            RecyclerView.State state) {
+        outRect.setEmpty();
+    }
+
+    void obtainVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+        }
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    private void releaseVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    private ViewHolder findSwipedView(MotionEvent motionEvent) {
+        final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
+        if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {
+            return null;
+        }
+        final int pointerIndex = motionEvent.findPointerIndex(mActivePointerId);
+        final float dx = motionEvent.getX(pointerIndex) - mInitialTouchX;
+        final float dy = motionEvent.getY(pointerIndex) - mInitialTouchY;
+        final float absDx = Math.abs(dx);
+        final float absDy = Math.abs(dy);
+
+        if (absDx < mSlop && absDy < mSlop) {
+            return null;
+        }
+        if (absDx > absDy && lm.canScrollHorizontally()) {
+            return null;
+        } else if (absDy > absDx && lm.canScrollVertically()) {
+            return null;
+        }
+        View child = findChildView(motionEvent);
+        if (child == null) {
+            return null;
+        }
+        return mRecyclerView.getChildViewHolder(child);
+    }
+
+    /**
+     * Checks whether we should select a View for swiping.
+     */
+    boolean checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) {
+        if (mSelected != null || action != MotionEvent.ACTION_MOVE
+                || mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) {
+            return false;
+        }
+        if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {
+            return false;
+        }
+        final ViewHolder vh = findSwipedView(motionEvent);
+        if (vh == null) {
+            return false;
+        }
+        final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh);
+
+        final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK)
+                >> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE);
+
+        if (swipeFlags == 0) {
+            return false;
+        }
+
+        // mDx and mDy are only set in allowed directions. We use custom x/y here instead of
+        // updateDxDy to avoid swiping if user moves more in the other direction
+        final float x = motionEvent.getX(pointerIndex);
+        final float y = motionEvent.getY(pointerIndex);
+
+        // Calculate the distance moved
+        final float dx = x - mInitialTouchX;
+        final float dy = y - mInitialTouchY;
+        // swipe target is chose w/o applying flags so it does not really check if swiping in that
+        // direction is allowed. This why here, we use mDx mDy to check slope value again.
+        final float absDx = Math.abs(dx);
+        final float absDy = Math.abs(dy);
+
+        if (absDx < mSlop && absDy < mSlop) {
+            return false;
+        }
+        if (absDx > absDy) {
+            if (dx < 0 && (swipeFlags & LEFT) == 0) {
+                return false;
+            }
+            if (dx > 0 && (swipeFlags & RIGHT) == 0) {
+                return false;
+            }
+        } else {
+            if (dy < 0 && (swipeFlags & UP) == 0) {
+                return false;
+            }
+            if (dy > 0 && (swipeFlags & DOWN) == 0) {
+                return false;
+            }
+        }
+        mDx = mDy = 0f;
+        mActivePointerId = motionEvent.getPointerId(0);
+        select(vh, ACTION_STATE_SWIPE);
+        return true;
+    }
+
+    View findChildView(MotionEvent event) {
+        // first check elevated views, if none, then call RV
+        final float x = event.getX();
+        final float y = event.getY();
+        if (mSelected != null) {
+            final View selectedView = mSelected.itemView;
+            if (hitTest(selectedView, x, y, mSelectedStartX + mDx, mSelectedStartY + mDy)) {
+                return selectedView;
+            }
+        }
+        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {
+            final RecoverAnimation anim = mRecoverAnimations.get(i);
+            final View view = anim.mViewHolder.itemView;
+            if (hitTest(view, x, y, anim.mX, anim.mY)) {
+                return view;
+            }
+        }
+        return mRecyclerView.findChildViewUnder(x, y);
+    }
+
+    /**
+     * Starts dragging the provided ViewHolder. By default, ItemTouchHelper starts a drag when a
+     * View is long pressed. You can disable that behavior by overriding
+     * {@link ItemTouchHelper.Callback#isLongPressDragEnabled()}.
+     * <p>
+     * For this method to work:
+     * <ul>
+     * <li>The provided ViewHolder must be a child of the RecyclerView to which this
+     * ItemTouchHelper
+     * is attached.</li>
+     * <li>{@link ItemTouchHelper.Callback} must have dragging enabled.</li>
+     * <li>There must be a previous touch event that was reported to the ItemTouchHelper
+     * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener
+     * grabs previous events, this should work as expected.</li>
+     * </ul>
+     *
+     * For example, if you would like to let your user to be able to drag an Item by touching one
+     * of its descendants, you may implement it as follows:
+     * <pre>
+     *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
+     *         public boolean onTouch(View v, MotionEvent event) {
+     *             if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+     *                 mItemTouchHelper.startDrag(viewHolder);
+     *             }
+     *             return false;
+     *         }
+     *     });
+     * </pre>
+     * <p>
+     *
+     * @param viewHolder The ViewHolder to start dragging. It must be a direct child of
+     *                   RecyclerView.
+     * @see ItemTouchHelper.Callback#isItemViewSwipeEnabled()
+     */
+    public void startDrag(ViewHolder viewHolder) {
+        if (!mCallback.hasDragFlag(mRecyclerView, viewHolder)) {
+            Log.e(TAG, "Start drag has been called but dragging is not enabled");
+            return;
+        }
+        if (viewHolder.itemView.getParent() != mRecyclerView) {
+            Log.e(TAG, "Start drag has been called with a view holder which is not a child of "
+                    + "the RecyclerView which is controlled by this ItemTouchHelper.");
+            return;
+        }
+        obtainVelocityTracker();
+        mDx = mDy = 0f;
+        select(viewHolder, ACTION_STATE_DRAG);
+    }
+
+    /**
+     * Starts swiping the provided ViewHolder. By default, ItemTouchHelper starts swiping a View
+     * when user swipes their finger (or mouse pointer) over the View. You can disable this
+     * behavior
+     * by overriding {@link ItemTouchHelper.Callback}
+     * <p>
+     * For this method to work:
+     * <ul>
+     * <li>The provided ViewHolder must be a child of the RecyclerView to which this
+     * ItemTouchHelper is attached.</li>
+     * <li>{@link ItemTouchHelper.Callback} must have swiping enabled.</li>
+     * <li>There must be a previous touch event that was reported to the ItemTouchHelper
+     * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener
+     * grabs previous events, this should work as expected.</li>
+     * </ul>
+     *
+     * For example, if you would like to let your user to be able to swipe an Item by touching one
+     * of its descendants, you may implement it as follows:
+     * <pre>
+     *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
+     *         public boolean onTouch(View v, MotionEvent event) {
+     *             if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+     *                 mItemTouchHelper.startSwipe(viewHolder);
+     *             }
+     *             return false;
+     *         }
+     *     });
+     * </pre>
+     *
+     * @param viewHolder The ViewHolder to start swiping. It must be a direct child of
+     *                   RecyclerView.
+     */
+    public void startSwipe(ViewHolder viewHolder) {
+        if (!mCallback.hasSwipeFlag(mRecyclerView, viewHolder)) {
+            Log.e(TAG, "Start swipe has been called but swiping is not enabled");
+            return;
+        }
+        if (viewHolder.itemView.getParent() != mRecyclerView) {
+            Log.e(TAG, "Start swipe has been called with a view holder which is not a child of "
+                    + "the RecyclerView controlled by this ItemTouchHelper.");
+            return;
+        }
+        obtainVelocityTracker();
+        mDx = mDy = 0f;
+        select(viewHolder, ACTION_STATE_SWIPE);
+    }
+
+    RecoverAnimation findAnimation(MotionEvent event) {
+        if (mRecoverAnimations.isEmpty()) {
+            return null;
+        }
+        View target = findChildView(event);
+        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {
+            final RecoverAnimation anim = mRecoverAnimations.get(i);
+            if (anim.mViewHolder.itemView == target) {
+                return anim;
+            }
+        }
+        return null;
+    }
+
+    void updateDxDy(MotionEvent ev, int directionFlags, int pointerIndex) {
+        final float x = ev.getX(pointerIndex);
+        final float y = ev.getY(pointerIndex);
+
+        // Calculate the distance moved
+        mDx = x - mInitialTouchX;
+        mDy = y - mInitialTouchY;
+        if ((directionFlags & LEFT) == 0) {
+            mDx = Math.max(0, mDx);
+        }
+        if ((directionFlags & RIGHT) == 0) {
+            mDx = Math.min(0, mDx);
+        }
+        if ((directionFlags & UP) == 0) {
+            mDy = Math.max(0, mDy);
+        }
+        if ((directionFlags & DOWN) == 0) {
+            mDy = Math.min(0, mDy);
+        }
+    }
+
+    private int swipeIfNecessary(ViewHolder viewHolder) {
+        if (mActionState == ACTION_STATE_DRAG) {
+            return 0;
+        }
+        final int originalMovementFlags = mCallback.getMovementFlags(mRecyclerView, viewHolder);
+        final int absoluteMovementFlags = mCallback.convertToAbsoluteDirection(
+                originalMovementFlags,
+                mRecyclerView.getLayoutDirection());
+        final int flags = (absoluteMovementFlags
+                & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);
+        if (flags == 0) {
+            return 0;
+        }
+        final int originalFlags = (originalMovementFlags
+                & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);
+        int swipeDir;
+        if (Math.abs(mDx) > Math.abs(mDy)) {
+            if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {
+                // if swipe dir is not in original flags, it should be the relative direction
+                if ((originalFlags & swipeDir) == 0) {
+                    // convert to relative
+                    return Callback.convertToRelativeDirection(swipeDir,
+                            mRecyclerView.getLayoutDirection());
+                }
+                return swipeDir;
+            }
+            if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {
+                return swipeDir;
+            }
+        } else {
+            if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {
+                return swipeDir;
+            }
+            if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {
+                // if swipe dir is not in original flags, it should be the relative direction
+                if ((originalFlags & swipeDir) == 0) {
+                    // convert to relative
+                    return Callback.convertToRelativeDirection(swipeDir,
+                            mRecyclerView.getLayoutDirection());
+                }
+                return swipeDir;
+            }
+        }
+        return 0;
+    }
+
+    private int checkHorizontalSwipe(ViewHolder viewHolder, int flags) {
+        if ((flags & (LEFT | RIGHT)) != 0) {
+            final int dirFlag = mDx > 0 ? RIGHT : LEFT;
+            if (mVelocityTracker != null && mActivePointerId > -1) {
+                mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
+                        mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
+                final float xVelocity = mVelocityTracker.getXVelocity(mActivePointerId);
+                final float yVelocity = mVelocityTracker.getYVelocity(mActivePointerId);
+                final int velDirFlag = xVelocity > 0f ? RIGHT : LEFT;
+                final float absXVelocity = Math.abs(xVelocity);
+                if ((velDirFlag & flags) != 0 && dirFlag == velDirFlag
+                        && absXVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity)
+                        && absXVelocity > Math.abs(yVelocity)) {
+                    return velDirFlag;
+                }
+            }
+
+            final float threshold = mRecyclerView.getWidth() * mCallback
+                    .getSwipeThreshold(viewHolder);
+
+            if ((flags & dirFlag) != 0 && Math.abs(mDx) > threshold) {
+                return dirFlag;
+            }
+        }
+        return 0;
+    }
+
+    private int checkVerticalSwipe(ViewHolder viewHolder, int flags) {
+        if ((flags & (UP | DOWN)) != 0) {
+            final int dirFlag = mDy > 0 ? DOWN : UP;
+            if (mVelocityTracker != null && mActivePointerId > -1) {
+                mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
+                        mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
+                final float xVelocity = mVelocityTracker.getXVelocity(mActivePointerId);
+                final float yVelocity = mVelocityTracker.getYVelocity(mActivePointerId);
+                final int velDirFlag = yVelocity > 0f ? DOWN : UP;
+                final float absYVelocity = Math.abs(yVelocity);
+                if ((velDirFlag & flags) != 0 && velDirFlag == dirFlag
+                        && absYVelocity >= mCallback.getSwipeEscapeVelocity(mSwipeEscapeVelocity)
+                        && absYVelocity > Math.abs(xVelocity)) {
+                    return velDirFlag;
+                }
+            }
+
+            final float threshold = mRecyclerView.getHeight() * mCallback
+                    .getSwipeThreshold(viewHolder);
+            if ((flags & dirFlag) != 0 && Math.abs(mDy) > threshold) {
+                return dirFlag;
+            }
+        }
+        return 0;
+    }
+
+    private void addChildDrawingOrderCallback() {
+        if (Build.VERSION.SDK_INT >= 21) {
+            return; // we use elevation on Lollipop
+        }
+        if (mChildDrawingOrderCallback == null) {
+            mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {
+                @Override
+                public int onGetChildDrawingOrder(int childCount, int i) {
+                    if (mOverdrawChild == null) {
+                        return i;
+                    }
+                    int childPosition = mOverdrawChildPosition;
+                    if (childPosition == -1) {
+                        childPosition = mRecyclerView.indexOfChild(mOverdrawChild);
+                        mOverdrawChildPosition = childPosition;
+                    }
+                    if (i == childCount - 1) {
+                        return childPosition;
+                    }
+                    return i < childPosition ? i : i + 1;
+                }
+            };
+        }
+        mRecyclerView.setChildDrawingOrderCallback(mChildDrawingOrderCallback);
+    }
+
+    void removeChildDrawingOrderCallbackIfNecessary(View view) {
+        if (view == mOverdrawChild) {
+            mOverdrawChild = null;
+            // only remove if we've added
+            if (mChildDrawingOrderCallback != null) {
+                mRecyclerView.setChildDrawingOrderCallback(null);
+            }
+        }
+    }
+
+    /**
+     * An interface which can be implemented by LayoutManager for better integration with
+     * {@link ItemTouchHelper}.
+     */
+    public interface ViewDropHandler {
+
+        /**
+         * Called by the {@link ItemTouchHelper} after a View is dropped over another View.
+         * <p>
+         * A LayoutManager should implement this interface to get ready for the upcoming move
+         * operation.
+         * <p>
+         * For example, LinearLayoutManager sets up a "scrollToPositionWithOffset" calls so that
+         * the View under drag will be used as an anchor View while calculating the next layout,
+         * making layout stay consistent.
+         *
+         * @param view   The View which is being dragged. It is very likely that user is still
+         *               dragging this View so there might be other
+         *               {@link #prepareForDrop(View, View, int, int)} after this one.
+         * @param target The target view which is being dropped on.
+         * @param x      The <code>left</code> offset of the View that is being dragged. This value
+         *               includes the movement caused by the user.
+         * @param y      The <code>top</code> offset of the View that is being dragged. This value
+         *               includes the movement caused by the user.
+         */
+        void prepareForDrop(View view, View target, int x, int y);
+    }
+
+    /**
+     * This class is the contract between ItemTouchHelper and your application. It lets you control
+     * which touch behaviors are enabled per each ViewHolder and also receive callbacks when user
+     * performs these actions.
+     * <p>
+     * To control which actions user can take on each view, you should override
+     * {@link #getMovementFlags(RecyclerView, ViewHolder)} and return appropriate set
+     * of direction flags. ({@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link #END},
+     * {@link #UP}, {@link #DOWN}). You can use
+     * {@link #makeMovementFlags(int, int)} to easily construct it. Alternatively, you can use
+     * {@link SimpleCallback}.
+     * <p>
+     * If user drags an item, ItemTouchHelper will call
+     * {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)
+     * onMove(recyclerView, dragged, target)}.
+     * Upon receiving this callback, you should move the item from the old position
+     * ({@code dragged.getAdapterPosition()}) to new position ({@code target.getAdapterPosition()})
+     * in your adapter and also call {@link RecyclerView.Adapter#notifyItemMoved(int, int)}.
+     * To control where a View can be dropped, you can override
+     * {@link #canDropOver(RecyclerView, ViewHolder, ViewHolder)}. When a
+     * dragging View overlaps multiple other views, Callback chooses the closest View with which
+     * dragged View might have changed positions. Although this approach works for many use cases,
+     * if you have a custom LayoutManager, you can override
+     * {@link #chooseDropTarget(ViewHolder, java.util.List, int, int)} to select a
+     * custom drop target.
+     * <p>
+     * When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls
+     * {@link #onSwiped(ViewHolder, int)}. At this point, you should update your
+     * adapter (e.g. remove the item) and call related Adapter#notify event.
+     */
+    @SuppressWarnings("UnusedParameters")
+    public abstract static class Callback {
+
+        public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200;
+
+        public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250;
+
+        static final int RELATIVE_DIR_FLAGS = START | END
+                | ((START | END) << DIRECTION_FLAG_COUNT)
+                | ((START | END) << (2 * DIRECTION_FLAG_COUNT));
+
+        private static final ItemTouchUIUtil sUICallback = new ItemTouchUIUtilImpl();
+
+        private static final int ABS_HORIZONTAL_DIR_FLAGS = LEFT | RIGHT
+                | ((LEFT | RIGHT) << DIRECTION_FLAG_COUNT)
+                | ((LEFT | RIGHT) << (2 * DIRECTION_FLAG_COUNT));
+
+        private static final Interpolator sDragScrollInterpolator = new Interpolator() {
+            @Override
+            public float getInterpolation(float t) {
+                return t * t * t * t * t;
+            }
+        };
+
+        private static final Interpolator sDragViewScrollCapInterpolator = new Interpolator() {
+            @Override
+            public float getInterpolation(float t) {
+                t -= 1.0f;
+                return t * t * t * t * t + 1.0f;
+            }
+        };
+
+        /**
+         * Drag scroll speed keeps accelerating until this many milliseconds before being capped.
+         */
+        private static final long DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
+
+        private int mCachedMaxScrollSpeed = -1;
+
+        /**
+         * Returns the {@link ItemTouchUIUtil} that is used by the {@link Callback} class for
+         * visual
+         * changes on Views in response to user interactions. {@link ItemTouchUIUtil} has different
+         * implementations for different platform versions.
+         * <p>
+         * By default, {@link Callback} applies these changes on
+         * {@link RecyclerView.ViewHolder#itemView}.
+         * <p>
+         * For example, if you have a use case where you only want the text to move when user
+         * swipes over the view, you can do the following:
+         * <pre>
+         *     public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){
+         *         getDefaultUIUtil().clearView(((ItemTouchViewHolder) viewHolder).textView);
+         *     }
+         *     public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
+         *         if (viewHolder != null){
+         *             getDefaultUIUtil().onSelected(((ItemTouchViewHolder) viewHolder).textView);
+         *         }
+         *     }
+         *     public void onChildDraw(Canvas c, RecyclerView recyclerView,
+         *             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
+         *             boolean isCurrentlyActive) {
+         *         getDefaultUIUtil().onDraw(c, recyclerView,
+         *                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,
+         *                 actionState, isCurrentlyActive);
+         *         return true;
+         *     }
+         *     public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
+         *             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
+         *             boolean isCurrentlyActive) {
+         *         getDefaultUIUtil().onDrawOver(c, recyclerView,
+         *                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,
+         *                 actionState, isCurrentlyActive);
+         *         return true;
+         *     }
+         * </pre>
+         *
+         * @return The {@link ItemTouchUIUtil} instance that is used by the {@link Callback}
+         */
+        public static ItemTouchUIUtil getDefaultUIUtil() {
+            return sUICallback;
+        }
+
+        /**
+         * Replaces a movement direction with its relative version by taking layout direction into
+         * account.
+         *
+         * @param flags           The flag value that include any number of movement flags.
+         * @param layoutDirection The layout direction of the View. Can be obtained from
+         *                        {@link View#getLayoutDirection()}.
+         * @return Updated flags which uses relative flags ({@link #START}, {@link #END}) instead
+         * of {@link #LEFT}, {@link #RIGHT}.
+         * @see #convertToAbsoluteDirection(int, int)
+         */
+        public static int convertToRelativeDirection(int flags, int layoutDirection) {
+            int masked = flags & ABS_HORIZONTAL_DIR_FLAGS;
+            if (masked == 0) {
+                return flags; // does not have any abs flags, good.
+            }
+            flags &= ~masked; //remove left / right.
+            if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
+                // no change. just OR with 2 bits shifted mask and return
+                flags |= masked << 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.
+                return flags;
+            } else {
+                // add RIGHT flag as START
+                flags |= ((masked << 1) & ~ABS_HORIZONTAL_DIR_FLAGS);
+                // first clean RIGHT bit then add LEFT flag as END
+                flags |= ((masked << 1) & ABS_HORIZONTAL_DIR_FLAGS) << 2;
+            }
+            return flags;
+        }
+
+        /**
+         * Convenience method to create movement flags.
+         * <p>
+         * For instance, if you want to let your items be drag & dropped vertically and swiped
+         * left to be dismissed, you can call this method with:
+         * <code>makeMovementFlags(UP | DOWN, LEFT);</code>
+         *
+         * @param dragFlags  The directions in which the item can be dragged.
+         * @param swipeFlags The directions in which the item can be swiped.
+         * @return Returns an integer composed of the given drag and swipe flags.
+         */
+        public static int makeMovementFlags(int dragFlags, int swipeFlags) {
+            return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags)
+                    | makeFlag(ACTION_STATE_SWIPE, swipeFlags)
+                    | makeFlag(ACTION_STATE_DRAG, dragFlags);
+        }
+
+        /**
+         * Shifts the given direction flags to the offset of the given action state.
+         *
+         * @param actionState The action state you want to get flags in. Should be one of
+         *                    {@link #ACTION_STATE_IDLE}, {@link #ACTION_STATE_SWIPE} or
+         *                    {@link #ACTION_STATE_DRAG}.
+         * @param directions  The direction flags. Can be composed from {@link #UP}, {@link #DOWN},
+         *                    {@link #RIGHT}, {@link #LEFT} {@link #START} and {@link #END}.
+         * @return And integer that represents the given directions in the provided actionState.
+         */
+        public static int makeFlag(int actionState, int directions) {
+            return directions << (actionState * DIRECTION_FLAG_COUNT);
+        }
+
+        /**
+         * Should return a composite flag which defines the enabled move directions in each state
+         * (idle, swiping, dragging).
+         * <p>
+         * Instead of composing this flag manually, you can use {@link #makeMovementFlags(int,
+         * int)}
+         * or {@link #makeFlag(int, int)}.
+         * <p>
+         * This flag is composed of 3 sets of 8 bits, where first 8 bits are for IDLE state, next
+         * 8 bits are for SWIPE state and third 8 bits are for DRAG state.
+         * Each 8 bit sections can be constructed by simply OR'ing direction flags defined in
+         * {@link ItemTouchHelper}.
+         * <p>
+         * For example, if you want it to allow swiping LEFT and RIGHT but only allow starting to
+         * swipe by swiping RIGHT, you can return:
+         * <pre>
+         *      makeFlag(ACTION_STATE_IDLE, RIGHT) | makeFlag(ACTION_STATE_SWIPE, LEFT | RIGHT);
+         * </pre>
+         * This means, allow right movement while IDLE and allow right and left movement while
+         * swiping.
+         *
+         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached.
+         * @param viewHolder   The ViewHolder for which the movement information is necessary.
+         * @return flags specifying which movements are allowed on this ViewHolder.
+         * @see #makeMovementFlags(int, int)
+         * @see #makeFlag(int, int)
+         */
+        public abstract int getMovementFlags(RecyclerView recyclerView,
+                ViewHolder viewHolder);
+
+        /**
+         * Converts a given set of flags to absolution direction which means {@link #START} and
+         * {@link #END} are replaced with {@link #LEFT} and {@link #RIGHT} depending on the layout
+         * direction.
+         *
+         * @param flags           The flag value that include any number of movement flags.
+         * @param layoutDirection The layout direction of the RecyclerView.
+         * @return Updated flags which includes only absolute direction values.
+         */
+        public int convertToAbsoluteDirection(int flags, int layoutDirection) {
+            int masked = flags & RELATIVE_DIR_FLAGS;
+            if (masked == 0) {
+                return flags; // does not have any relative flags, good.
+            }
+            flags &= ~masked; //remove start / end
+            if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
+                // no change. just OR with 2 bits shifted mask and return
+                flags |= masked >> 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.
+                return flags;
+            } else {
+                // add START flag as RIGHT
+                flags |= ((masked >> 1) & ~RELATIVE_DIR_FLAGS);
+                // first clean start bit then add END flag as LEFT
+                flags |= ((masked >> 1) & RELATIVE_DIR_FLAGS) >> 2;
+            }
+            return flags;
+        }
+
+        final int getAbsoluteMovementFlags(RecyclerView recyclerView,
+                ViewHolder viewHolder) {
+            final int flags = getMovementFlags(recyclerView, viewHolder);
+            return convertToAbsoluteDirection(flags, recyclerView.getLayoutDirection());
+        }
+
+        boolean hasDragFlag(RecyclerView recyclerView, ViewHolder viewHolder) {
+            final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);
+            return (flags & ACTION_MODE_DRAG_MASK) != 0;
+        }
+
+        boolean hasSwipeFlag(RecyclerView recyclerView,
+                ViewHolder viewHolder) {
+            final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);
+            return (flags & ACTION_MODE_SWIPE_MASK) != 0;
+        }
+
+        /**
+         * Return true if the current ViewHolder can be dropped over the the target ViewHolder.
+         * <p>
+         * This method is used when selecting drop target for the dragged View. After Views are
+         * eliminated either via bounds check or via this method, resulting set of views will be
+         * passed to {@link #chooseDropTarget(ViewHolder, java.util.List, int, int)}.
+         * <p>
+         * Default implementation returns true.
+         *
+         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to.
+         * @param current      The ViewHolder that user is dragging.
+         * @param target       The ViewHolder which is below the dragged ViewHolder.
+         * @return True if the dragged ViewHolder can be replaced with the target ViewHolder, false
+         * otherwise.
+         */
+        public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
+                ViewHolder target) {
+            return true;
+        }
+
+        /**
+         * Called when ItemTouchHelper wants to move the dragged item from its old position to
+         * the new position.
+         * <p>
+         * If this method returns true, ItemTouchHelper assumes {@code viewHolder} has been moved
+         * to the adapter position of {@code target} ViewHolder
+         * ({@link ViewHolder#getAdapterPosition()
+         * ViewHolder#getAdapterPosition()}).
+         * <p>
+         * If you don't support drag & drop, this method will never be called.
+         *
+         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to.
+         * @param viewHolder   The ViewHolder which is being dragged by the user.
+         * @param target       The ViewHolder over which the currently active item is being
+         *                     dragged.
+         * @return True if the {@code viewHolder} has been moved to the adapter position of
+         * {@code target}.
+         * @see #onMoved(RecyclerView, ViewHolder, int, ViewHolder, int, int, int)
+         */
+        public abstract boolean onMove(RecyclerView recyclerView,
+                ViewHolder viewHolder, ViewHolder target);
+
+        /**
+         * Returns whether ItemTouchHelper should start a drag and drop operation if an item is
+         * long pressed.
+         * <p>
+         * Default value returns true but you may want to disable this if you want to start
+         * dragging on a custom view touch using {@link #startDrag(ViewHolder)}.
+         *
+         * @return True if ItemTouchHelper should start dragging an item when it is long pressed,
+         * false otherwise. Default value is <code>true</code>.
+         * @see #startDrag(ViewHolder)
+         */
+        public boolean isLongPressDragEnabled() {
+            return true;
+        }
+
+        /**
+         * Returns whether ItemTouchHelper should start a swipe operation if a pointer is swiped
+         * over the View.
+         * <p>
+         * Default value returns true but you may want to disable this if you want to start
+         * swiping on a custom view touch using {@link #startSwipe(ViewHolder)}.
+         *
+         * @return True if ItemTouchHelper should start swiping an item when user swipes a pointer
+         * over the View, false otherwise. Default value is <code>true</code>.
+         * @see #startSwipe(ViewHolder)
+         */
+        public boolean isItemViewSwipeEnabled() {
+            return true;
+        }
+
+        /**
+         * When finding views under a dragged view, by default, ItemTouchHelper searches for views
+         * that overlap with the dragged View. By overriding this method, you can extend or shrink
+         * the search box.
+         *
+         * @return The extra margin to be added to the hit box of the dragged View.
+         */
+        public int getBoundingBoxMargin() {
+            return 0;
+        }
+
+        /**
+         * Returns the fraction that the user should move the View to be considered as swiped.
+         * The fraction is calculated with respect to RecyclerView's bounds.
+         * <p>
+         * Default value is .5f, which means, to swipe a View, user must move the View at least
+         * half of RecyclerView's width or height, depending on the swipe direction.
+         *
+         * @param viewHolder The ViewHolder that is being dragged.
+         * @return A float value that denotes the fraction of the View size. Default value
+         * is .5f .
+         */
+        public float getSwipeThreshold(ViewHolder viewHolder) {
+            return .5f;
+        }
+
+        /**
+         * Returns the fraction that the user should move the View to be considered as it is
+         * dragged. After a view is moved this amount, ItemTouchHelper starts checking for Views
+         * below it for a possible drop.
+         *
+         * @param viewHolder The ViewHolder that is being dragged.
+         * @return A float value that denotes the fraction of the View size. Default value is
+         * .5f .
+         */
+        public float getMoveThreshold(ViewHolder viewHolder) {
+            return .5f;
+        }
+
+        /**
+         * Defines the minimum velocity which will be considered as a swipe action by the user.
+         * <p>
+         * You can increase this value to make it harder to swipe or decrease it to make it easier.
+         * Keep in mind that ItemTouchHelper also checks the perpendicular velocity and makes sure
+         * current direction velocity is larger then the perpendicular one. Otherwise, user's
+         * movement is ambiguous. You can change the threshold by overriding
+         * {@link #getSwipeVelocityThreshold(float)}.
+         * <p>
+         * The velocity is calculated in pixels per second.
+         * <p>
+         * The default framework value is passed as a parameter so that you can modify it with a
+         * multiplier.
+         *
+         * @param defaultValue The default value (in pixels per second) used by the
+         *                     ItemTouchHelper.
+         * @return The minimum swipe velocity. The default implementation returns the
+         * <code>defaultValue</code> parameter.
+         * @see #getSwipeVelocityThreshold(float)
+         * @see #getSwipeThreshold(ViewHolder)
+         */
+        public float getSwipeEscapeVelocity(float defaultValue) {
+            return defaultValue;
+        }
+
+        /**
+         * Defines the maximum velocity ItemTouchHelper will ever calculate for pointer movements.
+         * <p>
+         * To consider a movement as swipe, ItemTouchHelper requires it to be larger than the
+         * perpendicular movement. If both directions reach to the max threshold, none of them will
+         * be considered as a swipe because it is usually an indication that user rather tried to
+         * scroll then swipe.
+         * <p>
+         * The velocity is calculated in pixels per second.
+         * <p>
+         * You can customize this behavior by changing this method. If you increase the value, it
+         * will be easier for the user to swipe diagonally and if you decrease the value, user will
+         * need to make a rather straight finger movement to trigger a swipe.
+         *
+         * @param defaultValue The default value(in pixels per second) used by the ItemTouchHelper.
+         * @return The velocity cap for pointer movements. The default implementation returns the
+         * <code>defaultValue</code> parameter.
+         * @see #getSwipeEscapeVelocity(float)
+         */
+        public float getSwipeVelocityThreshold(float defaultValue) {
+            return defaultValue;
+        }
+
+        /**
+         * Called by ItemTouchHelper to select a drop target from the list of ViewHolders that
+         * are under the dragged View.
+         * <p>
+         * Default implementation filters the View with which dragged item have changed position
+         * in the drag direction. For instance, if the view is dragged UP, it compares the
+         * <code>view.getTop()</code> of the two views before and after drag started. If that value
+         * is different, the target view passes the filter.
+         * <p>
+         * Among these Views which pass the test, the one closest to the dragged view is chosen.
+         * <p>
+         * This method is called on the main thread every time user moves the View. If you want to
+         * override it, make sure it does not do any expensive operations.
+         *
+         * @param selected    The ViewHolder being dragged by the user.
+         * @param dropTargets The list of ViewHolder that are under the dragged View and
+         *                    candidate as a drop.
+         * @param curX        The updated left value of the dragged View after drag translations
+         *                    are applied. This value does not include margins added by
+         *                    {@link RecyclerView.ItemDecoration}s.
+         * @param curY        The updated top value of the dragged View after drag translations
+         *                    are applied. This value does not include margins added by
+         *                    {@link RecyclerView.ItemDecoration}s.
+         * @return A ViewHolder to whose position the dragged ViewHolder should be
+         * moved to.
+         */
+        public ViewHolder chooseDropTarget(ViewHolder selected,
+                List<ViewHolder> dropTargets, int curX, int curY) {
+            int right = curX + selected.itemView.getWidth();
+            int bottom = curY + selected.itemView.getHeight();
+            ViewHolder winner = null;
+            int winnerScore = -1;
+            final int dx = curX - selected.itemView.getLeft();
+            final int dy = curY - selected.itemView.getTop();
+            final int targetsSize = dropTargets.size();
+            for (int i = 0; i < targetsSize; i++) {
+                final ViewHolder target = dropTargets.get(i);
+                if (dx > 0) {
+                    int diff = target.itemView.getRight() - right;
+                    if (diff < 0 && target.itemView.getRight() > selected.itemView.getRight()) {
+                        final int score = Math.abs(diff);
+                        if (score > winnerScore) {
+                            winnerScore = score;
+                            winner = target;
+                        }
+                    }
+                }
+                if (dx < 0) {
+                    int diff = target.itemView.getLeft() - curX;
+                    if (diff > 0 && target.itemView.getLeft() < selected.itemView.getLeft()) {
+                        final int score = Math.abs(diff);
+                        if (score > winnerScore) {
+                            winnerScore = score;
+                            winner = target;
+                        }
+                    }
+                }
+                if (dy < 0) {
+                    int diff = target.itemView.getTop() - curY;
+                    if (diff > 0 && target.itemView.getTop() < selected.itemView.getTop()) {
+                        final int score = Math.abs(diff);
+                        if (score > winnerScore) {
+                            winnerScore = score;
+                            winner = target;
+                        }
+                    }
+                }
+
+                if (dy > 0) {
+                    int diff = target.itemView.getBottom() - bottom;
+                    if (diff < 0 && target.itemView.getBottom() > selected.itemView.getBottom()) {
+                        final int score = Math.abs(diff);
+                        if (score > winnerScore) {
+                            winnerScore = score;
+                            winner = target;
+                        }
+                    }
+                }
+            }
+            return winner;
+        }
+
+        /**
+         * Called when a ViewHolder is swiped by the user.
+         * <p>
+         * If you are returning relative directions ({@link #START} , {@link #END}) from the
+         * {@link #getMovementFlags(RecyclerView, ViewHolder)} method, this method
+         * will also use relative directions. Otherwise, it will use absolute directions.
+         * <p>
+         * If you don't support swiping, this method will never be called.
+         * <p>
+         * ItemTouchHelper will keep a reference to the View until it is detached from
+         * RecyclerView.
+         * As soon as it is detached, ItemTouchHelper will call
+         * {@link #clearView(RecyclerView, ViewHolder)}.
+         *
+         * @param viewHolder The ViewHolder which has been swiped by the user.
+         * @param direction  The direction to which the ViewHolder is swiped. It is one of
+         *                   {@link #UP}, {@link #DOWN},
+         *                   {@link #LEFT} or {@link #RIGHT}. If your
+         *                   {@link #getMovementFlags(RecyclerView, ViewHolder)}
+         *                   method
+         *                   returned relative flags instead of {@link #LEFT} / {@link #RIGHT};
+         *                   `direction` will be relative as well. ({@link #START} or {@link
+         *                   #END}).
+         */
+        public abstract void onSwiped(ViewHolder viewHolder, int direction);
+
+        /**
+         * Called when the ViewHolder swiped or dragged by the ItemTouchHelper is changed.
+         * <p/>
+         * If you override this method, you should call super.
+         *
+         * @param viewHolder  The new ViewHolder that is being swiped or dragged. Might be null if
+         *                    it is cleared.
+         * @param actionState One of {@link ItemTouchHelper#ACTION_STATE_IDLE},
+         *                    {@link ItemTouchHelper#ACTION_STATE_SWIPE} or
+         *                    {@link ItemTouchHelper#ACTION_STATE_DRAG}.
+         * @see #clearView(RecyclerView, RecyclerView.ViewHolder)
+         */
+        public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
+            if (viewHolder != null) {
+                sUICallback.onSelected(viewHolder.itemView);
+            }
+        }
+
+        private int getMaxDragScroll(RecyclerView recyclerView) {
+            if (mCachedMaxScrollSpeed == -1) {
+                mCachedMaxScrollSpeed = recyclerView.getResources().getDimensionPixelSize(
+                        R.dimen.item_touch_helper_max_drag_scroll_per_frame);
+            }
+            return mCachedMaxScrollSpeed;
+        }
+
+        /**
+         * Called when {@link #onMove(RecyclerView, ViewHolder, ViewHolder)} returns true.
+         * <p>
+         * ItemTouchHelper does not create an extra Bitmap or View while dragging, instead, it
+         * modifies the existing View. Because of this reason, it is important that the View is
+         * still part of the layout after it is moved. This may not work as intended when swapped
+         * Views are close to RecyclerView bounds or there are gaps between them (e.g. other Views
+         * which were not eligible for dropping over).
+         * <p>
+         * This method is responsible to give necessary hint to the LayoutManager so that it will
+         * keep the View in visible area. For example, for LinearLayoutManager, this is as simple
+         * as calling {@link LinearLayoutManager#scrollToPositionWithOffset(int, int)}.
+         *
+         * Default implementation calls {@link RecyclerView#scrollToPosition(int)} if the View's
+         * new position is likely to be out of bounds.
+         * <p>
+         * It is important to ensure the ViewHolder will stay visible as otherwise, it might be
+         * removed by the LayoutManager if the move causes the View to go out of bounds. In that
+         * case, drag will end prematurely.
+         *
+         * @param recyclerView The RecyclerView controlled by the ItemTouchHelper.
+         * @param viewHolder   The ViewHolder under user's control.
+         * @param fromPos      The previous adapter position of the dragged item (before it was
+         *                     moved).
+         * @param target       The ViewHolder on which the currently active item has been dropped.
+         * @param toPos        The new adapter position of the dragged item.
+         * @param x            The updated left value of the dragged View after drag translations
+         *                     are applied. This value does not include margins added by
+         *                     {@link RecyclerView.ItemDecoration}s.
+         * @param y            The updated top value of the dragged View after drag translations
+         *                     are applied. This value does not include margins added by
+         *                     {@link RecyclerView.ItemDecoration}s.
+         */
+        public void onMoved(final RecyclerView recyclerView,
+                final ViewHolder viewHolder, int fromPos, final ViewHolder target, int toPos, int x,
+                int y) {
+            final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+            if (layoutManager instanceof ViewDropHandler) {
+                ((ViewDropHandler) layoutManager).prepareForDrop(viewHolder.itemView,
+                        target.itemView, x, y);
+                return;
+            }
+
+            // if layout manager cannot handle it, do some guesswork
+            if (layoutManager.canScrollHorizontally()) {
+                final int minLeft = layoutManager.getDecoratedLeft(target.itemView);
+                if (minLeft <= recyclerView.getPaddingLeft()) {
+                    recyclerView.scrollToPosition(toPos);
+                }
+                final int maxRight = layoutManager.getDecoratedRight(target.itemView);
+                if (maxRight >= recyclerView.getWidth() - recyclerView.getPaddingRight()) {
+                    recyclerView.scrollToPosition(toPos);
+                }
+            }
+
+            if (layoutManager.canScrollVertically()) {
+                final int minTop = layoutManager.getDecoratedTop(target.itemView);
+                if (minTop <= recyclerView.getPaddingTop()) {
+                    recyclerView.scrollToPosition(toPos);
+                }
+                final int maxBottom = layoutManager.getDecoratedBottom(target.itemView);
+                if (maxBottom >= recyclerView.getHeight() - recyclerView.getPaddingBottom()) {
+                    recyclerView.scrollToPosition(toPos);
+                }
+            }
+        }
+
+        void onDraw(Canvas c, RecyclerView parent, ViewHolder selected,
+                List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,
+                int actionState, float dX, float dY) {
+            final int recoverAnimSize = recoverAnimationList.size();
+            for (int i = 0; i < recoverAnimSize; i++) {
+                final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);
+                anim.update();
+                final int count = c.save();
+                onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,
+                        false);
+                c.restoreToCount(count);
+            }
+            if (selected != null) {
+                final int count = c.save();
+                onChildDraw(c, parent, selected, dX, dY, actionState, true);
+                c.restoreToCount(count);
+            }
+        }
+
+        void onDrawOver(Canvas c, RecyclerView parent, ViewHolder selected,
+                List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,
+                int actionState, float dX, float dY) {
+            final int recoverAnimSize = recoverAnimationList.size();
+            for (int i = 0; i < recoverAnimSize; i++) {
+                final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);
+                final int count = c.save();
+                onChildDrawOver(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,
+                        false);
+                c.restoreToCount(count);
+            }
+            if (selected != null) {
+                final int count = c.save();
+                onChildDrawOver(c, parent, selected, dX, dY, actionState, true);
+                c.restoreToCount(count);
+            }
+            boolean hasRunningAnimation = false;
+            for (int i = recoverAnimSize - 1; i >= 0; i--) {
+                final RecoverAnimation anim = recoverAnimationList.get(i);
+                if (anim.mEnded && !anim.mIsPendingCleanup) {
+                    recoverAnimationList.remove(i);
+                } else if (!anim.mEnded) {
+                    hasRunningAnimation = true;
+                }
+            }
+            if (hasRunningAnimation) {
+                parent.invalidate();
+            }
+        }
+
+        /**
+         * Called by the ItemTouchHelper when the user interaction with an element is over and it
+         * also completed its animation.
+         * <p>
+         * This is a good place to clear all changes on the View that was done in
+         * {@link #onSelectedChanged(RecyclerView.ViewHolder, int)},
+         * {@link #onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int,
+         * boolean)} or
+         * {@link #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean)}.
+         *
+         * @param recyclerView The RecyclerView which is controlled by the ItemTouchHelper.
+         * @param viewHolder   The View that was interacted by the user.
+         */
+        public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
+            sUICallback.clearView(viewHolder.itemView);
+        }
+
+        /**
+         * Called by ItemTouchHelper on RecyclerView's onDraw callback.
+         * <p>
+         * If you would like to customize how your View's respond to user interactions, this is
+         * a good place to override.
+         * <p>
+         * Default implementation translates the child by the given <code>dX</code>,
+         * <code>dY</code>.
+         * ItemTouchHelper also takes care of drawing the child after other children if it is being
+         * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this
+         * is
+         * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L
+         * and after, it changes View's elevation value to be greater than all other children.)
+         *
+         * @param c                 The canvas which RecyclerView is drawing its children
+         * @param recyclerView      The RecyclerView to which ItemTouchHelper is attached to
+         * @param viewHolder        The ViewHolder which is being interacted by the User or it was
+         *                          interacted and simply animating to its original position
+         * @param dX                The amount of horizontal displacement caused by user's action
+         * @param dY                The amount of vertical displacement caused by user's action
+         * @param actionState       The type of interaction on the View. Is either {@link
+         *                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.
+         * @param isCurrentlyActive True if this view is currently being controlled by the user or
+         *                          false it is simply animating back to its original state.
+         * @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
+         * boolean)
+         */
+        public void onChildDraw(Canvas c, RecyclerView recyclerView,
+                ViewHolder viewHolder,
+                float dX, float dY, int actionState, boolean isCurrentlyActive) {
+            sUICallback.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState,
+                    isCurrentlyActive);
+        }
+
+        /**
+         * Called by ItemTouchHelper on RecyclerView's onDraw callback.
+         * <p>
+         * If you would like to customize how your View's respond to user interactions, this is
+         * a good place to override.
+         * <p>
+         * Default implementation translates the child by the given <code>dX</code>,
+         * <code>dY</code>.
+         * ItemTouchHelper also takes care of drawing the child after other children if it is being
+         * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this
+         * is
+         * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L
+         * and after, it changes View's elevation value to be greater than all other children.)
+         *
+         * @param c                 The canvas which RecyclerView is drawing its children
+         * @param recyclerView      The RecyclerView to which ItemTouchHelper is attached to
+         * @param viewHolder        The ViewHolder which is being interacted by the User or it was
+         *                          interacted and simply animating to its original position
+         * @param dX                The amount of horizontal displacement caused by user's action
+         * @param dY                The amount of vertical displacement caused by user's action
+         * @param actionState       The type of interaction on the View. Is either {@link
+         *                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.
+         * @param isCurrentlyActive True if this view is currently being controlled by the user or
+         *                          false it is simply animating back to its original state.
+         * @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
+         * boolean)
+         */
+        public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
+                ViewHolder viewHolder,
+                float dX, float dY, int actionState, boolean isCurrentlyActive) {
+            sUICallback.onDrawOver(c, recyclerView, viewHolder.itemView, dX, dY, actionState,
+                    isCurrentlyActive);
+        }
+
+        /**
+         * Called by the ItemTouchHelper when user action finished on a ViewHolder and now the View
+         * will be animated to its final position.
+         * <p>
+         * Default implementation uses ItemAnimator's duration values. If
+         * <code>animationType</code> is {@link #ANIMATION_TYPE_DRAG}, it returns
+         * {@link RecyclerView.ItemAnimator#getMoveDuration()}, otherwise, it returns
+         * {@link RecyclerView.ItemAnimator#getRemoveDuration()}. If RecyclerView does not have
+         * any {@link RecyclerView.ItemAnimator} attached, this method returns
+         * {@code DEFAULT_DRAG_ANIMATION_DURATION} or {@code DEFAULT_SWIPE_ANIMATION_DURATION}
+         * depending on the animation type.
+         *
+         * @param recyclerView  The RecyclerView to which the ItemTouchHelper is attached to.
+         * @param animationType The type of animation. Is one of {@link #ANIMATION_TYPE_DRAG},
+         *                      {@link #ANIMATION_TYPE_SWIPE_CANCEL} or
+         *                      {@link #ANIMATION_TYPE_SWIPE_SUCCESS}.
+         * @param animateDx     The horizontal distance that the animation will offset
+         * @param animateDy     The vertical distance that the animation will offset
+         * @return The duration for the animation
+         */
+        public long getAnimationDuration(RecyclerView recyclerView, int animationType,
+                float animateDx, float animateDy) {
+            final RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator();
+            if (itemAnimator == null) {
+                return animationType == ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION
+                        : DEFAULT_SWIPE_ANIMATION_DURATION;
+            } else {
+                return animationType == ANIMATION_TYPE_DRAG ? itemAnimator.getMoveDuration()
+                        : itemAnimator.getRemoveDuration();
+            }
+        }
+
+        /**
+         * Called by the ItemTouchHelper when user is dragging a view out of bounds.
+         * <p>
+         * You can override this method to decide how much RecyclerView should scroll in response
+         * to this action. Default implementation calculates a value based on the amount of View
+         * out of bounds and the time it spent there. The longer user keeps the View out of bounds,
+         * the faster the list will scroll. Similarly, the larger portion of the View is out of
+         * bounds, the faster the RecyclerView will scroll.
+         *
+         * @param recyclerView        The RecyclerView instance to which ItemTouchHelper is
+         *                            attached to.
+         * @param viewSize            The total size of the View in scroll direction, excluding
+         *                            item decorations.
+         * @param viewSizeOutOfBounds The total size of the View that is out of bounds. This value
+         *                            is negative if the View is dragged towards left or top edge.
+         * @param totalSize           The total size of RecyclerView in the scroll direction.
+         * @param msSinceStartScroll  The time passed since View is kept out of bounds.
+         * @return The amount that RecyclerView should scroll. Keep in mind that this value will
+         * be passed to {@link RecyclerView#scrollBy(int, int)} method.
+         */
+        public int interpolateOutOfBoundsScroll(RecyclerView recyclerView,
+                int viewSize, int viewSizeOutOfBounds,
+                int totalSize, long msSinceStartScroll) {
+            final int maxScroll = getMaxDragScroll(recyclerView);
+            final int absOutOfBounds = Math.abs(viewSizeOutOfBounds);
+            final int direction = (int) Math.signum(viewSizeOutOfBounds);
+            // might be negative if other direction
+            float outOfBoundsRatio = Math.min(1f, 1f * absOutOfBounds / viewSize);
+            final int cappedScroll = (int) (direction * maxScroll
+                    * sDragViewScrollCapInterpolator.getInterpolation(outOfBoundsRatio));
+            final float timeRatio;
+            if (msSinceStartScroll > DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS) {
+                timeRatio = 1f;
+            } else {
+                timeRatio = (float) msSinceStartScroll / DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS;
+            }
+            final int value = (int) (cappedScroll * sDragScrollInterpolator
+                    .getInterpolation(timeRatio));
+            if (value == 0) {
+                return viewSizeOutOfBounds > 0 ? 1 : -1;
+            }
+            return value;
+        }
+    }
+
+    /**
+     * A simple wrapper to the default Callback which you can construct with drag and swipe
+     * directions and this class will handle the flag callbacks. You should still override onMove
+     * or
+     * onSwiped depending on your use case.
+     *
+     * <pre>
+     * ItemTouchHelper mIth = new ItemTouchHelper(
+     *     new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
+     *         ItemTouchHelper.LEFT) {
+     *         public abstract boolean onMove(RecyclerView recyclerView,
+     *             ViewHolder viewHolder, ViewHolder target) {
+     *             final int fromPos = viewHolder.getAdapterPosition();
+     *             final int toPos = target.getAdapterPosition();
+     *             // move item in `fromPos` to `toPos` in adapter.
+     *             return true;// true if moved, false otherwise
+     *         }
+     *         public void onSwiped(ViewHolder viewHolder, int direction) {
+     *             // remove from adapter
+     *         }
+     * });
+     * </pre>
+     */
+    public abstract static class SimpleCallback extends Callback {
+
+        private int mDefaultSwipeDirs;
+
+        private int mDefaultDragDirs;
+
+        /**
+         * Creates a Callback for the given drag and swipe allowance. These values serve as
+         * defaults
+         * and if you want to customize behavior per ViewHolder, you can override
+         * {@link #getSwipeDirs(RecyclerView, ViewHolder)}
+         * and / or {@link #getDragDirs(RecyclerView, ViewHolder)}.
+         *
+         * @param dragDirs  Binary OR of direction flags in which the Views can be dragged. Must be
+         *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link
+         *                  #END},
+         *                  {@link #UP} and {@link #DOWN}.
+         * @param swipeDirs Binary OR of direction flags in which the Views can be swiped. Must be
+         *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link
+         *                  #END},
+         *                  {@link #UP} and {@link #DOWN}.
+         */
+        public SimpleCallback(int dragDirs, int swipeDirs) {
+            mDefaultSwipeDirs = swipeDirs;
+            mDefaultDragDirs = dragDirs;
+        }
+
+        /**
+         * Updates the default swipe directions. For example, you can use this method to toggle
+         * certain directions depending on your use case.
+         *
+         * @param defaultSwipeDirs Binary OR of directions in which the ViewHolders can be swiped.
+         */
+        public void setDefaultSwipeDirs(int defaultSwipeDirs) {
+            mDefaultSwipeDirs = defaultSwipeDirs;
+        }
+
+        /**
+         * Updates the default drag directions. For example, you can use this method to toggle
+         * certain directions depending on your use case.
+         *
+         * @param defaultDragDirs Binary OR of directions in which the ViewHolders can be dragged.
+         */
+        public void setDefaultDragDirs(int defaultDragDirs) {
+            mDefaultDragDirs = defaultDragDirs;
+        }
+
+        /**
+         * Returns the swipe directions for the provided ViewHolder.
+         * Default implementation returns the swipe directions that was set via constructor or
+         * {@link #setDefaultSwipeDirs(int)}.
+         *
+         * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to.
+         * @param viewHolder   The RecyclerView for which the swipe direction is queried.
+         * @return A binary OR of direction flags.
+         */
+        public int getSwipeDirs(RecyclerView recyclerView, ViewHolder viewHolder) {
+            return mDefaultSwipeDirs;
+        }
+
+        /**
+         * Returns the drag directions for the provided ViewHolder.
+         * Default implementation returns the drag directions that was set via constructor or
+         * {@link #setDefaultDragDirs(int)}.
+         *
+         * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to.
+         * @param viewHolder   The RecyclerView for which the swipe direction is queried.
+         * @return A binary OR of direction flags.
+         */
+        public int getDragDirs(RecyclerView recyclerView, ViewHolder viewHolder) {
+            return mDefaultDragDirs;
+        }
+
+        @Override
+        public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
+            return makeMovementFlags(getDragDirs(recyclerView, viewHolder),
+                    getSwipeDirs(recyclerView, viewHolder));
+        }
+    }
+
+    private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
+
+        ItemTouchHelperGestureListener() {
+        }
+
+        @Override
+        public boolean onDown(MotionEvent e) {
+            return true;
+        }
+
+        @Override
+        public void onLongPress(MotionEvent e) {
+            View child = findChildView(e);
+            if (child != null) {
+                ViewHolder vh = mRecyclerView.getChildViewHolder(child);
+                if (vh != null) {
+                    if (!mCallback.hasDragFlag(mRecyclerView, vh)) {
+                        return;
+                    }
+                    int pointerId = e.getPointerId(0);
+                    // Long press is deferred.
+                    // Check w/ active pointer id to avoid selecting after motion
+                    // event is canceled.
+                    if (pointerId == mActivePointerId) {
+                        final int index = e.findPointerIndex(mActivePointerId);
+                        final float x = e.getX(index);
+                        final float y = e.getY(index);
+                        mInitialTouchX = x;
+                        mInitialTouchY = y;
+                        mDx = mDy = 0f;
+                        if (DEBUG) {
+                            Log.d(TAG,
+                                    "onlong press: x:" + mInitialTouchX + ",y:" + mInitialTouchY);
+                        }
+                        if (mCallback.isLongPressDragEnabled()) {
+                            select(vh, ACTION_STATE_DRAG);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private class RecoverAnimation implements Animator.AnimatorListener {
+
+        final float mStartDx;
+
+        final float mStartDy;
+
+        final float mTargetX;
+
+        final float mTargetY;
+
+        final ViewHolder mViewHolder;
+
+        final int mActionState;
+
+        private final ValueAnimator mValueAnimator;
+
+        final int mAnimationType;
+
+        public boolean mIsPendingCleanup;
+
+        float mX;
+
+        float mY;
+
+        // if user starts touching a recovering view, we put it into interaction mode again,
+        // instantly.
+        boolean mOverridden = false;
+
+        boolean mEnded = false;
+
+        private float mFraction;
+
+        RecoverAnimation(ViewHolder viewHolder, int animationType,
+                int actionState, float startDx, float startDy, float targetX, float targetY) {
+            mActionState = actionState;
+            mAnimationType = animationType;
+            mViewHolder = viewHolder;
+            mStartDx = startDx;
+            mStartDy = startDy;
+            mTargetX = targetX;
+            mTargetY = targetY;
+            mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
+            mValueAnimator.addUpdateListener(
+                    new ValueAnimator.AnimatorUpdateListener() {
+                        @Override
+                        public void onAnimationUpdate(ValueAnimator animation) {
+                            setFraction(animation.getAnimatedFraction());
+                        }
+                    });
+            mValueAnimator.setTarget(viewHolder.itemView);
+            mValueAnimator.addListener(this);
+            setFraction(0f);
+        }
+
+        public void setDuration(long duration) {
+            mValueAnimator.setDuration(duration);
+        }
+
+        public void start() {
+            mViewHolder.setIsRecyclable(false);
+            mValueAnimator.start();
+        }
+
+        public void cancel() {
+            mValueAnimator.cancel();
+        }
+
+        public void setFraction(float fraction) {
+            mFraction = fraction;
+        }
+
+        /**
+         * We run updates on onDraw method but use the fraction from animator callback.
+         * This way, we can sync translate x/y values w/ the animators to avoid one-off frames.
+         */
+        public void update() {
+            if (mStartDx == mTargetX) {
+                mX = mViewHolder.itemView.getTranslationX();
+            } else {
+                mX = mStartDx + mFraction * (mTargetX - mStartDx);
+            }
+            if (mStartDy == mTargetY) {
+                mY = mViewHolder.itemView.getTranslationY();
+            } else {
+                mY = mStartDy + mFraction * (mTargetY - mStartDy);
+            }
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (!mEnded) {
+                mViewHolder.setIsRecyclable(true);
+            }
+            mEnded = true;
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            setFraction(1f); //make sure we recover the view's state.
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/helper/ItemTouchUIUtil.java b/core/java/com/android/internal/widget/helper/ItemTouchUIUtil.java
new file mode 100644
index 0000000..e368a6d
--- /dev/null
+++ b/core/java/com/android/internal/widget/helper/ItemTouchUIUtil.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.helper;
+
+import android.graphics.Canvas;
+import android.view.View;
+
+import com.android.internal.widget.RecyclerView;
+
+/**
+ * Utility class for {@link ItemTouchHelper} which handles item transformations for different
+ * API versions.
+ * <p/>
+ * This class has methods that map to {@link ItemTouchHelper.Callback}'s drawing methods. Default
+ * implementations in {@link ItemTouchHelper.Callback} call these methods with
+ * {@link RecyclerView.ViewHolder#itemView} and {@link ItemTouchUIUtil} makes necessary changes
+ * on the View depending on the API level. You can access the instance of {@link ItemTouchUIUtil}
+ * via {@link ItemTouchHelper.Callback#getDefaultUIUtil()} and call its methods with the children
+ * of ViewHolder that you want to apply default effects.
+ *
+ * @see ItemTouchHelper.Callback#getDefaultUIUtil()
+ */
+public interface ItemTouchUIUtil {
+
+    /**
+     * The default implementation for {@link ItemTouchHelper.Callback#onChildDraw(Canvas,
+     * RecyclerView, RecyclerView.ViewHolder, float, float, int, boolean)}
+     */
+    void onDraw(Canvas c, RecyclerView recyclerView, View view,
+            float dX, float dY, int actionState, boolean isCurrentlyActive);
+
+    /**
+     * The default implementation for {@link ItemTouchHelper.Callback#onChildDrawOver(Canvas,
+     * RecyclerView, RecyclerView.ViewHolder, float, float, int, boolean)}
+     */
+    void onDrawOver(Canvas c, RecyclerView recyclerView, View view,
+            float dX, float dY, int actionState, boolean isCurrentlyActive);
+
+    /**
+     * The default implementation for {@link ItemTouchHelper.Callback#clearView(RecyclerView,
+     * RecyclerView.ViewHolder)}
+     */
+    void clearView(View view);
+
+    /**
+     * The default implementation for {@link ItemTouchHelper.Callback#onSelectedChanged(
+     * RecyclerView.ViewHolder, int)}
+     */
+    void onSelected(View view);
+}
+
diff --git a/core/java/com/android/internal/widget/helper/ItemTouchUIUtilImpl.java b/core/java/com/android/internal/widget/helper/ItemTouchUIUtilImpl.java
new file mode 100644
index 0000000..0de240b
--- /dev/null
+++ b/core/java/com/android/internal/widget/helper/ItemTouchUIUtilImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget.helper;
+
+import android.graphics.Canvas;
+import android.view.View;
+
+import com.android.internal.R;
+import com.android.internal.widget.RecyclerView;
+
+/**
+ * Package private class to keep implementations. Putting them inside ItemTouchUIUtil makes them
+ * public API, which is not desired in this case.
+ */
+class ItemTouchUIUtilImpl implements ItemTouchUIUtil {
+    @Override
+    public void onDraw(Canvas c, RecyclerView recyclerView, View view,
+            float dX, float dY, int actionState, boolean isCurrentlyActive) {
+        if (isCurrentlyActive) {
+            Object originalElevation = view.getTag(
+                    R.id.item_touch_helper_previous_elevation);
+            if (originalElevation == null) {
+                originalElevation = view.getElevation();
+                float newElevation = 1f + findMaxElevation(recyclerView, view);
+                view.setElevation(newElevation);
+                view.setTag(R.id.item_touch_helper_previous_elevation,
+                        originalElevation);
+            }
+        }
+        view.setTranslationX(dX);
+        view.setTranslationY(dY);
+    }
+
+    private float findMaxElevation(RecyclerView recyclerView, View itemView) {
+        final int childCount = recyclerView.getChildCount();
+        float max = 0;
+        for (int i = 0; i < childCount; i++) {
+            final View child = recyclerView.getChildAt(i);
+            if (child == itemView) {
+                continue;
+            }
+            final float elevation = child.getElevation();
+            if (elevation > max) {
+                max = elevation;
+            }
+        }
+        return max;
+    }
+
+    @Override
+    public void clearView(View view) {
+        final Object tag = view.getTag(
+                R.id.item_touch_helper_previous_elevation);
+        if (tag != null && tag instanceof Float) {
+            view.setElevation((Float) tag);
+        }
+        view.setTag(R.id.item_touch_helper_previous_elevation, null);
+        view.setTranslationX(0f);
+        view.setTranslationY(0f);
+    }
+
+    @Override
+    public void onSelected(View view) {
+    }
+
+    @Override
+    public void onDrawOver(Canvas c, RecyclerView recyclerView,
+            View view, float dX, float dY, int actionState, boolean isCurrentlyActive) {
+    }
+}
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 06ef4c9..03f2bc1 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.os.StrictMode;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Slog;
@@ -50,16 +51,20 @@
         SocketTagger.set(new NetworkManagementSocketTagger());
     }
 
-    public static void setThreadSocketStatsTag(int tag) {
+    public static int setThreadSocketStatsTag(int tag) {
+        final int old = threadSocketTags.get().statsTag;
         threadSocketTags.get().statsTag = tag;
+        return old;
     }
 
     public static int getThreadSocketStatsTag() {
         return threadSocketTags.get().statsTag;
     }
 
-    public static void setThreadSocketStatsUid(int uid) {
+    public static int setThreadSocketStatsUid(int uid) {
+        final int old = threadSocketTags.get().statsUid;
         threadSocketTags.get().statsUid = uid;
+        return old;
     }
 
     @Override
@@ -69,6 +74,9 @@
             Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
                     + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
         }
+        if (options.statsTag == -1 && StrictMode.vmUntaggedSocketEnabled()) {
+            StrictMode.onUntaggedSocket();
+        }
         // TODO: skip tagging when options would be no-op
         tagSocketFd(fd, options.statsTag, options.statsUid);
     }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 168da5f..fcb4c7b 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -109,6 +109,10 @@
     // background while in data-usage save mode, as read from the configuration files.
     final ArraySet<String> mAllowInDataUsageSave = new ArraySet<>();
 
+    // These are the action strings of broadcasts which are whitelisted to
+    // be delivered anonymously even to apps which target O+.
+    final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>();
+
     // These are the package names of apps which should be in the 'always'
     // URL-handling state upon factory reset.
     final ArraySet<String> mLinkedApps = new ArraySet<>();
@@ -162,6 +166,10 @@
         return mPermissions;
     }
 
+    public ArraySet<String> getAllowImplicitBroadcasts() {
+        return mAllowImplicitBroadcasts;
+    }
+
     public ArraySet<String> getAllowInPowerSaveExceptIdle() {
         return mAllowInPowerSaveExceptIdle;
     }
@@ -438,6 +446,17 @@
                     XmlUtils.skipCurrentTag(parser);
                     continue;
 
+                } else if ("allow-implicit-broadcast".equals(name) && allowAll) {
+                    String action = parser.getAttributeValue(null, "action");
+                    if (action == null) {
+                        Slog.w(TAG, "<allow-implicit-broadcast> without action in " + permFile
+                                + " at " + parser.getPositionDescription());
+                    } else {
+                        mAllowImplicitBroadcasts.add(action);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+
                 } else if ("app-link".equals(name) && allowAppConfigs) {
                     String pkgname = parser.getAttributeValue(null, "package");
                     if (pkgname == null) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 68034f6..327f142 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -118,7 +118,6 @@
     android/graphics/ColorFilter.cpp \
     android/graphics/DrawFilter.cpp \
     android/graphics/FontFamily.cpp \
-    android/graphics/FontUtils.cpp \
     android/graphics/CreateJavaOutputStreamAdaptor.cpp \
     android/graphics/GIFMovie.cpp \
     android/graphics/GraphicBuffer.cpp \
@@ -159,6 +158,7 @@
     android_hardware_camera2_legacy_LegacyCameraDevice.cpp \
     android_hardware_camera2_legacy_PerfMeasurement.cpp \
     android_hardware_camera2_DngCreator.cpp \
+    android_hardware_HardwareBuffer.cpp \
     android_hardware_Radio.cpp \
     android_hardware_SensorManager.cpp \
     android_hardware_SerialPort.cpp \
@@ -238,6 +238,7 @@
     libnativehelper \
     liblog \
     libcutils \
+    libdebuggerd_client \
     libutils \
     libbinder \
     libui \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index fb5d037..340f2ee 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -87,6 +87,7 @@
 extern int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv *env);
 extern int register_android_hardware_camera2_legacy_PerfMeasurement(JNIEnv *env);
 extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
+extern int register_android_hardware_HardwareBuffer(JNIEnv *env);
 extern int register_android_hardware_Radio(JNIEnv *env);
 extern int register_android_hardware_SensorManager(JNIEnv *env);
 extern int register_android_hardware_SerialPort(JNIEnv *env);
@@ -1373,6 +1374,7 @@
     REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
     REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
     REG_JNI(register_android_hardware_camera2_DngCreator),
+    REG_JNI(register_android_hardware_HardwareBuffer),
     REG_JNI(register_android_hardware_Radio),
     REG_JNI(register_android_hardware_SensorManager),
     REG_JNI(register_android_hardware_SerialPort),
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 685c93d..0c863fd 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -30,7 +30,6 @@
 #include <android_runtime/android_util_AssetManager.h>
 #include <androidfw/AssetManager.h>
 #include "Utils.h"
-#include "FontUtils.h"
 
 #include <hwui/MinikinSkia.h>
 #include <hwui/Typeface.h>
@@ -69,6 +68,12 @@
     return reinterpret_cast<jlong>(family);
 }
 
+static void FontFamily_abort(jlong builderPtr) {
+    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
+    minikin::Font::clearElementsWithLock(&builder->fonts);
+    delete builder;
+}
+
 static void FontFamily_unref(jlong familyPtr) {
     minikin::FontFamily* fontFamily = reinterpret_cast<minikin::FontFamily*>(familyPtr);
     fontFamily->Unref();
@@ -144,6 +149,16 @@
     return true;
 }
 
+static struct {
+    jmethodID mGet;
+    jmethodID mSize;
+} gListClassInfo;
+
+static struct {
+    jfieldID mTag;
+    jfieldID mStyleValue;
+} gAxisClassInfo;
+
 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr,
         jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
     NPE_CHECK_RETURN_ZERO(env, font);
@@ -152,22 +167,20 @@
     std::unique_ptr<SkFontMgr::FontParameters::Axis[]> skiaAxes;
     int skiaAxesLength = 0;
     if (listOfAxis) {
-        ListHelper list(env, listOfAxis);
-        jint listSize = list.size();
+        jint listSize = env->CallIntMethod(listOfAxis, gListClassInfo.mSize);
 
         skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]);
         skiaAxesLength = listSize;
         for (jint i = 0; i < listSize; ++i) {
-            jobject axisObject = list.get(i);
+            jobject axisObject = env->CallObjectMethod(listOfAxis, gListClassInfo.mGet, i);
             if (!axisObject) {
                 skiaAxes[i].fTag = 0;
                 skiaAxes[i].fStyleValue = 0;
                 continue;
             }
-            AxisHelper axis(env, axisObject);
 
-            jint tag = axis.getTag();
-            jfloat stylevalue = axis.getStyleValue();
+            jint tag = env->GetIntField(axisObject, gAxisClassInfo.mTag);
+            jfloat stylevalue = env->GetFloatField(axisObject, gAxisClassInfo.mStyleValue);
             skiaAxes[i].fTag = tag;
             skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue);
         }
@@ -211,8 +224,8 @@
     delete static_cast<Asset*>(context);
 }
 
-static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong builderPtr,
-        jobject jassetMgr, jstring jpath) {
+static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr,
+        jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset) {
     NPE_CHECK_RETURN_ZERO(env, jassetMgr);
     NPE_CHECK_RETURN_ZERO(env, jpath);
 
@@ -222,7 +235,18 @@
     }
 
     ScopedUtfChars str(env, jpath);
-    Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+    if (str.c_str() == nullptr) {
+        return false;
+    }
+
+    Asset* asset;
+    if (isAsset) {
+        asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+    } else {
+        asset = cookie ? mgr->openNonAsset(static_cast<int32_t>(cookie), str.c_str(),
+                Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+    }
+
     if (NULL == asset) {
         return false;
     }
@@ -253,12 +277,13 @@
 static const JNINativeMethod gFontFamilyMethods[] = {
     { "nInitBuilder",          "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
     { "nCreateFamily",         "(J)J", (void*)FontFamily_create },
+    { "nAbort",                "(J)V", (void*)FontFamily_abort },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
     { "nAddFont",              "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont },
     { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
             (void*)FontFamily_addFontWeightStyle },
-    { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
-            (void*)FontFamily_addFontFromAsset },
+    { "nAddFontFromAssetManager",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZ)Z",
+            (void*)FontFamily_addFontFromAssetManager },
 };
 
 int register_android_graphics_FontFamily(JNIEnv* env)
@@ -266,7 +291,14 @@
     int err = RegisterMethodsOrDie(env, "android/graphics/FontFamily", gFontFamilyMethods,
             NELEM(gFontFamilyMethods));
 
-    init_FontUtils(env);
+    jclass listClass = FindClassOrDie(env, "java/util/List");
+    gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
+    gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I");
+
+    jclass axisClass = FindClassOrDie(env, "android/text/FontConfig$Axis");
+    gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "mTag", "I");
+    gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "mStyleValue", "F");
+
     return err;
 }
 
diff --git a/core/jni/android/graphics/FontUtils.cpp b/core/jni/android/graphics/FontUtils.cpp
index 11c2d29..91fec2a 100644
--- a/core/jni/android/graphics/FontUtils.cpp
+++ b/core/jni/android/graphics/FontUtils.cpp
@@ -55,9 +55,9 @@
     gListClassInfo.mGet = GetMethodIDOrDie(env, listClass, "get", "(I)Ljava/lang/Object;");
     gListClassInfo.mSize = GetMethodIDOrDie(env, listClass, "size", "()I");
 
-    jclass axisClass = FindClassOrDie(env, "android/graphics/FontListParser$Axis");
-    gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "tag", "I");
-    gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "styleValue", "F");
+    jclass axisClass = FindClassOrDie(env, "android/text/FontConfig$Axis");
+    gAxisClassInfo.mTag = GetFieldIDOrDie(env, axisClass, "mTag", "I");
+    gAxisClassInfo.mStyleValue = GetFieldIDOrDie(env, axisClass, "mStyleValue", "F");
 }
 
 }  // namespace android
diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h
deleted file mode 100644
index 6fbd5e3..0000000
--- a/core/jni/android/graphics/FontUtils.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROID_GRAPHICS_FONT_UTILS_H_
-#define _ANDROID_GRAPHICS_FONT_UTILS_H_
-
-#include <jni.h>
-
-namespace android {
-
-// Utility wrapper for java.util.List
-class ListHelper {
-public:
-  ListHelper(JNIEnv* env, jobject list) : mEnv(env), mList(list) {}
-
-  jint size() const;
-  jobject get(jint index) const;
-
-private:
-  JNIEnv* mEnv;
-  jobject mList;
-};
-
-// Utility wrapper for android.graphics.FontListParser$Axis
-class AxisHelper {
-public:
-  AxisHelper(JNIEnv* env, jobject axis) : mEnv(env), mAxis(axis) {}
-
-  jint getTag() const;
-  jfloat getStyleValue() const;
-
-private:
-  JNIEnv* mEnv;
-  jobject mAxis;
-};
-
-void init_FontUtils(JNIEnv* env);
-
-}; // namespace android
-
-#endif  // _ANDROID_GRAPHICS_FONT_UTILS_H_
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 0a0fce3e..c920b8d 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -17,14 +17,12 @@
 #include "jni.h"
 #include "core_jni_helpers.h"
 
-#include "FontUtils.h"
 #include "GraphicsJNI.h"
 #include "ScopedPrimitiveArray.h"
 #include "SkTypeface.h"
 #include <android_runtime/android_util_AssetManager.h>
 #include <androidfw/AssetManager.h>
 #include <hwui/Typeface.h>
-#include <minikin/FontFamily.h>
 
 using namespace android;
 
@@ -42,23 +40,6 @@
     return reinterpret_cast<jlong>(face);
 }
 
-static jlong Typeface_createFromTypefaceWithVariation(JNIEnv* env, jobject, jlong familyHandle,
-        jobject listOfAxis) {
-    std::vector<minikin::FontVariation> variations;
-    ListHelper list(env, listOfAxis);
-    for (jint i = 0; i < list.size(); i++) {
-        jobject axisObject = list.get(i);
-        if (axisObject == nullptr) {
-            continue;
-        }
-        AxisHelper axis(env, axisObject);
-        variations.push_back(minikin::FontVariation(axis.getTag(), axis.getStyleValue()));
-    }
-    Typeface* baseTypeface = reinterpret_cast<Typeface*>(familyHandle);
-    Typeface* result = Typeface::createFromTypefaceWithVariation(baseTypeface, variations);
-    return reinterpret_cast<jlong>(result);
-}
-
 static jlong Typeface_createWeightAlias(JNIEnv* env, jobject, jlong familyHandle, jint weight) {
     Typeface* family = reinterpret_cast<Typeface*>(familyHandle);
     Typeface* face = Typeface::createWeightAlias(family, weight);
@@ -96,8 +77,6 @@
 
 static const JNINativeMethod gTypefaceMethods[] = {
     { "nativeCreateFromTypeface", "(JI)J", (void*)Typeface_createFromTypeface },
-    { "nativeCreateFromTypefaceWithVariation", "(JLjava/util/List;)J",
-            (void*)Typeface_createFromTypefaceWithVariation },
     { "nativeCreateWeightAlias",  "(JI)J", (void*)Typeface_createWeightAlias },
     { "nativeUnref",              "(J)V",  (void*)Typeface_unref },
     { "nativeGetStyle",           "(J)I",  (void*)Typeface_getStyle },
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 9ce5670..c49287c 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -63,97 +63,87 @@
     get_canvas(canvasHandle)->setBitmap(bitmap);
 }
 
-static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
+static jboolean isOpaque(jlong canvasHandle) {
     return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
 }
 
-static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
+static jint getWidth(jlong canvasHandle) {
     return static_cast<jint>(get_canvas(canvasHandle)->width());
 }
 
-static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
+static jint getHeight(jlong canvasHandle) {
     return static_cast<jint>(get_canvas(canvasHandle)->height());
 }
 
-static void setHighContrastText(JNIEnv*, jobject, jlong canvasHandle, jboolean highContrastText) {
+static void setHighContrastText(jlong canvasHandle, jboolean highContrastText) {
     Canvas* canvas = get_canvas(canvasHandle);
     canvas->setHighContrastText(highContrastText);
 }
 
-static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
-    return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
-}
-
-static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
+static jint save(jlong canvasHandle, jint flagsHandle) {
     SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
     return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
 }
 
-static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
+static jint saveLayer(jlong canvasHandle, jfloat l, jfloat t,
                       jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
     Paint* paint  = reinterpret_cast<Paint*>(paintHandle);
     SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
     return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
 }
 
-static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
+static jint saveLayerAlpha(jlong canvasHandle, jfloat l, jfloat t,
                            jfloat r, jfloat b, jint alpha, jint flagsHandle) {
     SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
 }
 
-static void restore(JNIEnv* env, jobject, jlong canvasHandle, jboolean throwOnUnderflow) {
+static bool restore(jlong canvasHandle) {
     Canvas* canvas = get_canvas(canvasHandle);
-    if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
-        if (throwOnUnderflow) {
-            doThrowISE(env, "Underflow in restore - more restores than saves");
-        }
-        return; // compat behavior - return without throwing
+    if (canvas->getSaveCount() <= 1) {
+        return false; // cannot restore anymore
     }
     canvas->restore();
+    return true; // success
 }
 
-static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount,
-        jboolean throwOnUnderflow) {
+static void restoreToCount(jlong canvasHandle, jint saveCount) {
     Canvas* canvas = get_canvas(canvasHandle);
-    if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
-        if (throwOnUnderflow) {
-            doThrowIAE(env, "Underflow in restoreToCount - more restores than saves");
-            return;
-        }
-        restoreCount = 1; // compat behavior - restore as far as possible
-    }
-    canvas->restoreToCount(restoreCount);
+    canvas->restoreToCount(saveCount);
 }
 
-static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+static jint getSaveCount(jlong canvasHandle) {
+    return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
+}
+
+static void getMatrix(jlong canvasHandle, jlong matrixHandle) {
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
     get_canvas(canvasHandle)->getMatrix(matrix);
 }
 
-static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+static void setMatrix(jlong canvasHandle, jlong matrixHandle) {
     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
     get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
 }
 
-static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
+static void concat(jlong canvasHandle, jlong matrixHandle) {
     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
     get_canvas(canvasHandle)->concat(*matrix);
 }
 
-static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
+static void rotate(jlong canvasHandle, jfloat degrees) {
     get_canvas(canvasHandle)->rotate(degrees);
 }
 
-static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
+static void scale(jlong canvasHandle, jfloat sx, jfloat sy) {
     get_canvas(canvasHandle)->scale(sx, sy);
 }
 
-static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
+static void skew(jlong canvasHandle, jfloat sx, jfloat sy) {
     get_canvas(canvasHandle)->skew(sx, sy);
 }
 
-static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
+static void translate(jlong canvasHandle, jfloat dx, jfloat dy) {
     get_canvas(canvasHandle)->translate(dx, dy);
 }
 
@@ -171,13 +161,13 @@
     return result ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
+static jboolean quickRejectRect(jlong canvasHandle,
                                 jfloat left, jfloat top, jfloat right, jfloat bottom) {
     bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
     return result ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
+static jboolean quickRejectPath(jlong canvasHandle, jlong pathHandle) {
     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
     bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
     return result ? JNI_TRUE : JNI_FALSE;
@@ -205,14 +195,14 @@
     return static_cast<SkClipOp>(rgnOp);
 }
 
-static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
+static jboolean clipRect(jlong canvasHandle, jfloat l, jfloat t,
                          jfloat r, jfloat b, jint opHandle) {
     bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b,
             opHandleToClipOp(opHandle));
     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
+static jboolean clipPath(jlong canvasHandle, jlong pathHandle,
                          jint opHandle) {
     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
     bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle));
@@ -565,7 +555,7 @@
     env->ReleaseStringChars(text, jchars);
 }
 
-static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
+static void setDrawFilter(jlong canvasHandle, jlong filterHandle) {
     get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
 }
 
@@ -587,6 +577,9 @@
 
     // ------------ @FastNative ----------------
     {"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
+    {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
+
+    // ------------ @CriticalNative ----------------
     {"nIsOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
     {"nGetWidth","(J)I", (void*) CanvasJNI::getWidth},
     {"nGetHeight","(J)I", (void*) CanvasJNI::getHeight},
@@ -595,16 +588,15 @@
     {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
     {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
     {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
-    {"nRestore","(JZ)V", (void*) CanvasJNI::restore},
-    {"nRestoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount},
-    {"nGetCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
+    {"nRestore","(J)Z", (void*) CanvasJNI::restore},
+    {"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
+    {"nGetMatrix", "(JJ)V", (void*)CanvasJNI::getMatrix},
     {"nSetMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
     {"nConcat","(JJ)V", (void*) CanvasJNI::concat},
     {"nRotate","(JF)V", (void*) CanvasJNI::rotate},
     {"nScale","(JFF)V", (void*) CanvasJNI::scale},
     {"nSkew","(JFF)V", (void*) CanvasJNI::skew},
     {"nTranslate","(JFF)V", (void*) CanvasJNI::translate},
-    {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
     {"nQuickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
     {"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
     {"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
new file mode 100644
index 0000000..fadf8a4
--- /dev/null
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HardwareBuffer"
+
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include "android_os_Parcel.h"
+#include "android/graphics/GraphicsJNI.h"
+
+#include <android/hardware_buffer.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+
+#include <binder/Parcel.h>
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/ISurfaceComposer.h>
+#include <ui/GraphicBuffer.h>
+
+#include <private/gui/ComposerService.h>
+
+#include "core_jni_helpers.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+// Defines
+// ----------------------------------------------------------------------------
+
+// Debug
+static const bool kDebugGraphicBuffer = false;
+
+// ----------------------------------------------------------------------------
+// Types
+// ----------------------------------------------------------------------------
+
+static struct {
+    jclass clazz;
+    jfieldID mNativeObject;
+    jmethodID ctor;
+} gHardwareBufferClassInfo;
+
+class GraphicBufferWrapper {
+public:
+    explicit GraphicBufferWrapper(const sp<GraphicBuffer>& buffer)
+            : buffer(buffer) {}
+
+    sp<GraphicBuffer> buffer;
+};
+
+
+// ----------------------------------------------------------------------------
+// Helper functions
+// ----------------------------------------------------------------------------
+
+static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
+    return (mask & bitsToCheck) == bitsToCheck;
+}
+
+// ----------------------------------------------------------------------------
+// HardwareBuffer lifecycle
+// ----------------------------------------------------------------------------
+
+static jlong android_hardware_HardwareBuffer_create(JNIEnv* env, jobject clazz,
+        jint width, jint height, jint format, jint layers, jlong usage) {
+
+    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+    sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
+    if (alloc == NULL) {
+        if (kDebugGraphicBuffer) {
+            ALOGW("createGraphicBufferAlloc() failed in HardwareBuffer.create()");
+        }
+        return NULL;
+    }
+
+    // TODO: update createGraphicBuffer to take two 64-bit values.
+    int pixelFormat = android_hardware_HardwareBuffer_convertToPixelFormat(format);
+    if (pixelFormat == 0) {
+        if (kDebugGraphicBuffer) {
+            ALOGW("createGraphicBufferAlloc() invalid pixel format in HardwareBuffer.create()");
+        }
+        return NULL;
+    }
+    uint32_t grallocUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(usage, 0);
+    status_t error;
+    sp<GraphicBuffer> buffer(alloc->createGraphicBuffer(width, height, pixelFormat,
+            layers, grallocUsage, &error));
+    if (buffer == NULL) {
+        if (kDebugGraphicBuffer) {
+            ALOGW("createGraphicBuffer() failed in HardwareBuffer.create()");
+        }
+        return NULL;
+    }
+
+    GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(buffer);
+    return reinterpret_cast<jlong>(wrapper);
+}
+
+static void destroyWrapper(GraphicBufferWrapper* wrapper) {
+    delete wrapper;
+}
+
+static jlong android_hardware_HardwareBuffer_getNativeFinalizer(JNIEnv* env,
+        jobject clazz) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyWrapper));
+}
+
+//----------------------------------------------------------------------------
+// Accessors
+// ----------------------------------------------------------------------------
+
+static inline GraphicBuffer* GraphicBufferWrapper_to_GraphicBuffer(
+        jlong nativeObject) {
+    return reinterpret_cast<GraphicBufferWrapper*>(nativeObject)->buffer.get();
+}
+
+static jint android_hardware_HardwareBuffer_getWidth(JNIEnv* env, jobject clazz,
+    jlong nativeObject) {
+    GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
+    return static_cast<jint>(buffer->getWidth());
+}
+
+static jint android_hardware_HardwareBuffer_getHeight(JNIEnv* env,
+    jobject clazz, jlong nativeObject) {
+    GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
+    return static_cast<jint>(buffer->getHeight());
+}
+
+static jint android_hardware_HardwareBuffer_getFormat(JNIEnv* env,
+    jobject clazz, jlong nativeObject) {
+    GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
+    return static_cast<jint>(android_hardware_HardwareBuffer_convertFromPixelFormat(
+            buffer->getPixelFormat()));
+}
+
+static jint android_hardware_HardwareBuffer_getLayers(JNIEnv* env,
+    jobject clazz, jlong nativeObject) {
+    GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
+    return static_cast<jint>(buffer->getLayerCount());
+}
+
+static jlong android_hardware_HardwareBuffer_getUsage(JNIEnv* env,
+    jobject clazz, jlong nativeObject) {
+    GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
+    return android_hardware_HardwareBuffer_convertFromGrallocUsageBits(
+            buffer->getUsage());
+}
+
+// ----------------------------------------------------------------------------
+// Serialization
+// ----------------------------------------------------------------------------
+
+static void android_hardware_HardwareBuffer_write(JNIEnv* env, jobject clazz,
+        jlong nativeObject, jobject dest) {
+    GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
+    Parcel* parcel = parcelForJavaObject(env, dest);
+    if (parcel) {
+        parcel->write(*buffer);
+    }
+}
+
+static jlong android_hardware_HardwareBuffer_read(JNIEnv* env, jobject clazz,
+        jobject in) {
+    Parcel* parcel = parcelForJavaObject(env, in);
+    if (parcel) {
+        sp<GraphicBuffer> buffer = new GraphicBuffer();
+        parcel->read(*buffer);
+        return reinterpret_cast<jlong>(new GraphicBufferWrapper(buffer));
+    }
+
+    return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// Public functions
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+AHardwareBuffer* android_hardware_HardwareBuffer_getNativeHardwareBuffer(
+        JNIEnv* env, jobject hardwareBufferObj) {
+    if (env->IsInstanceOf(hardwareBufferObj, gHardwareBufferClassInfo.clazz)) {
+        GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(
+                env->GetLongField(hardwareBufferObj, gHardwareBufferClassInfo.mNativeObject));
+        return reinterpret_cast<AHardwareBuffer*>(buffer);
+    } else {
+        return nullptr;
+    }
+}
+
+jobject android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+        JNIEnv* env, AHardwareBuffer* hardwareBuffer) {
+    GraphicBuffer* buffer = reinterpret_cast<GraphicBuffer*>(hardwareBuffer);
+    GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(buffer);
+    jobject hardwareBufferObj = env->NewObject(gHardwareBufferClassInfo.clazz,
+            gHardwareBufferClassInfo.ctor, reinterpret_cast<jlong>(wrapper));
+    if (hardwareBufferObj == NULL) {
+        delete wrapper;
+        if (env->ExceptionCheck()) {
+            ALOGE("Could not create instance of HardwareBuffer from AHardwareBuffer.");
+            LOGE_EX(env);
+            env->ExceptionClear();
+        }
+        return nullptr;
+    }
+    return hardwareBufferObj;
+}
+
+uint32_t android_hardware_HardwareBuffer_convertFromPixelFormat(uint32_t format) {
+    switch (format) {
+        case HAL_PIXEL_FORMAT_RGBA_8888:
+            return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+        case HAL_PIXEL_FORMAT_RGBX_8888:
+            return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
+        case HAL_PIXEL_FORMAT_RGB_565:
+            return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
+        case HAL_PIXEL_FORMAT_RGB_888:
+            return AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM;
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+            return AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT;
+        case HAL_PIXEL_FORMAT_BLOB:
+            return AHARDWAREBUFFER_FORMAT_BLOB;
+        default:
+            ALOGE("Unknown pixel format %u", format);
+            return 0;
+    }
+}
+
+uint32_t android_hardware_HardwareBuffer_convertToPixelFormat(uint32_t format) {
+    switch (format) {
+        case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+            return HAL_PIXEL_FORMAT_RGBA_8888;
+        case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
+            return HAL_PIXEL_FORMAT_RGBX_8888;
+        case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+            return HAL_PIXEL_FORMAT_RGB_565;
+        case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
+            return HAL_PIXEL_FORMAT_RGB_888;
+        case AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT:
+            return HAL_PIXEL_FORMAT_RGBA_FP16;
+        case AHARDWAREBUFFER_FORMAT_BLOB:
+            return HAL_PIXEL_FORMAT_BLOB;
+        default:
+            ALOGE("Unknown AHardwareBuffer format %u", format);
+            return 0;
+    }
+}
+
+uint32_t android_hardware_HardwareBuffer_convertToGrallocUsageBits(uint64_t usage0,
+        uint64_t usage1) {
+    uint32_t bits = 0;
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_CPU_READ))
+        bits |= GRALLOC_USAGE_SW_READ_RARELY;
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN))
+        bits |= GRALLOC_USAGE_SW_READ_OFTEN;
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_CPU_WRITE))
+        bits |= GRALLOC_USAGE_SW_WRITE_RARELY;
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN))
+        bits |= GRALLOC_USAGE_SW_WRITE_OFTEN;
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE))
+        bits |= GRALLOC_USAGE_HW_TEXTURE;
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT))
+        bits |= GRALLOC_USAGE_HW_RENDER;
+    // Not sure what this should be.
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_GPU_CUBEMAP)) bits |= 0;
+    //if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER) bits |= 0;
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE))
+        bits |= GRALLOC_USAGE_HW_VIDEO_ENCODER;
+    if (containsBits(usage0, AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT))
+        bits |= GRALLOC_USAGE_PROTECTED;
+
+    (void)usage1;
+
+    return bits;
+}
+
+uint64_t android_hardware_HardwareBuffer_convertFromGrallocUsageBits(uint64_t usage0) {
+    uint64_t bits = 0;
+    if (containsBits(usage0, GRALLOC_USAGE_SW_READ_RARELY))
+        bits |= AHARDWAREBUFFER_USAGE0_CPU_READ;
+    if (containsBits(usage0, GRALLOC_USAGE_SW_READ_OFTEN))
+        bits |= AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN;
+    if (containsBits(usage0, GRALLOC_USAGE_SW_WRITE_RARELY))
+        bits |= AHARDWAREBUFFER_USAGE0_CPU_WRITE;
+    if (containsBits(usage0, GRALLOC_USAGE_SW_WRITE_OFTEN))
+        bits |= AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN;
+    if (containsBits(usage0, GRALLOC_USAGE_HW_TEXTURE))
+        bits |= AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE;
+    if (containsBits(usage0, GRALLOC_USAGE_HW_RENDER))
+        bits |= AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT;
+    if (containsBits(usage0, GRALLOC_USAGE_HW_VIDEO_ENCODER))
+        bits |= AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE;
+    if (containsBits(usage0, GRALLOC_USAGE_PROTECTED))
+        bits |= AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT;
+
+    return bits;
+}
+
+}  // namespace android
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/hardware/HardwareBuffer";
+
+static const JNINativeMethod gMethods[] = {
+    { "nCreateHardwareBuffer",  "(IIIIJ)J", (void*) android_hardware_HardwareBuffer_create },
+    { "nGetNativeFinalizer", "()J",          (void*) android_hardware_HardwareBuffer_getNativeFinalizer },
+    { "nWriteHardwareBufferToParcel",  "(JLandroid/os/Parcel;)V",
+            (void*) android_hardware_HardwareBuffer_write },
+    { "nReadHardwareBufferFromParcel", "(Landroid/os/Parcel;)J",
+            (void*) android_hardware_HardwareBuffer_read },
+
+    // --------------- @FastNative ----------------------
+    { "nGetWidth", "(J)I",                   (void*) android_hardware_HardwareBuffer_getWidth },
+    { "nGetHeight", "(J)I",                  (void*) android_hardware_HardwareBuffer_getHeight },
+    { "nGetFormat", "(J)I",                  (void*) android_hardware_HardwareBuffer_getFormat },
+    { "nGetLayers", "(J)I",                  (void*) android_hardware_HardwareBuffer_getLayers },
+    { "nGetUsage", "(J)J",                  (void*) android_hardware_HardwareBuffer_getUsage },
+};
+
+int register_android_hardware_HardwareBuffer(JNIEnv* env) {
+    int err = RegisterMethodsOrDie(env, kClassPathName, gMethods,
+            NELEM(gMethods));
+
+    jclass clazz = FindClassOrDie(env, "android/hardware/HardwareBuffer");
+    gHardwareBufferClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+    gHardwareBufferClassInfo.mNativeObject = GetFieldIDOrDie(env,
+            gHardwareBufferClassInfo.clazz, "mNativeObject", "J");
+    gHardwareBufferClassInfo.ctor = GetMethodIDOrDie(env,
+            gHardwareBufferClassInfo.clazz, "<init>", "(J)V");
+
+    return err;
+}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 271f24b..bc2fc1c 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -26,6 +26,7 @@
 #include <gui/Sensor.h>
 #include <gui/SensorEventQueue.h>
 #include <gui/SensorManager.h>
+#include <cutils/native_handle.h>
 #include <utils/Log.h>
 #include <utils/Looper.h>
 #include <utils/Vector.h>
@@ -243,6 +244,54 @@
     return mgr->isDataInjectionEnabled();
 }
 
+static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
+        jlong size, jint channelType, jlongArray channelData) {
+    jint ret = -1;
+    jsize len = _env->GetArrayLength(channelData);
+    if (len > 2) {
+        jlong *data = _env->GetLongArrayElements(channelData, NULL);
+        if (data != nullptr) {
+            // construct native handle from jlong*
+            jlong numFd = data[0];
+            jlong numInt = data[1];
+            if ((numFd + numInt + 2) == len) {
+                native_handle_t *nativeHandle = native_handle_create(numFd, numInt);
+                if (nativeHandle != nullptr) {
+                    const jlong *readPointer = data + 2;
+                    int *writePointer = nativeHandle->data;
+                    size_t n = static_cast<size_t>(numFd + numInt);
+                    while (n--) {
+                        // native type of data is int, jlong is just to ensure Java does not
+                        // truncate data on 64-bit system. The cast here is safe.
+                        *writePointer++ = static_cast<int>(*readPointer++);
+                    }
+
+                    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
+                    ret = mgr->createDirectChannel(size, channelType, nativeHandle);
+
+                    // do not native_handle_close() here as handle is owned by java
+                    native_handle_delete(nativeHandle);
+                }
+            }
+            // unidirectional parameter passing, thus JNI_ABORT
+            _env->ReleaseLongArrayElements(channelData, data, JNI_ABORT);
+        }
+    }
+    return ret;
+}
+
+static void nativeDestroyDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
+        jint channelHandle) {
+    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
+    mgr->destroyDirectChannel(channelHandle);
+}
+
+static jint nativeConfigDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
+        jint channelHandle, jint sensorHandle, jint rate) {
+    SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
+    return mgr->configureDirectChannel(channelHandle, sensorHandle, rate);
+}
+
 //----------------------------------------------------------------------------
 
 class Receiver : public LooperCallback {
@@ -447,7 +496,19 @@
 
     {"nativeIsDataInjectionEnabled",
             "(J)Z",
-            (void*)nativeIsDataInjectionEnabled},
+            (void*)nativeIsDataInjectionEnabled },
+
+    {"nativeCreateDirectChannel",
+            "(JJI[J)I",
+            (void*)nativeCreateDirectChannel },
+
+    {"nativeDestroyDirectChannel",
+            "(JI)V",
+            (void*)nativeDestroyDirectChannel },
+
+    {"nativeConfigDirectChannel",
+            "(JIII)I",
+            (void*)nativeConfigDirectChannel },
 };
 
 static const JNINativeMethod gBaseEventQueueMethods[] = {
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 793d132..0c7f5a1 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -509,7 +509,7 @@
     sp<MemoryDealer> memoryDealer;
     sp<IMemory> memory;
     size_t size;
-    sound_model_handle_t handle;
+    sound_model_handle_t handle = 0;
     jobject jUuid;
     jstring jUuidString;
     const char *nUuidString;
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 86c4df7..b2c8168 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -972,6 +972,18 @@
 }
 
 // ----------------------------------------------------------------------------
+static jint android_media_AudioTrack_get_flags(JNIEnv *env,  jobject thiz) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+
+    if (lpTrack == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+            "Unable to retrieve AudioTrack pointer for getFlags()");
+        return (jint)AUDIO_JAVA_ERROR;
+    }
+    return (jint)lpTrack->getFlags();
+}
+
+// ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_get_timestamp(JNIEnv *env,  jobject thiz, jlongArray jTimestamp) {
     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
 
@@ -1212,6 +1224,7 @@
     {"native_get_position",  "()I",      (void *)android_media_AudioTrack_get_position},
     {"native_get_latency",   "()I",      (void *)android_media_AudioTrack_get_latency},
     {"native_get_underrun_count", "()I",      (void *)android_media_AudioTrack_get_underrun_count},
+    {"native_get_flags",     "()I",      (void *)android_media_AudioTrack_get_flags},
     {"native_get_timestamp", "([J)I",    (void *)android_media_AudioTrack_get_timestamp},
     {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
     {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 14d7e81..be3a87b 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -33,7 +33,7 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
-#include <cutils/debugger.h>
+#include <debuggerd/client.h>
 #include <log/log.h>
 #include <utils/misc.h>
 #include <utils/String8.h>
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 0b4fbcc..8a7600b 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -144,26 +144,22 @@
     return ctx.write();
 }
 
-/*
- * In class android.util.EventLog:
- *  static native void readEvents(int[] tags, Collection<Event> output)
- *
- *  Reads events from the event log
- */
-static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED,
-                                             jintArray tags,
-                                             jobject out) {
-
-    if (tags == NULL || out == NULL) {
-        jniThrowNullPointerException(env, NULL);
+static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime, jobject out) {
+    struct logger_list *logger_list;
+    if (startTime) {
+        logger_list = android_logger_list_alloc_time(loggerMode,
+                log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
+    } else {
+        logger_list = android_logger_list_alloc(loggerMode, 0, 0);
+    }
+    if (!logger_list) {
+        jniThrowIOException(env, errno);
         return;
     }
 
-    struct logger_list *logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0);
-
-    if (!logger_list) {
+    if (!android_logger_open(logger_list, LOG_ID_EVENTS)) {
         jniThrowIOException(env, errno);
+        android_logger_list_free(logger_list);
         return;
     }
 
@@ -228,6 +224,41 @@
 }
 
 /*
+ * In class android.util.EventLog:
+ *  static native void readEvents(int[] tags, Collection<Event> output)
+ *
+ *  Reads events from the event log
+ */
+static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED,
+                                             jintArray tags,
+                                             jobject out) {
+
+    if (tags == NULL || out == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
+    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out);
+ }
+/*
+ * In class android.util.EventLog:
+ *  static native void readEventsOnWrapping(int[] tags, long timestamp, Collection<Event> output)
+ *
+ *  Reads events from the event log, blocking until events after timestamp are to be overwritten.
+ */
+static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz UNUSED,
+                                             jintArray tags,
+                                             jlong timestamp,
+                                             jobject out) {
+    if (tags == NULL || out == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP,
+            tags, timestamp, out);
+}
+
+/*
  * JNI registration.
  */
 static const JNINativeMethod gRegisterMethods[] = {
@@ -247,6 +278,10 @@
       "([ILjava/util/Collection;)V",
       (void*) android_util_EventLog_readEvents
     },
+    { "readEventsOnWrapping",
+      "([IJLjava/util/Collection;)V",
+      (void*) android_util_EventLog_readEventsOnWrapping
+    },
 };
 
 static struct { const char *name; jclass *clazz; } gClasses[] = {
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index 6b634df..4150636 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -78,6 +78,9 @@
 
 status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
         PointerIcon* outPointerIcon) {
+    if (!pointerIconObj) {
+        return BAD_VALUE;
+    }
     outPointerIcon->style = env->GetIntField(pointerIconObj, gPointerIconClassInfo.mType);
     outPointerIcon->hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX);
     outPointerIcon->hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY);
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index d75d5c1..f221392 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -43,57 +43,6 @@
         ? (reinterpret_cast<RenderNode*>(renderNodePtr)->setPropertyFieldsDirty(dirtyFlag), true) \
         : false)
 
-static JNIEnv* getenv(JavaVM* vm) {
-    JNIEnv* env;
-    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
-    }
-    return env;
-}
-
-static jfieldID gRenderNode_validFieldID;
-
-class RenderNodeContext : public VirtualLightRefBase {
-public:
-    RenderNodeContext(JNIEnv* env, jobject jobjRef) {
-        env->GetJavaVM(&mVm);
-        // This holds a weak ref because otherwise there's a cyclic global ref
-        // with this holding a strong global ref to the view which holds
-        // a strong ref to RenderNode which holds a strong ref to this.
-        mWeakRef = env->NewWeakGlobalRef(jobjRef);
-    }
-
-    virtual ~RenderNodeContext() {
-        JNIEnv* env = getenv(mVm);
-        env->DeleteWeakGlobalRef(mWeakRef);
-    }
-
-    jobject acquireLocalRef(JNIEnv* env) {
-        return env->NewLocalRef(mWeakRef);
-    }
-
-private:
-    JavaVM* mVm;
-    jweak mWeakRef;
-};
-
-// Called by ThreadedRenderer's JNI layer
-void onRenderNodeRemoved(JNIEnv* env, RenderNode* node) {
-    auto context = reinterpret_cast<RenderNodeContext*>(node->getUserContext());
-    if (!context) return;
-    jobject jnode = context->acquireLocalRef(env);
-    if (!jnode) {
-        // The owning node has been GC'd, release the context
-        node->setUserContext(nullptr);
-        return;
-    }
-
-    // Update the valid field, since native has already removed
-    // the staging DisplayList
-    env->SetBooleanField(jnode, gRenderNode_validFieldID, false);
-    env->DeleteLocalRef(jnode);
-}
-
 // ----------------------------------------------------------------------------
 // DisplayList view properties
 // ----------------------------------------------------------------------------
@@ -108,8 +57,7 @@
     return renderNode->getDebugSize();
 }
 
-static jlong android_view_RenderNode_create(JNIEnv* env, jobject thiz,
-        jstring name) {
+static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) {
     RenderNode* renderNode = new RenderNode();
     renderNode->incStrong(0);
     if (name != NULL) {
@@ -117,7 +65,6 @@
         renderNode->setName(textArray);
         env->ReleaseStringUTFChars(name, textArray);
     }
-    renderNode->setUserContext(new RenderNodeContext(env, thiz));
     return reinterpret_cast<jlong>(renderNode);
 }
 
@@ -132,22 +79,13 @@
 
 static void android_view_RenderNode_setDisplayList(JNIEnv* env,
         jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
-    class RemovedObserver : public TreeObserver {
-    public:
-        virtual void onMaybeRemovedFromTree(RenderNode* node) override {
-            maybeRemovedNodes.insert(sp<RenderNode>(node));
-        }
-        std::set< sp<RenderNode> > maybeRemovedNodes;
-    };
-
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
-    RemovedObserver observer;
-    renderNode->setStagingDisplayList(newData, &observer);
-    for (auto& node : observer.maybeRemovedNodes) {
-        if (node->hasParents()) continue;
-        onRenderNodeRemoved(env, node.get());
-    }
+    renderNode->setStagingDisplayList(newData);
+}
+
+static jboolean android_view_RenderNode_isValid(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->isValid();
 }
 
 // ----------------------------------------------------------------------------
@@ -621,6 +559,7 @@
 // ----------------------------------------------------------------------------
 // Critical JNI via @CriticalNative annotation in RenderNode.java
 // ----------------------------------------------------------------------------
+    { "nIsValid",              "(J)Z",   (void*) android_view_RenderNode_isValid },
     { "nSetLayerType",         "(JI)Z",  (void*) android_view_RenderNode_setLayerType },
     { "nSetLayerPaint",        "(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
     { "nSetStaticMatrix",      "(JJ)Z",  (void*) android_view_RenderNode_setStaticMatrix },
@@ -691,8 +630,6 @@
             "updateWindowPosition_renderWorker", "(JIIII)V");
     gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz,
             "windowPositionLost_uiRtSync", "(J)V");
-    clazz = FindClassOrDie(env, "android/view/RenderNode");
-    gRenderNode_validFieldID = GetFieldIDOrDie(env, clazz, "mValid", "Z");
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 96e6f81..bc5f847 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -532,7 +532,7 @@
     UiFrameInfoBuilder(proxy->frameInfo())
             .setVsync(vsync, vsync)
             .addFlag(FrameInfoFlags::SurfaceCanvas);
-    proxy->syncAndDrawFrame(nullptr);
+    proxy->syncAndDrawFrame();
 }
 
 static void destroy(JNIEnv* env, jclass clazz, jlong rendererPtr) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index c00bcd4..d37f96a 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -71,31 +71,6 @@
     return env;
 }
 
-// TODO: Clean this up, it's a bit odd to need to call over to
-// rendernode's jni layer. Probably means RootRenderNode should be pulled
-// into HWUI with appropriate callbacks for the various JNI hooks so
-// that RenderNode's JNI layer can handle its own thing
-void onRenderNodeRemoved(JNIEnv* env, RenderNode* node);
-
-class ScopedRemovedRenderNodeObserver : public TreeObserver {
-public:
-    explicit ScopedRemovedRenderNodeObserver(JNIEnv* env) : mEnv(env) {}
-    ~ScopedRemovedRenderNodeObserver() {
-        for (auto& node : mMaybeRemovedNodes) {
-            if (node->hasParents()) continue;
-            onRenderNodeRemoved(mEnv, node.get());
-        }
-    }
-
-    virtual void onMaybeRemovedFromTree(RenderNode* node) override {
-        mMaybeRemovedNodes.insert(sp<RenderNode>(node));
-    }
-
-private:
-    JNIEnv* mEnv;
-    std::set< sp<RenderNode> > mMaybeRemovedNodes;
-};
-
 class OnFinishedEvent {
 public:
     OnFinishedEvent(BaseRenderNodeAnimator* animator, AnimationListener* listener)
@@ -715,18 +690,16 @@
             "Mismatched size expectations, given %d expected %d",
             frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    ScopedRemovedRenderNodeObserver observer(env);
     env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
-    return proxy->syncAndDrawFrame(&observer);
+    return proxy->syncAndDrawFrame();
 }
 
 static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong rootNodePtr) {
-    ScopedRemovedRenderNodeObserver observer(env);
     RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
     rootRenderNode->destroy();
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->destroy(&observer);
+    proxy->destroy();
 }
 
 static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz,
@@ -758,10 +731,9 @@
 
 static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong nodePtr) {
-    ScopedRemovedRenderNodeObserver observer(env);
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     RenderNode* node = reinterpret_cast<RenderNode*>(nodePtr);
-    proxy->buildLayer(node, &observer);
+    proxy->buildLayer(node);
 }
 
 static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz,
@@ -796,9 +768,8 @@
 
 static void android_view_ThreadedRenderer_destroyHardwareResources(JNIEnv* env, jobject clazz,
         jlong proxyPtr) {
-    ScopedRemovedRenderNodeObserver observer(env);
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->destroyHardwareResources(&observer);
+    proxy->destroyHardwareResources();
 }
 
 static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz,
diff --git a/core/jni/com_android_internal_os_FuseAppLoop.cpp b/core/jni/com_android_internal_os_FuseAppLoop.cpp
index 92a6934..dd003eb 100644
--- a/core/jni/com_android_internal_os_FuseAppLoop.cpp
+++ b/core/jni/com_android_internal_os_FuseAppLoop.cpp
@@ -51,7 +51,6 @@
     JNIEnv* const mEnv;
     jobject const mSelf;
     ScopedLocalRef<jbyteArray> mJniBuffer;
-    bool mActive;
 
     template <typename T>
     T checkException(T result) const {
@@ -67,8 +66,7 @@
     Callback(JNIEnv* env, jobject self) :
         mEnv(env),
         mSelf(self),
-        mJniBuffer(env, nullptr),
-        mActive(true) {}
+        mJniBuffer(env, nullptr) {}
 
     bool Init() {
         mJniBuffer.reset(mEnv->NewByteArray(kBufferSize));
@@ -76,7 +74,7 @@
     }
 
     bool IsActive() override {
-        return mActive;
+        return true;
     }
 
     int64_t OnGetSize(uint64_t inode) override {
@@ -92,10 +90,7 @@
     }
 
     int32_t OnRelease(uint64_t inode) override {
-        if (checkException(mEnv->CallIntMethod(mSelf, gOnReleaseMethod, inode)) == -1) {
-            mActive = false;
-        }
-        return fuse::kFuseSuccess;
+        return checkException(mEnv->CallIntMethod(mSelf, gOnReleaseMethod, inode));
     }
 
     int32_t OnRead(uint64_t inode, uint64_t offset, uint32_t size, void* buffer) override {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 070a2d9..5c65241 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -247,6 +247,11 @@
 
 static void DropCapabilitiesBoundingSet(JNIEnv* env) {
   for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
+    // Keep CAP_SYS_PTRACE in our bounding set so crash_dump can gain it.
+    if (i == CAP_SYS_PTRACE) {
+      continue;
+    }
+
     int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
     if (rc == -1) {
       if (errno == EINVAL) {
diff --git a/core/jni/include/android_runtime/android_hardware_HardwareBuffer.h b/core/jni/include/android_runtime/android_hardware_HardwareBuffer.h
new file mode 100644
index 0000000..60e065c
--- /dev/null
+++ b/core/jni/include/android_runtime/android_hardware_HardwareBuffer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_HARDWARE_HARDWAREBUFFER_H
+#define _ANDROID_HARDWARE_HARDWAREBUFFER_H
+
+#include <android/hardware_buffer.h>
+
+#include "jni.h"
+
+namespace android {
+
+/* Gets the underlying AHardwareBuffer for a HardwareBuffer. */
+extern AHardwareBuffer* android_hardware_HardwareBuffer_getNativeHardwareBuffer(
+        JNIEnv* env, jobject hardwareBufferObj);
+
+/* Returns a HardwareBuffer wrapper for the underlying AHardwareBuffer. */
+extern jobject android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+        JNIEnv* env, AHardwareBuffer* hardwareBuffer);
+
+/* Convert from HAL_PIXEL_FORMAT values to AHARDWAREBUFFER_FORMAT values. */
+extern uint32_t android_hardware_HardwareBuffer_convertFromPixelFormat(
+      uint32_t format);
+
+/* Convert from AHARDWAREBUFFER_FORMAT values to HAL_PIXEL_FORMAT values. */
+extern uint32_t android_hardware_HardwareBuffer_convertToPixelFormat(
+      uint32_t format);
+
+/* Convert from AHARDWAREBUFFER_USAGE* flags to to gralloc usage flags. */
+extern uint32_t android_hardware_HardwareBuffer_convertToGrallocUsageBits(
+      uint64_t usage0, uint64_t usage1);
+
+/* Convert from gralloc usage flags to to AHARDWAREBUFFER_USAGE0* flags. */
+extern uint64_t android_hardware_HardwareBuffer_convertFromGrallocUsageBits(
+      uint64_t usage0);
+
+} // namespace android
+
+#endif // _ANDROID_HARDWARE_HARDWAREBUFFER_H
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index ec2f32b..ac9ebe0 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,6 +21,8 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/core/proto/android/service/fingerprint.proto";
+import "frameworks/base/core/proto/android/service/netstats.proto";
+import "frameworks/base/core/proto/android/providers/settings.proto";
 
 package android.os;
 
@@ -49,4 +51,6 @@
 
     // System Services
     android.service.fingerprint.FingerprintServiceDumpProto fingerprint = 3000;
+    android.service.NetworkStatsServiceDumpProto netstats = 3001;
+    android.providers.settings.SettingsServiceDumpProto settings = 3002;
 }
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
new file mode 100644
index 0000000..7674d7a
--- /dev/null
+++ b/core/proto/android/providers/settings.proto
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.providers.settings;
+
+option java_multiple_files = true;
+option java_outer_classname = "SettingsServiceProto";
+
+message SettingsServiceDumpProto {
+    // Per user settings
+    repeated UserSettingsProto user_settings = 1;
+
+    // Global settings
+    GlobalSettingsProto global_settings = 2;
+}
+
+message UserSettingsProto {
+    // Should be 0, 10, 11, 12, etc. where 0 is the owner.
+    int32 user_id = 1;
+
+    // The secure settings for this user
+    SecureSettingsProto secure_settings = 2;
+
+    // The system settings for this user
+    SystemSettingsProto system_settings = 3;
+}
+
+message GlobalSettingsProto {
+    // Historical operations
+    repeated SettingsOperationProto historical_op = 1;
+
+    SettingProto add_users_when_locked = 2;
+    SettingProto enable_accessibility_global_gesture_enabled = 3;
+    SettingProto airplane_mode_on = 4;
+    SettingProto theater_mode_on = 5;
+    SettingProto radio_bluetooth = 6;
+    SettingProto radio_wifi = 7;
+    SettingProto radio_wimax = 8;
+    SettingProto radio_cell = 9;
+    SettingProto radio_nfc = 10;
+    SettingProto airplane_mode_radios = 11;
+    SettingProto airplane_mode_toggleable_radios = 12;
+    SettingProto bluetooth_disabled_profiles = 13;
+    SettingProto bluetooth_interoperability_list = 14;
+    SettingProto wifi_sleep_policy = 15;
+    SettingProto auto_time = 16;
+    SettingProto auto_time_zone = 17;
+    SettingProto car_dock_sound = 18;
+    SettingProto car_undock_sound = 19;
+    SettingProto desk_dock_sound = 20;
+    SettingProto desk_undock_sound = 21;
+    SettingProto dock_sounds_enabled = 22;
+    SettingProto dock_sounds_enabled_when_accessibility = 23;
+    SettingProto lock_sound = 24;
+    SettingProto unlock_sound = 25;
+    SettingProto trusted_sound = 26;
+    SettingProto low_battery_sound = 27;
+    SettingProto power_sounds_enabled = 28;
+    SettingProto wireless_charging_started_sound = 29;
+    SettingProto charging_sounds_enabled = 30;
+    SettingProto stay_on_while_plugged_in = 31;
+    SettingProto bugreport_in_power_menu = 32;
+    SettingProto adb_enabled = 33;
+    SettingProto debug_view_attributes = 34;
+    SettingProto assisted_gps_enabled = 35;
+    SettingProto bluetooth_on = 36;
+    SettingProto cdma_cell_broadcast_sms = 37;
+    SettingProto cdma_roaming_mode = 38;
+    SettingProto cdma_subscription_mode = 39;
+    SettingProto data_activity_timeout_mobile = 40;
+    SettingProto data_activity_timeout_wifi = 41;
+    SettingProto data_roaming = 42;
+    SettingProto mdc_initial_max_retry = 43;
+    SettingProto force_allow_on_external = 44;
+    SettingProto development_force_resizable_activities = 45;
+    SettingProto development_enable_freeform_windows_support = 46;
+    SettingProto development_settings_enabled = 47;
+    SettingProto device_provisioned = 48;
+    SettingProto device_provisioning_mobile_data_enabled = 49;
+    SettingProto display_size_forced = 50;
+    SettingProto display_scaling_force = 51;
+    SettingProto download_max_bytes_over_mobile = 52;
+    SettingProto download_recommended_max_bytes_over_mobile = 53;
+    SettingProto hdmi_control_enabled = 54;
+    SettingProto hdmi_system_audio_enabled = 55;
+    SettingProto hdmi_control_auto_wakeup_enabled = 56;
+    SettingProto hdmi_control_auto_device_off_enabled = 57;
+    SettingProto mhl_input_switching_enabled = 58;
+    SettingProto mhl_power_charge_enabled = 59;
+    SettingProto mobile_data = 60;
+    SettingProto mobile_data_always_on = 61;
+    SettingProto connectivity_metrics_buffer_size = 62;
+    SettingProto netstats_enabled = 63;
+    SettingProto netstats_poll_interval = 64;
+    SettingProto netstats_time_cache_max_age = 65;
+    SettingProto netstats_global_alert_bytes = 66;
+    SettingProto netstats_sample_enabled = 67;
+    SettingProto netstats_dev_bucket_duration = 68;
+    SettingProto netstats_dev_persist_bytes = 69;
+    SettingProto netstats_dev_rotate_age = 70;
+    SettingProto netstats_dev_delete_age = 71;
+    SettingProto netstats_uid_bucket_duration = 72;
+    SettingProto netstats_uid_persist_bytes = 73;
+    SettingProto netstats_uid_rotate_age = 74;
+    SettingProto netstats_uid_delete_age = 75;
+    SettingProto netstats_uid_tag_bucket_duration = 76;
+    SettingProto netstats_uid_tag_persist_bytes = 77;
+    SettingProto netstats_uid_tag_rotate_age = 78;
+    SettingProto netstats_uid_tag_delete_age = 79;
+    SettingProto network_preference = 80;
+    SettingProto network_scorer_app = 81;
+    SettingProto nitz_update_diff = 82;
+    SettingProto nitz_update_spacing = 83;
+    SettingProto ntp_server = 84;
+    SettingProto ntp_timeout = 85;
+    SettingProto storage_benchmark_interval = 86;
+    SettingProto dns_resolver_sample_validity_seconds = 87;
+    SettingProto dns_resolver_success_threshold_percent = 88;
+    SettingProto dns_resolver_min_samples = 89;
+    SettingProto dns_resolver_max_samples = 90;
+    SettingProto ota_disable_automatic_update = 91;
+    SettingProto package_verifier_enable = 92;
+    SettingProto package_verifier_timeout = 93;
+    SettingProto package_verifier_default_response = 94;
+    SettingProto package_verifier_setting_visible = 95;
+    SettingProto package_verifier_include_adb = 96;
+    SettingProto fstrim_mandatory_interval = 97;
+    SettingProto pdp_watchdog_poll_interval_ms = 98;
+    SettingProto pdp_watchdog_long_poll_interval_ms = 99;
+    SettingProto pdp_watchdog_error_poll_interval_ms = 100;
+    SettingProto pdp_watchdog_trigger_packet_count = 101;
+    SettingProto pdp_watchdog_error_poll_count = 102;
+    SettingProto pdp_watchdog_max_pdp_reset_fail_count = 103;
+    SettingProto sampling_profiler_ms = 104;
+    SettingProto setup_prepaid_data_service_url = 105;
+    SettingProto setup_prepaid_detection_target_url = 106;
+    SettingProto setup_prepaid_detection_redir_host = 107;
+    SettingProto sms_outgoing_check_interval_ms = 108;
+    SettingProto sms_outgoing_check_max_count = 109;
+    SettingProto sms_short_code_confirmation = 110;
+    SettingProto sms_short_code_rule = 111;
+    SettingProto tcp_default_init_rwnd = 112;
+    SettingProto tether_supported = 113;
+    SettingProto tether_dun_required = 114;
+    SettingProto tether_dun_apn = 115;
+    SettingProto carrier_app_whitelist = 116;
+    SettingProto usb_mass_storage_enabled = 117;
+    SettingProto use_google_mail = 118;
+    SettingProto webview_data_reduction_proxy_key = 119;
+    SettingProto webview_fallback_logic_enabled = 120;
+    SettingProto webview_provider = 121;
+    SettingProto webview_multiprocess = 122;
+    SettingProto network_switch_notification_daily_limit = 123;
+    SettingProto network_switch_notification_rate_limit_millis = 124;
+    SettingProto network_avoid_bad_wifi = 125;
+    SettingProto wifi_display_on = 126;
+    SettingProto wifi_display_certification_on = 127;
+    SettingProto wifi_display_wps_config = 128;
+    SettingProto wifi_networks_available_notification_on = 129;
+    SettingProto wimax_networks_available_notification_on = 130;
+    SettingProto wifi_networks_available_repeat_delay = 131;
+    SettingProto wifi_country_code = 132;
+    SettingProto wifi_framework_scan_interval_ms = 133;
+    SettingProto wifi_idle_ms = 134;
+    SettingProto wifi_num_open_networks_kept = 135;
+    SettingProto wifi_on = 136;
+    SettingProto wifi_scan_always_available = 137;
+    SettingProto wifi_wakeup_enabled = 138;
+    SettingProto network_recommendations_enabled = 139;
+    SettingProto ble_scan_always_available = 140;
+    SettingProto wifi_saved_state = 141;
+    SettingProto wifi_supplicant_scan_interval_ms = 142;
+    SettingProto wifi_enhanced_auto_join = 143;
+    SettingProto wifi_network_show_rssi = 144;
+    SettingProto wifi_scan_interval_when_p2p_connected_ms = 145;
+    SettingProto wifi_watchdog_on = 146;
+    SettingProto wifi_watchdog_poor_network_test_enabled = 147;
+    SettingProto wifi_suspend_optimizations_enabled = 148;
+    SettingProto wifi_verbose_logging_enabled = 149;
+    SettingProto wifi_max_dhcp_retry_count = 150;
+    SettingProto wifi_mobile_data_transition_wakelock_timeout_ms = 151;
+    SettingProto wifi_device_owner_configs_lockdown = 152;
+    SettingProto wifi_frequency_band = 153;
+    SettingProto wifi_p2p_device_name = 154;
+    SettingProto wifi_reenable_delay_ms = 155;
+    SettingProto wifi_ephemeral_out_of_range_timeout_ms = 156;
+    SettingProto data_stall_alarm_non_aggressive_delay_in_ms = 157;
+    SettingProto data_stall_alarm_aggressive_delay_in_ms = 158;
+    SettingProto provisioning_apn_alarm_delay_in_ms = 159;
+    SettingProto gprs_register_check_period_ms = 160;
+    SettingProto wtf_is_fatal = 161;
+    SettingProto mode_ringer = 162;
+    SettingProto overlay_display_devices = 163;
+    SettingProto battery_discharge_duration_threshold = 164;
+    SettingProto battery_discharge_threshold = 165;
+    SettingProto send_action_app_error = 166;
+    SettingProto dropbox_age_seconds = 167;
+    SettingProto dropbox_max_files = 168;
+    SettingProto dropbox_quota_kb = 169;
+    SettingProto dropbox_quota_percent = 170;
+    SettingProto dropbox_reserve_percent = 171;
+    SettingProto dropbox_tag_prefix = 172;
+    SettingProto error_logcat_prefix = 173;
+    SettingProto sys_free_storage_log_interval = 174;
+    SettingProto disk_free_change_reporting_threshold = 175;
+    SettingProto sys_storage_threshold_percentage = 176;
+    SettingProto sys_storage_threshold_max_bytes = 177;
+    SettingProto sys_storage_full_threshold_bytes = 178;
+    SettingProto sync_max_retry_delay_in_seconds = 179;
+    SettingProto connectivity_change_delay = 180;
+    SettingProto connectivity_sampling_interval_in_seconds = 181;
+    SettingProto pac_change_delay = 182;
+    SettingProto captive_portal_mode = 183;
+    SettingProto captive_portal_server = 184;
+    SettingProto captive_portal_https_url = 185;
+    SettingProto captive_portal_http_url = 186;
+    SettingProto captive_portal_fallback_url = 187;
+    SettingProto captive_portal_use_https = 188;
+    SettingProto captive_portal_user_agent = 189;
+    SettingProto nsd_on = 190;
+    SettingProto set_install_location = 191;
+    SettingProto default_install_location = 192;
+    SettingProto inet_condition_debounce_up_delay = 193;
+    SettingProto inet_condition_debounce_down_delay = 194;
+    SettingProto read_external_storage_enforced_default = 195;
+    SettingProto http_proxy = 196;
+    SettingProto global_http_proxy_host = 197;
+    SettingProto global_http_proxy_port = 198;
+    SettingProto global_http_proxy_exclusion_list = 199;
+    SettingProto global_http_proxy_pac = 200;
+    SettingProto set_global_http_proxy = 201;
+    SettingProto default_dns_server = 202;
+    SettingProto bluetooth_headset_priority_prefix = 203;
+    SettingProto bluetooth_a2dp_sink_priority_prefix = 204;
+    SettingProto bluetooth_a2dp_src_priority_prefix = 205;
+    SettingProto bluetooth_input_device_priority_prefix = 206;
+    SettingProto bluetooth_map_priority_prefix = 207;
+    SettingProto bluetooth_map_client_priority_prefix = 208;
+    SettingProto bluetooth_pbap_client_priority_prefix = 209;
+    SettingProto bluetooth_sap_priority_prefix = 210;
+    SettingProto bluetooth_pan_priority_prefix = 211;
+    SettingProto device_idle_constants = 212;
+    SettingProto device_idle_constants_watch = 213;
+    SettingProto app_idle_constants = 214;
+    SettingProto alarm_manager_constants = 215;
+    SettingProto job_scheduler_constants = 216;
+    SettingProto shortcut_manager_constants = 217;
+    SettingProto window_animation_scale = 218;
+    SettingProto transition_animation_scale = 219;
+    SettingProto animator_duration_scale = 220;
+    SettingProto fancy_ime_animations = 221;
+    SettingProto compatibility_mode = 222;
+    SettingProto emergency_tone = 223;
+    SettingProto call_auto_retry = 224;
+    SettingProto emergency_affordance_needed = 225;
+    SettingProto preferred_network_mode = 226;
+    SettingProto debug_app = 227;
+    SettingProto wait_for_debugger = 228;
+    SettingProto low_power_mode = 229;
+    SettingProto low_power_mode_trigger_level = 230;
+    SettingProto always_finish_activities = 231;
+    SettingProto dock_audio_media_enabled = 232;
+    SettingProto encoded_surround_output = 233;
+    SettingProto audio_safe_volume_state = 234;
+    SettingProto tzinfo_update_content_url = 235;
+    SettingProto tzinfo_update_metadata_url = 236;
+    SettingProto selinux_update_content_url = 237;
+    SettingProto selinux_update_metadata_url = 238;
+    SettingProto sms_short_codes_update_content_url = 239;
+    SettingProto sms_short_codes_update_metadata_url = 240;
+    SettingProto apn_db_update_content_url = 241;
+    SettingProto apn_db_update_metadata_url = 242;
+    SettingProto cert_pin_update_content_url = 243;
+    SettingProto cert_pin_update_metadata_url = 244;
+    SettingProto intent_firewall_update_content_url = 245;
+    SettingProto intent_firewall_update_metadata_url = 246;
+    SettingProto selinux_status = 247;
+    SettingProto development_force_rtl = 248;
+    SettingProto low_battery_sound_timeout = 249;
+    SettingProto wifi_bounce_delay_override_ms = 250;
+    SettingProto policy_control = 251;
+    SettingProto zen_mode = 252;
+    SettingProto zen_mode_ringer_level = 253;
+    SettingProto zen_mode_config_etag = 254;
+    SettingProto heads_up_notifications_enabled = 255;
+    SettingProto device_name = 256;
+    SettingProto network_scoring_provisioned = 257;
+    SettingProto require_password_to_decrypt = 258;
+    SettingProto enhanced_4g_mode_enabled = 259;
+    SettingProto vt_ims_enabled = 260;
+    SettingProto wfc_ims_enabled = 261;
+    SettingProto wfc_ims_mode = 262;
+    SettingProto wfc_ims_roaming_mode = 263;
+    SettingProto wfc_ims_roaming_enabled = 264;
+    SettingProto lte_service_forced = 265;
+    SettingProto ephemeral_cookie_max_size_bytes = 266;
+    SettingProto enable_ephemeral_feature = 267;
+    SettingProto uninstalled_ephemeral_app_cache_duration_millis = 268;
+    SettingProto allow_user_switching_when_system_user_locked = 269;
+    SettingProto boot_count = 270;
+    SettingProto safe_boot_disallowed = 271;
+    SettingProto device_demo_mode = 272;
+    SettingProto retail_demo_mode_constants = 273;
+    SettingProto database_downgrade_reason = 274;
+    SettingProto contacts_database_wal_enabled = 275;
+    SettingProto multi_sim_voice_call_subscription = 276;
+    SettingProto multi_sim_voice_prompt = 277;
+    SettingProto multi_sim_data_call_subscription = 278;
+    SettingProto multi_sim_sms_subscription = 279;
+    SettingProto multi_sim_sms_prompt = 280;
+    SettingProto new_contact_aggregator = 281;
+    SettingProto contact_metadata_sync_enabled = 282;
+    SettingProto enable_cellular_on_boot = 283;
+    SettingProto max_notification_enqueue_rate = 284;
+    SettingProto cell_on = 285;
+}
+
+message SecureSettingsProto {
+    // Historical operations
+    repeated SettingsOperationProto historical_op = 1;
+
+    SettingProto android_id = 2;
+    SettingProto default_input_method = 3;
+    SettingProto selected_input_method_subtype = 4;
+    SettingProto input_methods_subtype_history = 5;
+    SettingProto input_method_selector_visibility = 6;
+    SettingProto voice_interaction_service = 7;
+    SettingProto auto_fill_service = 8;
+    SettingProto bluetooth_hci_log = 9;
+    SettingProto user_setup_complete = 10;
+    SettingProto completed_category_prefix = 11;
+    SettingProto enabled_input_methods = 12;
+    SettingProto disabled_system_input_methods = 13;
+    SettingProto show_ime_with_hard_keyboard = 14;
+    SettingProto always_on_vpn_app = 15;
+    SettingProto always_on_vpn_lockdown = 16;
+    SettingProto install_non_market_apps = 17;
+    SettingProto location_mode = 18;
+    SettingProto location_previous_mode = 19;
+    SettingProto lock_to_app_exit_locked = 20;
+    SettingProto lock_screen_lock_after_timeout = 21;
+    SettingProto lock_screen_allow_remote_input = 22;
+    SettingProto show_note_about_notification_hiding = 23;
+    SettingProto trust_agents_initialized = 24;
+    SettingProto parental_control_enabled = 25;
+    SettingProto parental_control_last_update = 26;
+    SettingProto parental_control_redirect_url = 27;
+    SettingProto settings_classname = 28;
+    SettingProto accessibility_enabled = 29;
+    SettingProto touch_exploration_enabled = 30;
+    SettingProto enabled_accessibility_services = 31;
+    SettingProto touch_exploration_granted_accessibility_services = 32;
+    SettingProto accessibility_speak_password = 33;
+    SettingProto accessibility_high_text_contrast_enabled = 34;
+    SettingProto accessibility_script_injection = 35;
+    SettingProto accessibility_screen_reader_url = 36;
+    SettingProto accessibility_web_content_key_bindings = 37;
+    SettingProto accessibility_display_magnification_enabled = 38;
+    SettingProto accessibility_display_magnification_scale = 39;
+    SettingProto accessibility_soft_keyboard_mode = 40;
+    SettingProto accessibility_captioning_enabled = 41;
+    SettingProto accessibility_captioning_locale = 42;
+    SettingProto accessibility_captioning_preset = 43;
+    SettingProto accessibility_captioning_background_color = 44;
+    SettingProto accessibility_captioning_foreground_color = 45;
+    SettingProto accessibility_captioning_edge_type = 46;
+    SettingProto accessibility_captioning_edge_color = 47;
+    SettingProto accessibility_captioning_window_color = 48;
+    SettingProto accessibility_captioning_typeface = 49;
+    SettingProto accessibility_captioning_font_scale = 50;
+    SettingProto accessibility_display_inversion_enabled = 51;
+    SettingProto accessibility_display_daltonizer_enabled = 52;
+    SettingProto accessibility_display_daltonizer = 53;
+    SettingProto accessibility_autoclick_enabled = 54;
+    SettingProto accessibility_autoclick_delay = 55;
+    SettingProto accessibility_large_pointer_icon = 56;
+    SettingProto long_press_timeout = 57;
+    SettingProto multi_press_timeout = 58;
+    SettingProto enabled_print_services = 59;
+    SettingProto disabled_print_services = 60;
+    SettingProto display_density_forced = 61;
+    SettingProto tts_default_rate = 62;
+    SettingProto tts_default_pitch = 63;
+    SettingProto tts_default_synth = 64;
+    SettingProto tts_default_locale = 65;
+    SettingProto tts_enabled_plugins = 66;
+    SettingProto connectivity_release_pending_intent_delay_ms = 67;
+    SettingProto allowed_geolocation_origins = 68;
+    SettingProto preferred_tty_mode = 69;
+    SettingProto enhanced_voice_privacy_enabled = 70;
+    SettingProto tty_mode_enabled = 71;
+    SettingProto backup_enabled = 72;
+    SettingProto backup_auto_restore = 73;
+    SettingProto backup_provisioned = 74;
+    SettingProto backup_transport = 75;
+    SettingProto last_setup_shown = 76;
+    SettingProto search_global_search_activity = 77;
+    SettingProto search_num_promoted_sources = 78;
+    SettingProto search_max_results_to_display = 79;
+    SettingProto search_max_results_per_source = 80;
+    SettingProto search_web_results_override_limit = 81;
+    SettingProto search_promoted_source_deadline_millis = 82;
+    SettingProto search_source_timeout_millis = 83;
+    SettingProto search_prefill_millis = 84;
+    SettingProto search_max_stat_age_millis = 85;
+    SettingProto search_max_source_event_age_millis = 86;
+    SettingProto search_min_impressions_for_source_ranking = 87;
+    SettingProto search_min_clicks_for_source_ranking = 88;
+    SettingProto search_max_shortcuts_returned = 89;
+    SettingProto search_query_thread_core_pool_size = 90;
+    SettingProto search_query_thread_max_pool_size = 91;
+    SettingProto search_shortcut_refresh_core_pool_size = 92;
+    SettingProto search_shortcut_refresh_max_pool_size = 93;
+    SettingProto search_thread_keepalive_seconds = 94;
+    SettingProto search_per_source_concurrent_query_limit = 95;
+    SettingProto mount_play_notification_snd = 96;
+    SettingProto mount_ums_autostart = 97;
+    SettingProto mount_ums_prompt = 98;
+    SettingProto mount_ums_notify_enabled = 99;
+    SettingProto anr_show_background = 100;
+    SettingProto voice_recognition_service = 101;
+    SettingProto package_verifier_user_consent = 102;
+    SettingProto selected_spell_checker = 103;
+    SettingProto selected_spell_checker_subtype = 104;
+    SettingProto spell_checker_enabled = 105;
+    SettingProto incall_power_button_behavior = 106;
+    SettingProto incall_back_button_behavior = 107;
+    SettingProto wake_gesture_enabled = 108;
+    SettingProto doze_enabled = 109;
+    SettingProto doze_always_on = 110;
+    SettingProto doze_pulse_on_pick_up = 111;
+    SettingProto doze_pulse_on_double_tap = 112;
+    SettingProto ui_night_mode = 113;
+    SettingProto screensaver_enabled = 114;
+    SettingProto screensaver_components = 115;
+    SettingProto screensaver_activate_on_dock = 116;
+    SettingProto screensaver_activate_on_sleep = 117;
+    SettingProto screensaver_default_component = 118;
+    SettingProto nfc_payment_default_component = 119;
+    SettingProto nfc_payment_foreground = 120;
+    SettingProto sms_default_application = 121;
+    SettingProto dialer_default_application = 122;
+    SettingProto emergency_assistance_application = 123;
+    SettingProto assist_structure_enabled = 124;
+    SettingProto assist_screenshot_enabled = 125;
+    SettingProto assist_disclosure_enabled = 126;
+    SettingProto enabled_notification_assistant = 127;
+    SettingProto enabled_notification_listeners = 128;
+    SettingProto enabled_notification_policy_access_packages = 129;
+    SettingProto sync_parent_sounds = 130;
+    SettingProto immersive_mode_confirmations = 131;
+    SettingProto print_service_search_uri = 132;
+    SettingProto payment_service_search_uri = 133;
+    SettingProto skip_first_use_hints = 134;
+    SettingProto unsafe_volume_music_active_ms = 135;
+    SettingProto lock_screen_show_notifications = 136;
+    SettingProto tv_input_hidden_inputs = 137;
+    SettingProto tv_input_custom_labels = 138;
+    SettingProto usb_audio_automatic_routing_disabled = 139;
+    SettingProto sleep_timeout = 140;
+    SettingProto double_tap_to_wake = 141;
+    SettingProto assistant = 142;
+    SettingProto camera_gesture_disabled = 143;
+    SettingProto camera_double_tap_power_gesture_disabled = 144;
+    SettingProto camera_double_twist_to_flip_enabled = 145;
+    SettingProto night_display_activated = 146;
+    SettingProto night_display_auto_mode = 147;
+    SettingProto night_display_custom_start_time = 148;
+    SettingProto night_display_custom_end_time = 149;
+    SettingProto brightness_use_twilight = 150;
+    SettingProto enabled_vr_listeners = 151;
+    SettingProto vr_display_mode = 152;
+    SettingProto carrier_apps_handled = 153;
+    SettingProto managed_profile_contact_remote_search = 154;
+    SettingProto automatic_storage_manager_enabled = 155;
+    SettingProto automatic_storage_manager_days_to_retain = 156;
+    SettingProto automatic_storage_manager_bytes_cleared = 157;
+    SettingProto automatic_storage_manager_last_run = 158;
+    SettingProto system_navigation_keys_enabled = 159;
+    SettingProto downloads_backup_enabled = 160;
+    SettingProto downloads_backup_allow_metered = 161;
+    SettingProto downloads_backup_charging_only = 162;
+    SettingProto automatic_storage_manager_downloads_days_to_retain = 163;
+    SettingProto qs_tiles = 164;
+    SettingProto demo_user_setup_complete = 165;
+    SettingProto web_action_enabled = 166;
+    SettingProto device_paired = 167;
+}
+
+message SystemSettingsProto {
+    // Historical operations
+    repeated SettingsOperationProto historical_op = 1;
+
+    SettingProto end_button_behavior = 2;
+    SettingProto advanced_settings = 3;
+    SettingProto bluetooth_discoverability = 4;
+    SettingProto bluetooth_discoverability_timeout = 5;
+    SettingProto font_scale = 6;
+    SettingProto system_locales = 7;
+    SettingProto screen_off_timeout = 8;
+    SettingProto screen_brightness = 9;
+    SettingProto screen_brightness_for_vr = 10;
+    SettingProto screen_brightness_mode = 11;
+    SettingProto screen_auto_brightness_adj = 12;
+    SettingProto mode_ringer_streams_affected = 13;
+    SettingProto mute_streams_affected = 14;
+    SettingProto vibrate_on = 15;
+    SettingProto vibrate_input_devices = 16;
+    SettingProto volume_ring = 17;
+    SettingProto volume_system = 18;
+    SettingProto volume_voice = 19;
+    SettingProto volume_music = 20;
+    SettingProto volume_alarm = 21;
+    SettingProto volume_notification = 22;
+    SettingProto volume_bluetooth_sco = 23;
+    SettingProto volume_master = 24;
+    SettingProto master_mono = 25;
+    SettingProto vibrate_in_silent = 26;
+    SettingProto append_for_last_audible = 27;
+    SettingProto ringtone = 28;
+    SettingProto ringtone_cache = 29;
+    SettingProto notification_sound = 30;
+    SettingProto notification_sound_cache = 31;
+    SettingProto alarm_alert = 32;
+    SettingProto alarm_alert_cache = 33;
+    SettingProto media_button_receiver = 34;
+    SettingProto text_auto_replace = 35;
+    SettingProto text_auto_caps = 36;
+    SettingProto text_auto_punctuate = 37;
+    SettingProto text_show_password = 38;
+    SettingProto show_gtalk_service_status = 39;
+    SettingProto time_12_24 = 40;
+    SettingProto date_format = 41;
+    SettingProto setup_wizard_has_run = 42;
+    SettingProto accelerometer_rotation = 43;
+    SettingProto user_rotation = 44;
+    SettingProto hide_rotation_lock_toggle_for_accessibility = 45;
+    SettingProto vibrate_when_ringing = 46;
+    SettingProto dtmf_tone_when_dialing = 47;
+    SettingProto dtmf_tone_type_when_dialing = 48;
+    SettingProto hearing_aid = 49;
+    SettingProto tty_mode = 50;
+    SettingProto sound_effects_enabled = 51;
+    SettingProto haptic_feedback_enabled = 52;
+    SettingProto notification_light_pulse = 53;
+    SettingProto pointer_location = 54;
+    SettingProto show_touches = 55;
+    SettingProto window_orientation_listener_log = 56;
+    SettingProto lockscreen_sounds_enabled = 57;
+    SettingProto lockscreen_disabled = 58;
+    SettingProto sip_receive_calls = 59;
+    SettingProto sip_call_options = 60;
+    SettingProto sip_always = 61;
+    SettingProto sip_address_only = 62;
+    SettingProto pointer_speed = 63;
+    SettingProto lock_to_app_enabled = 64;
+    SettingProto egg_mode = 65;
+    SettingProto when_to_make_wifi_calls = 66;
+}
+
+message SettingProto {
+    // ID of the setting
+    string id = 1;
+
+    // Name of the setting
+    string name = 2;
+
+    // Package name of the setting
+    string pkg = 3;
+
+    // Value of this setting
+    string value = 4;
+
+    // Default value of this setting
+    string default_value = 5;
+
+    // Whether the default is set by the system
+    bool default_from_system = 6;
+}
+
+message SettingsOperationProto {
+    // When the operation happened
+    int64 timestamp = 1;
+
+    // Type of the operation
+    string operation = 2;
+
+    // Name of the setting that was affected (optional)
+    string setting = 3;
+}
diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto
new file mode 100644
index 0000000..4d86526
--- /dev/null
+++ b/core/proto/android/service/diskstats.proto
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.service.diskstats;
+
+option java_multiple_files = true;
+option java_outer_classname = "DiskStatsServiceProto";
+
+message DiskStatsServiceDumpProto {
+    enum EncryptionType {
+        // Unknown encryption type
+        ENCRYPTION_UNKNOWN = 0;
+        // No encryption
+        ENCRYPTION_NONE = 1;
+        // Full disk encryption
+        ENCRYPTION_FULL_DISK = 2;
+        // File-based encryption
+        ENCRYPTION_FILE_BASED = 3;
+    }
+    // Whether the latency test resulted in an error
+    bool has_test_error = 1;
+    // If the test errored, error message is contained here
+    string error_message = 2;
+    // 512B write latency in milliseconds, if the test was successful
+    int32 write_512b_latency_millis = 3;
+    // Free Space in the major partitions
+    repeated DiskStatsFreeSpaceProto partitions_free_space = 4;
+    // Is the device using file-based encryption, full disk encryption or other
+    EncryptionType encryption = 5;
+    // Cached values of folder sizes, etc.
+    DiskStatsCachedValuesProto cached_folder_sizes = 6;
+}
+
+message DiskStatsCachedValuesProto {
+    // Total app data size, in kilobytes
+    int64 agg_apps_size = 1;
+    // Total app cache size, in kilobytes
+    int64 agg_apps_cache_size = 2;
+    // Size of image files, in kilobytes
+    int64 photos_size = 3;
+    // Size of video files, in kilobytes
+    int64 videos_size = 4;
+    // Size of audio files, in kilobytes
+    int64 audio_size = 5;
+    // Size of downloads, in kilobytes
+    int64 downloads_size = 6;
+    // Size of system directory, in kilobytes
+    int64 system_size = 7;
+    // Size of other files, in kilobytes
+    int64 other_size = 8;
+    // Sizes of individual packages
+    repeated DiskStatsAppSizesProto app_sizes = 9;
+}
+
+message DiskStatsAppSizesProto {
+    // Name of the package
+    string package_name = 1;
+    // App's data size in kilobytes
+    int64 app_size = 2;
+    // App's cache size in kilobytes
+    int64 cache_size = 3;
+}
+
+message DiskStatsFreeSpaceProto {
+    enum Folder {
+        // Data folder
+        FOLDER_DATA = 0;
+        // Cache folder
+        FOLDER_CACHE = 1;
+        // System folder
+        FOLDER_SYSTEM = 2;
+    }
+    // Which folder?
+    Folder folder = 1;
+    // Available space, in kilobytes
+    int64 available_space = 2;
+    // Total space, in kilobytes
+    int64 total_space = 3;
+}
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
new file mode 100644
index 0000000..5cca6ab
--- /dev/null
+++ b/core/proto/android/service/netstats.proto
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.service;
+
+option java_multiple_files = true;
+option java_outer_classname = "NetworkStatsServiceProto";
+
+// Represents dumpsys from NetworkStatsService (netstats).
+message NetworkStatsServiceDumpProto {
+    repeated NetworkInterfaceProto active_interfaces = 1;
+
+    repeated NetworkInterfaceProto active_uid_interfaces = 2;
+
+    NetworkStatsRecorderProto dev_stats = 3;
+
+    NetworkStatsRecorderProto xt_stats = 4;
+
+    NetworkStatsRecorderProto uid_stats = 5;
+
+    NetworkStatsRecorderProto uid_tag_stats = 6;
+}
+
+// Corresponds to NetworkStatsService.mActiveIfaces/mActiveUidIfaces.
+message NetworkInterfaceProto {
+    string interface = 1;
+
+    NetworkIdentitySetProto identities = 2;
+}
+
+// Corresponds to NetworkIdentitySet.
+message NetworkIdentitySetProto {
+    repeated NetworkIdentityProto identities = 1;
+}
+
+// Corresponds to NetworkIdentity.
+message NetworkIdentityProto {
+    // Constats from ConnectivityManager.TYPE_*.
+    int32 type = 1;
+
+    string subscriber_id = 2;
+
+    string network_id = 3;
+
+    bool roaming = 4;
+
+    bool metered = 5;
+}
+
+// Corresponds to NetworkStatsRecorder.
+message NetworkStatsRecorderProto {
+    int64 pending_total_bytes = 1;
+
+    NetworkStatsCollectionProto complete_history = 2;
+}
+
+// Corresponds to NetworkStatsCollection.
+message NetworkStatsCollectionProto {
+    repeated NetworkStatsCollectionStatsProto stats = 1;
+}
+
+// Corresponds to NetworkStatsCollection.mStats.
+message NetworkStatsCollectionStatsProto {
+    NetworkStatsCollectionKeyProto key = 1;
+
+    NetworkStatsHistoryProto history = 2;
+}
+
+// Corresponds to NetworkStatsCollection.Key.
+message NetworkStatsCollectionKeyProto {
+    NetworkIdentitySetProto identity = 1;
+
+    int32 uid = 2;
+
+    int32 set = 3;
+
+    int32 tag = 4;
+}
+
+// Corresponds to NetworkStatsHistory.
+message NetworkStatsHistoryProto {
+    // Duration for this bucket in milliseconds.
+    int64 bucket_duration_ms = 1;
+
+    repeated NetworkStatsHistoryBucketProto buckets = 2;
+}
+
+// Corresponds to each bucket in NetworkStatsHistory.
+message NetworkStatsHistoryBucketProto {
+    // Bucket start time in milliseconds since epoch.
+    int64 bucket_start_ms = 1;
+
+    int64 rx_bytes = 2;
+
+    int64 rx_packets = 3;
+
+    int64 tx_bytes = 4;
+
+    int64 tx_packets = 5;
+
+    int64 operations = 6;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 84b03d2..2b6c0ba 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -443,6 +443,7 @@
     <protected-broadcast android:name="com.android.server.telecom.intent.action.CALLS_ADD_ENTRY" />
     <protected-broadcast android:name="com.android.settings.location.MODE_CHANGING" />
 
+    <protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
     <protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
     <protected-broadcast android:name="EventConditionProvider.EVALUATE" />
     <protected-broadcast android:name="SnoozeHelper.EVALUATE" />
@@ -514,6 +515,7 @@
     <!-- Added in O -->
     <!-- TODO: temporary broadcast used by AutoFillManagerServiceImpl; will be removed -->
     <protected-broadcast android:name="com.android.internal.autofill.action.REQUEST_AUTOFILL" />
+    <protected-broadcast android:name="android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
@@ -801,6 +803,16 @@
         android:description="@string/permdesc_callPhone"
         android:protectionLevel="dangerous" />
 
+    <!-- Allows an application to manage its own calls, but rely on the system to route focus to the
+         currently active call.
+        <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.MANAGE_OWN_CALLS"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:label="@string/permlab_manageOwnCalls"
+        android:description="@string/permdesc_manageOwnCalls"
+        android:protectionLevel="dangerous" />
+
     <!-- Allows an application to access the IMS call service: making and
          modifying a call
         <p>Protection level: signature|privileged
@@ -1259,7 +1271,7 @@
          recommendations and scores from the NetworkScoreService.
          <p>Not for use by third-party applications. @hide -->
     <permission android:name="android.permission.REQUEST_NETWORK_SCORES"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|setup" />
 
     <!-- ======================================= -->
     <!-- Permissions for short range, peripheral networks -->
@@ -1597,6 +1609,16 @@
     <permission android:name="android.permission.RECEIVE_STK_COMMANDS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by an ImsService to ensure that only the
+         system can bind to it.
+         <p>Protection level: signature|privileged
+         @SystemApi
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_IMS_SERVICE"
+        android:protectionLevel="signature|privileged" />
+
+
     <!-- ================================== -->
     <!-- Permissions for sdcard interaction -->
     <!-- ================================== -->
@@ -1953,7 +1975,7 @@
          {@link android.content.pm.PackageManager#addPackageToPreferred}
          for details. -->
     <permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|verifier" />
 
     <!-- Allows an application to receive the
          {@link android.content.Intent#ACTION_BOOT_COMPLETED} that is
@@ -2470,13 +2492,23 @@
         android:protectionLevel="signature" />
 
     <!-- Allows an application to request installing packages. Apps
-         targeting APIs greater than 22 must hold this permission in
+         targeting APIs greater than 25 must hold this permission in
          order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}.
-         <p>Protection level: normal
+         <p>Protection level: signature
     -->
     <permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"
         android:label="@string/permlab_requestInstallPackages"
         android:description="@string/permdesc_requestInstallPackages"
+        android:protectionLevel="signature|appop" />
+
+    <!-- Allows an application to request deleting packages. Apps
+         targeting APIs greater than 25 must hold this permission in
+         order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE}.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.REQUEST_DELETE_PACKAGES"
+        android:label="@string/permlab_requestDeletePackages"
+        android:description="@string/permdesc_requestDeletePackages"
         android:protectionLevel="normal" />
 
     <!-- @SystemApi Allows an application to install packages.
@@ -2625,6 +2657,21 @@
     <permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide Allows an application to set the volume key long-press listener.
+         <p>When it's set, the application will receive the volume key long-press event
+         instead of changing volume.</p>
+         <p>Not for use by third-party applications</p> -->
+    <permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi @hide Allows an application to set media key event listener.
+         <p>When it's set, the application will receive the media key event before
+         any other media sessions. If the event is handled by the listener, other sessions
+         cannot get the event.</p>
+         <p>Not for use by third-party applications</p> -->
+    <permission android:name="android.permission.SET_MEDIA_KEY_LISTENER"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- @SystemApi Required to be able to disable the device (very dangerous!).
          <p>Not for use by third-party applications.
          @hide
@@ -3117,7 +3164,7 @@
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
 
 
-    <!-- Allows the holder to access the ephemeral applications on the device.
+    <!-- Allows the holder to access the instant applications on the device.
     @hide -->
     <permission android:name="android.permission.ACCESS_EPHEMERAL_APPS"
             android:protectionLevel="signature" />
diff --git a/core/res/res/anim/app_starting_exit.xml b/core/res/res/anim/app_starting_exit.xml
index aaf7f15..dfa42e2 100644
--- a/core/res/res/anim/app_starting_exit.xml
+++ b/core/res/res/anim/app_starting_exit.xml
@@ -21,8 +21,8 @@
 <alpha
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:detachWallpaper="true"
-    android:interpolator="@interpolator/decelerate_quad"
+    android:interpolator="@interpolator/linear"
     android:fromAlpha="1.0"
     android:toAlpha="0.0"
-    android:duration="160" />
+    android:duration="150" />
 
diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml b/core/res/res/color/text_color_primary.xml
similarity index 61%
copy from packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
copy to core/res/res/color/text_color_primary.xml
index 85573e0..831a9c4 100644
--- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
+++ b/core/res/res/color/text_color_primary.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+        android:alpha="?attr/disabledAlpha"
+        android:color="?attr/colorForeground"/>
+    <item android:alpha="?attr/primaryContentAlpha"
+        android:color="?attr/colorForeground"/>
+</selector>
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index 516f252..182ba24 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2016 The Android Open Source Project
+Copyright (C) 2017 The Android Open Source Project
 
    Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -14,24 +14,27 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="512dp"
-        android:height="512dp"
+        android:width="480dp"
+        android:height="480dp"
         android:viewportWidth="48.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="#FFc7d4b6"
-        android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/>
+        android:pathData="M25.0,25.0m-20.5,0.0a20.5,20.5,0,1,1,41.0,0.0a20.5,20.5,0,1,1,-41.0,0.0"
+        android:fillAlpha="0.066"
+        android:fillColor="#000000"/>
     <path
-        android:fillColor="#FFfbd3cb"
-        android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/>
+        android:pathData="M24.0,24.0m-20.0,0.0a20.0,20.0,0,1,1,40.0,0.0a20.0,20.0,0,1,1,-40.0,0.0"
+        android:fillColor="#FFC107"/>
     <path
-        android:fillColor="#40000000"
-        android:pathData="M44.0,35.5l-12.0,-12.0l0.0,-4.0z"/>
+        android:pathData="M44,24.2010101 L33.9004889,14.101499 L14.101499,33.9004889 L24.2010101,44 C29.2525804,43.9497929 34.2887564,41.9975027 38.1431296,38.1431296 C41.9975027,34.2887564 43.9497929,29.2525804 44,24.2010101 Z"
+        android:fillColor="#FE9F00"/>
     <path
-        android:fillColor="#40000000"
-        android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/>
+        android:pathData="M24.0,24.0m-14.0,0.0a14.0,14.0,0,1,1,28.0,0.0a14.0,14.0,0,1,1,-28.0,0.0"
+        android:fillColor="#FED44F"/>
     <path
-        android:fillColor="#FFe0e0d6"
-        android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/>
+        android:pathData="M37.7829445,26.469236 L29.6578482,18.3441397 L18.3441397,29.6578482 L26.469236,37.7829445 C29.1911841,37.2979273 31.7972024,36.0037754 33.9004889,33.9004889 C36.0037754,31.7972024 37.2979273,29.1911841 37.7829445,26.469236 Z"
+        android:fillColor="#FFC107"/>
+    <path
+        android:pathData="M24.0,24.0m-8.0,0.0a8.0,8.0,0,1,1,16.0,0.0a8.0,8.0,0,1,1,-16.0,0.0"
+        android:fillColor="#FFFFFF"/>
 </vector>
-
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 5043cba..89e42e6 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2016 The Android Open Source Project
+Copyright (C) 2017 The Android Open Source Project
 
    Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -16,21 +16,17 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="24dp"
         android:height="24dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <path
-        android:fillColor="#A0FFFFFF"
-        android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/>
+        android:fillColor="#FF000000"
+        android:pathData="M12.0,12.0m-10.0,0.0a10.0,10.0,0,1,1,20.0,0.0a10.0,10.0,0,1,1,-20.0,0.0"
+        android:fillAlpha="0.25"/>
     <path
-        android:fillColor="#A0FFFFFF"
-        android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/>
+        android:fillColor="#FF000000"
+        android:pathData="M12,22 C6.4771525,22 2,17.5228475 2,12 C2,6.4771525 6.4771525,2 12,2 C17.5228475,2 22,6.4771525 22,12 C22,17.5228475 17.5228475,22 12,22 Z M12,18.5 C15.5898509,18.5 18.5,15.5898509 18.5,12 C18.5,8.41014913 15.5898509,5.5 12,5.5 C8.41014913,5.5 5.5,8.41014913 5.5,12 C5.5,15.5898509 8.41014913,18.5 12,18.5 Z"/>
     <path
-        android:fillColor="#40000000"
-        android:pathData="M44.0,35.5l-12.0,-12.0l0.0,-4.0z"/>
-    <path
-        android:fillColor="#40000000"
-        android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/>
+        android:fillColor="#FF000000"
+        android:pathData="M12,18.5 C8.41014913,18.5 5.5,15.5898509 5.5,12 C5.5,8.41014913 8.41014913,5.5 12,5.5 C15.5898509,5.5 18.5,8.41014913 18.5,12 C18.5,15.5898509 15.5898509,18.5 12,18.5 Z M12,15 C13.6568542,15 15,13.6568542 15,12 C15,10.3431458 13.6568542,9 12,9 C10.3431458,9 9,10.3431458 9,12 C9,13.6568542 10.3431458,15 12,15 Z"
+        android:fillAlpha="0.25"/>
 </vector>
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
index bd1eb41..606b686 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_0_bars.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml b/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
index aedb12c..a4c5bc2 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_1_bar.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
index 6f07cb5..9c27833 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_2_bars.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
index c41a8ca..6d693f1 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_3_bars.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml b/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
index ec0a52f..c48fa36 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_4_bars.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_4k.xml b/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
index 78bd0a0..0868845 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_4k.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_hd.xml b/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
index 78085c2f..657f5ed 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_hd.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_ld.xml b/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
index f660ab7..e2971aa 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_ld.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_signal_wifi_badged_sd.xml b/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
index 43b8653..b073be3 100644
--- a/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
+++ b/core/res/res/drawable/ic_signal_wifi_badged_sd.xml
@@ -13,11 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:viewportWidth="18"
     android:viewportHeight="18"
-    android:width="18dp"
-    android:height="18dp">
+    android:width="26dp"
+    android:height="24dp">
     <group
         android:translateX="386"
         android:translateY="-298">
diff --git a/core/res/res/drawable/ic_wifi_signal_0.xml b/core/res/res/drawable/ic_wifi_signal_0.xml
new file mode 100644
index 0000000..e732a8d
--- /dev/null
+++ b/core/res/res/drawable/ic_wifi_signal_0.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:width="26dp"
+  android:height="24dp"
+  android:viewportWidth="26"
+  android:viewportHeight="24">
+  <path
+    android:fillAlpha="0.3"
+    android:fillColor="#FFFFFF"
+    android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_wifi_signal_1.xml b/core/res/res/drawable/ic_wifi_signal_1.xml
new file mode 100644
index 0000000..3d006953
--- /dev/null
+++ b/core/res/res/drawable/ic_wifi_signal_1.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="26dp"
+    android:height="24dp"
+    android:viewportWidth="26"
+    android:viewportHeight="24">
+    <path
+        android:fillAlpha="0.3"
+        android:fillColor="#FFFFFF"
+        android:pathData="M13.1,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.5,6.5L13.1,22.0L13.1,22.0L13.1,22.0L13.1,22.0L13.1,22.0z"/>
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M13.1,22.0l5.5,-6.8c-0.2,-0.2 -2.3,-1.9 -5.5,-1.9s-5.3,1.8 -5.5,1.9L13.1,22.0L13.1,22.0L13.1,22.0L13.1,22.0L13.1,22.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_wifi_signal_2.xml b/core/res/res/drawable/ic_wifi_signal_2.xml
new file mode 100644
index 0000000..2cce9e9
--- /dev/null
+++ b/core/res/res/drawable/ic_wifi_signal_2.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="26dp"
+    android:height="24dp"
+    android:viewportWidth="26"
+    android:viewportHeight="24">
+    <path
+        android:fillAlpha="0.3"
+        android:fillColor="#FFFFFF"
+        android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M13.0,22.0l7.6,-9.4C20.3,12.4 17.4,10.0 13.0,10.0s-7.3,2.4 -7.6,2.7L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_wifi_signal_3.xml b/core/res/res/drawable/ic_wifi_signal_3.xml
new file mode 100644
index 0000000..d3b3d3a
--- /dev/null
+++ b/core/res/res/drawable/ic_wifi_signal_3.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="26dp"
+    android:height="24dp"
+    android:viewportWidth="26"
+    android:viewportHeight="24">
+    <path
+        android:fillAlpha="0.3"
+        android:fillColor="#FFFFFF"
+        android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M13.0,22.0l9.2,-11.4c-0.4,-0.3 -3.9,-3.2 -9.2,-3.2s-8.9,3.0 -9.2,3.2L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_wifi_signal_4.xml b/core/res/res/drawable/ic_wifi_signal_4.xml
new file mode 100644
index 0000000..aca4551
--- /dev/null
+++ b/core/res/res/drawable/ic_wifi_signal_4.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="26dp"
+    android:height="24dp"
+    android:viewportWidth="26"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
+</vector>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 1f71a18..0dfeb62 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -20,10 +20,11 @@
     android:id="@+id/notification_header"
     android:orientation="horizontal"
     android:layout_width="wrap_content"
-    android:layout_height="53dp"
+    android:layout_height="48dp"
     android:clipChildren="false"
     android:paddingTop="10dp"
-    android:paddingBottom="16dp"
+    android:paddingBottom="11dp"
+    android:layout_marginBottom="5dp"
     android:paddingStart="@dimen/notification_content_margin_start"
     android:paddingEnd="16dp">
     <com.android.internal.widget.CachingIconView
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
new file mode 100644
index 0000000..1ae317c
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:tag="ambient"
+    >
+    <include layout="@layout/notification_template_header" />
+
+    <LinearLayout
+            android:id="@+id/notification_action_list_margin_target"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="top"
+            android:layout_marginTop="@dimen/notification_content_margin_top"
+            android:layout_marginBottom="@dimen/notification_action_list_height"
+            android:clipToPadding="false"
+            android:orientation="vertical">
+
+        <LinearLayout
+            android:id="@+id/notification_main_column"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top"
+            android:paddingStart="@dimen/notification_content_margin_start"
+            android:paddingEnd="@dimen/notification_content_margin_end"
+            android:clipToPadding="false"
+            android:minHeight="@dimen/notification_min_content_height"
+            android:orientation="vertical"
+            >
+            <TextView android:id="@+id/title"
+                android:textAppearance="@style/TextAppearance.Material.Notification.Title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal"
+                android:textSize="20sp"
+                android:textColor="@android:color/white"
+            />
+            <TextView android:id="@+id/text"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:paddingBottom="@dimen/notification_content_margin_bottom"
+                android:textAppearance="@style/TextAppearance.Material.Notification"
+                android:singleLine="false"
+                android:layout_weight="1"
+                android:gravity="top"
+                android:visibility="gone"
+                android:textSize="18sp"
+                android:textColor="@android:color/white"
+                android:layout_marginTop="4dp"
+            />
+        </LinearLayout>
+    </LinearLayout>
+    <include layout="@layout/notification_material_action_list" />
+</FrameLayout>
diff --git a/core/res/res/layout/preference_widget_seekbar.xml b/core/res/res/layout/preference_widget_seekbar.xml
index 05daa1a..0c6dbf1 100644
--- a/core/res/res/layout/preference_widget_seekbar.xml
+++ b/core/res/res/layout/preference_widget_seekbar.xml
@@ -50,7 +50,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textAppearance="?android:attr/textAppearanceLarge"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
 
diff --git a/core/res/res/layout/preference_widget_seekbar_material.xml b/core/res/res/layout/preference_widget_seekbar_material.xml
index f70a472..6c72c9e 100644
--- a/core/res/res/layout/preference_widget_seekbar_material.xml
+++ b/core/res/res/layout/preference_widget_seekbar_material.xml
@@ -51,7 +51,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
 
@@ -60,7 +60,7 @@
             android:layout_height="wrap_content"
             android:layout_below="@android:id/title"
             android:layout_alignStart="@android:id/title"
-            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="4" />
 
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index cc64bb3..c004243 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Het <xliff:g id="LABEL">%1$s</xliff:g> gedeaktiveer"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferensie-oproep"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Nutswenk"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 953d3da..24feab7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1551,7 +1551,7 @@
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"ስራ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2ኛ ስራ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3ኛ ስራ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="lock_to_app_toast" msgid="7693684144593484">"ይህን ማያ ገጽ ለመንቀል ተመለስ እና አጠቃላ እይታን ተጭነው ይያዙ።"</string>
+    <string name="lock_to_app_toast" msgid="7693684144593484">"ይህን ማያ ገጽ ለመንቀል ተመለስ እና አጠቃላይ እይታን ተጭነው ይያዙ።"</string>
     <string name="lock_to_app_toast_locked" msgid="9125176335701699164">"መተግበሪያ ተሰክቷል፦ በዚህ መሣሪያ ላይ ማላቀቅ አይፈቀድም።"</string>
     <string name="lock_to_app_start" msgid="6643342070839862795">"ማያ ገጽ ተሰክቷል"</string>
     <string name="lock_to_app_exit" msgid="8598219838213787430">"ማያ ገጽ ተነቅሏል"</string>
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ተሰናክሏል"</string>
     <string name="conference_call" msgid="3751093130790472426">"የስብሰባ ጥሪ"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"የመሣሪያ ጥቆማ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1a8dc8e..f28d044c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1795,4 +1795,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"تم تعطيل <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"مكالمة جماعية"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"تلميح"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 36377e6..9b55a55 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> deaktiv edildi"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konfrans Zəngi"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 80d81fc..b0c62e9 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1702,4 +1702,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Vidžet <xliff:g id="LABEL">%1$s</xliff:g> je onemogućen"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Objašnjenje"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index e5f42c4..50cfa82 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Адключаны <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Канферэнц-выклік"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Падказка"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 05b8412..4b39129 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>: Деактивирано"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конферентно обаждане"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Подсказка"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index dbe7938..b6f7a12 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"অক্ষম করা <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"কনফারেন্স কল"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"টুলটিপ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index c4a3b8f..71c9f7d 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1704,4 +1704,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Onemogućen <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Savjet za alat"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 671f5d4..4db3b71 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> s\'ha desactivat"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferència"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Descripció emergent"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index fa5b13e..b72ef8b 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – zakázáno"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenční hovor"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Popisek"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index be50bcb..ad01840 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – deaktiveret"</string>
     <string name="conference_call" msgid="3751093130790472426">"Telefonmøde"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Værktøjstip"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 3f64c9b..db19e193 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> deaktiviert"</string>
     <string name="conference_call" msgid="3751093130790472426">"Telefonkonferenz"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Kurzinfo"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 21d42d9..333adaa 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Απενεργοποιημένο <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Κλήση συνδιάσκεψης"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Επεξήγηση εργαλείου"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 531a5ab..91b1620 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 531a5ab..91b1620 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 531a5ab..91b1620 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Disabled <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f888bd1..7db3b4c 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Se inhabilitó <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferencia"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Información sobre la herramienta"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 6d88d6f..39ca2ef 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> inhabilitado"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferencia"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Descripción emergente"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 01edc01..2ccc3eb 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Keelatud <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konverentskõne"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Tööriistavihje"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 3bb4492..c131c42 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> desgaituta dago"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferentzia-deia"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Aholkua"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index cb2f57e..d6ef85a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> غیرفعال شد"</string>
     <string name="conference_call" msgid="3751093130790472426">"تماس کنفرانسی"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"نکته‌ابزار"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a02017a..943b8e7 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ei ole käytössä."</string>
     <string name="conference_call" msgid="3751093130790472426">"Puhelinneuvottelu"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Työkaluvinkki"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index b864e2b..3deefd3 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1551,7 +1551,7 @@
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (travail)"</string>
     <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2e <xliff:g id="LABEL">%1$s</xliff:g> professionnel(le)"</string>
     <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3e <xliff:g id="LABEL">%1$s</xliff:g> professionnel(le)"</string>
-    <string name="lock_to_app_toast" msgid="7693684144593484">"Pour annuler l\'épinglage de cet écran, maintenez enfoncée les touches Retour et Aperçu."</string>
+    <string name="lock_to_app_toast" msgid="7693684144593484">"Pour annuler l\'épinglage de cet écran, maintenez enfoncées les touches Retour et Aperçu."</string>
     <string name="lock_to_app_toast_locked" msgid="9125176335701699164">"L\'application est épinglée : l\'annulation de l\'épinglage n\'est pas autorisée sur cet appareil."</string>
     <string name="lock_to_app_start" msgid="6643342070839862795">"Écran épinglé"</string>
     <string name="lock_to_app_exit" msgid="8598219838213787430">"Épinglage d\'écran annulé"</string>
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Désactivé : <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conférence téléphonique"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Infobulle"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index cd10d36..7abb654 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Élément \"<xliff:g id="LABEL">%1$s</xliff:g>\" désactivé"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conférence téléphonique"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Info-bulle"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index bcf19d9..9b0f666 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -21,7 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="byteShort" msgid="8340973892742019101">"B"</string>
-    <string name="kilobyteShort" msgid="7542884022844556968">"KB"</string>
+    <string name="kilobyteShort" msgid="7542884022844556968">"kB"</string>
     <string name="megabyteShort" msgid="6355851576770428922">"MB"</string>
     <string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
     <string name="terabyteShort" msgid="231613018159186962">"TB"</string>
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Desactivouse <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferencia telefónica"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Cadro de información"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index a79d53c..db02286 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> અક્ષમ કર્યું"</string>
     <string name="conference_call" msgid="3751093130790472426">"કોન્ફરન્સ કૉલ"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ટૂલટિપ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 26a0dcc..29ead1d 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"अक्षम <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"कॉन्फ़्रेंस कॉल"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"टूलटिप"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 683a1a3..8e3ed1c 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1702,4 +1702,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – onemogućeno"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferencijski poziv"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Opis"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3c8e390..f58f365 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"A(z) <xliff:g id="LABEL">%1$s</xliff:g> letiltva"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenciahívás"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Elemleírás"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 91cdfa4..c1702c0 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Անջատած <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Կոնֆերանս զանգ"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Հուշակ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 58447fa..05560d3 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> dinonaktifkan"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferensi Telepon"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Keterangan alat"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0178b77..573d014 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Slökkt <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Símafundur"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Ábending"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 9228b33..74adcca 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> disattivato"</string>
     <string name="conference_call" msgid="3751093130790472426">"Audioconferenza"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Descrizione comando"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index f9e4b34..54c3702 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> הושבת"</string>
     <string name="conference_call" msgid="3751093130790472426">"שיחת ועידה"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"הסבר קצר"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 46c7877..47b0325 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"停止済みの「<xliff:g id="LABEL">%1$s</xliff:g>」"</string>
     <string name="conference_call" msgid="3751093130790472426">"グループ通話"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ツールチップ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 7774c66..fb90136 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"გათიშული <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"საკონფერენციო ზარი"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"მინიშნება"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index d15b017..377f0c9 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> өшірулі"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференциялық қоңырау"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Қалқыма сөзкөмек"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 549bff2..756465c 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1673,4 +1673,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ដែលបានបិទដំណើរការ"</string>
     <string name="conference_call" msgid="3751093130790472426">"ការហៅជាក្រុម"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ផ្ទាំងលោត"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 163d922..8c8ab2f 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="conference_call" msgid="3751093130790472426">"ಕಾನ್ಫರೆನ್ಸ್ ಕರೆ"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ಟೂಲ್‌ಟಿಪ್"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index a943f53..f0a214e 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> 사용 중지됨"</string>
     <string name="conference_call" msgid="3751093130790472426">"다자간 통화"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"도움말"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index cdbb17f..f0f1ef8 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> өчүрүлдү"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференц чалуу"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Калкып чыгуучу кеңеш"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 1b8a7a2..0649961 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"ປິດການນຳໃຊ້ <xliff:g id="LABEL">%1$s</xliff:g> ແລ້ວ"</string>
     <string name="conference_call" msgid="3751093130790472426">"ການປະຊຸມສາຍ"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ຄຳອະທິບາຍເຄື່ອງມື"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1bcb8ca..bd694dd 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1665,7 +1665,7 @@
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Sutraukti"</string>
     <string name="zen_mode_feature_name" msgid="5254089399895895004">"Netrukdyti"</string>
     <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Prastova"</string>
-    <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Savaitgalio vakarą"</string>
+    <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Darbo dienos vakarą"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Savaitgalį"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Įvykis"</string>
     <string name="muted_by" msgid="6147073845094180001">"Nutildė <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Išj. valdiklis „<xliff:g id="LABEL">%1$s</xliff:g>“"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferencinis skambutis"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Patarimas"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 0df18d1..d17846f 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1702,4 +1702,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> atspējots"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferences zvans"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Rīka padoms"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mcc214-mnc01/config.xml b/core/res/res/values-mcc214-mnc01/config.xml
index 895b770..24150a7 100644
--- a/core/res/res/values-mcc214-mnc01/config.xml
+++ b/core/res/res/values-mcc214-mnc01/config.xml
@@ -40,27 +40,4 @@
       <item>INTERNET,airtelnet.es,,,vodafone,vodafone,,,,,214,01,1,DUN</item>
     </string-array>
 
-    <string-array translatable="false" name="config_operatorConsideredNonRoaming">
-        <item>21402</item>
-        <item>21403</item>
-        <item>21404</item>
-        <item>21405</item>
-        <item>21406</item>
-        <item>21407</item>
-        <item>21408</item>
-        <item>21409</item>
-        <item>21410</item>
-        <item>21411</item>
-        <item>21412</item>
-        <item>21413</item>
-        <item>21414</item>
-        <item>21415</item>
-        <item>21416</item>
-        <item>21417</item>
-        <item>21418</item>
-        <item>21419</item>
-        <item>21420</item>
-        <item>21421</item>
-    </string-array>
-
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index a4912c3..7d2ba9d 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1673,4 +1673,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Оневозможен <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференциски повик"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Совет за алатка"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 202e69a..c0be3a4 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="conference_call" msgid="3751093130790472426">"കോൺഫറൻസ് കോൾ"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ടൂൾ ടിപ്പ്"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 1dfb039..9078460 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1669,4 +1669,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g>-г цуцалсан"</string>
     <string name="conference_call" msgid="3751093130790472426">"Хурлын дуудлага"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Зөвлөмж"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 4f4275a..e455178 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1551,7 +1551,7 @@
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_2" msgid="5048136430082124036">"2 रे कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge_3" msgid="2808305070321719040">"3 रे कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="lock_to_app_toast" msgid="7693684144593484">"ही स्क्रीन अनपिन करण्यासाठी, परत आणि विहंगावलोकनास स्पर्श करा आणि धरून ठेवा."</string>
+    <string name="lock_to_app_toast" msgid="7693684144593484">"ही स्क्रीन अनपिन करण्यासाठी, परत जा आणि विहंगावलोकन करा स्पर्श करा आणि धरून ठेवा."</string>
     <string name="lock_to_app_toast_locked" msgid="9125176335701699164">"अॅप पिन केलेला आहे: या डिव्हाइसवर अनपिन करण्यास अनुमती नाही."</string>
     <string name="lock_to_app_start" msgid="6643342070839862795">"स्क्रीन पिन केली"</string>
     <string name="lock_to_app_exit" msgid="8598219838213787430">"स्क्रीन अनपिन केली"</string>
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> अक्षम केले"</string>
     <string name="conference_call" msgid="3751093130790472426">"परिषद कॉल"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"टूलटिप"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 69808069..d41ff77 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> dilumpuhkan"</string>
     <string name="conference_call" msgid="3751093130790472426">"Panggilan Sidang"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Keterangan item"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index bada9a9..291d92d 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"ပိတ်ထားသည့် <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"လူအမြောက်အမြားတပြိုင်နက် ခေါ်ဆိုမှု"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"အကြံပြုချက်ပြ ပေါ့အပ် ဝင်းဒိုး"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d83e61e..70a8828 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> er slått av"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferansesamtale"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Verktøytips"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index a033cba..0653ab7 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1677,4 +1677,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> लाई असक्षम गरियो"</string>
     <string name="conference_call" msgid="3751093130790472426">"सम्मेलन कल"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"उपकरणको वर्णन"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b6e2426..2e55648 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> uitgeschakeld"</string>
     <string name="conference_call" msgid="3751093130790472426">"Telefonische vergadering"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Knopinfo"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 21aeb9d..cff0ddb 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"ਕਾਨਫਰੰਸ ਕਾਲ"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ਟੂਲਟਿਪ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 57d64ad..45b48f9 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Wyłączono: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Połączenie konferencyjne"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Etykietka"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 094b30b..8b50b0f 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
     <string name="conference_call" msgid="3751093130790472426">"Teleconferência"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Dica"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index c0a73a0..736d6e0 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferência"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Sugestão"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 094b30b..8b50b0f 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Widget <xliff:g id="LABEL">%1$s</xliff:g> desativado"</string>
     <string name="conference_call" msgid="3751093130790472426">"Teleconferência"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Dica"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index e809e3b..43b8f1f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1702,4 +1702,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> a fost dezactivat"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conferință telefonică"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Balon explicativ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 44b4c51..cd6c871 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Виджет <xliff:g id="LABEL">%1$s</xliff:g> отключен"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференц-связь"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Подсказка"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 2e49ec2..031d9d0 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1673,4 +1673,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"අබල කළ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"සම්මන්ත්‍රණ ඇමතුම"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"මෙවලම් ඉඟිය"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 71a0373..e483d39 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Deaktivovaná miniaplikácia <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenčný hovor"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Popis"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index fbd2fc2..b74875a 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> – onemogočeno"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenčni klic"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Opis orodja"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 51c4b94..536c60f 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> u çaktivizua"</string>
     <string name="conference_call" msgid="3751093130790472426">"Telefonatë konferencë"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Këshilla për veglën"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index c23044c..c7d8fdf 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1702,4 +1702,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Виџет <xliff:g id="LABEL">%1$s</xliff:g> је онемогућен"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференцијски позив"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Објашњење"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index adfd909..f5e3445 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> har inaktiverats"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferenssamtal"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Beskrivning"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d460fe0..1c91e99 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1669,4 +1669,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> imezimwa"</string>
     <string name="conference_call" msgid="3751093130790472426">"Simu ya Kongamano"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Kidirisha cha vidokezo"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 96a9f48..b04f928 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"முடக்கப்பட்டது: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"குழு அழைப்பு"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"உதவிக்குறிப்பு"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 4b93693..7f2ffdd 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> నిలిపివేయబడింది"</string>
     <string name="conference_call" msgid="3751093130790472426">"కాన్ఫరెన్స్ కాల్"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"సాధనం చిట్కా"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index a46935c..7e2f78c 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"ปิดใช้ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"การประชุมสาย"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"เคล็ดลับเครื่องมือ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index b738ada..f245774 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Na-disable ang <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Conference Call"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Tooltip"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index dea294a..0c3db29 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> devre dışı"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferans Çağrısı"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"İpucu"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 842d06f..19e3798 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1733,4 +1733,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> вимкнено"</string>
     <string name="conference_call" msgid="3751093130790472426">"Конференц-виклик"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Спливаюча підказка"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index bb7a69e..3eb2025 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"غیر فعال کردہ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"کانفرنس کال"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"ٹول ٹپ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index d8b8e7e..c706816 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"<xliff:g id="LABEL">%1$s</xliff:g> vidjeti o‘chirilgan"</string>
     <string name="conference_call" msgid="3751093130790472426">"Konferens-aloqa"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Maslahat oynasi"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9d204c2..a406656 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"Đã tắt <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"Cuộc gọi nhiều bên"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Chú giải công cụ"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6f51494..55773c6 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"已停用的<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"电话会议"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"提示"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index a4723db..300cc36 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"「<xliff:g id="LABEL">%1$s</xliff:g>」已停用"</string>
     <string name="conference_call" msgid="3751093130790472426">"會議通話"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"提示"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index dd37549..ffa10d7 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"已停用的<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="conference_call" msgid="3751093130790472426">"電話會議"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"工具提示"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 9cb03b8..a5da794 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1671,4 +1671,20 @@
     <string name="suspended_widget_accessibility" msgid="6712143096475264190">"I-<xliff:g id="LABEL">%1$s</xliff:g> ekhutshaziwe"</string>
     <string name="conference_call" msgid="3751093130790472426">"Ikholi yengqungquthela"</string>
     <string name="tooltip_popup_title" msgid="5253721848739260181">"Ithulithiphu"</string>
+    <!-- no translation found for app_category_game (5431836943981492993) -->
+    <skip />
+    <!-- no translation found for app_category_audio (1659853108734301647) -->
+    <skip />
+    <!-- no translation found for app_category_video (2728726078629384196) -->
+    <skip />
+    <!-- no translation found for app_category_image (4867854544519846048) -->
+    <skip />
+    <!-- no translation found for app_category_social (5842783057834965912) -->
+    <skip />
+    <!-- no translation found for app_category_news (7496506240743986873) -->
+    <skip />
+    <!-- no translation found for app_category_maps (5878491404538024367) -->
+    <skip />
+    <!-- no translation found for app_category_productivity (3742083261781538852) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index dd33718..11a96f7 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -62,6 +62,8 @@
 
         <!-- Default disabled alpha for widgets that set enabled/disabled alpha programmatically. -->
         <attr name="disabledAlpha" format="float" />
+        <!-- The alpha applied to the foreground color to create the primary text color. -->
+        <attr name="primaryContentAlpha" format="float" />
         <!-- Default background dim amount when a menu, dialog, or something similar pops up. -->
         <attr name="backgroundDimAmount" format="float" />
         <!-- Control whether dimming behind the window is enabled.  The default
@@ -2272,13 +2274,16 @@
         <!-- Sets the padding, in pixels, of the end edge; see {@link android.R.attr#padding}. -->
         <attr name="paddingEnd" format="dimension" />
 
-        <!-- Boolean that controls whether a view can take focus.  By default the user can not
-             move focus to a view; by setting this attribute to true the view is
-             allowed to take focus.  This value does not impact the behavior of
+        <!-- Controls whether a view can take focus.  By default, this is "auto" which lets the
+             framework determine whether a user can move focus to a view.  By setting this attribute
+             to true the view is allowed to take focus. By setting it to "false" the view will not
+             take focus. This value does not impact the behavior of
              directly calling {@link android.view.View#requestFocus}, which will
              always request focus regardless of this view.  It only impacts where
              focus navigation will try to move focus. -->
-        <attr name="focusable" format="boolean" />
+        <attr name="focusable" format="boolean|enum">
+            <enum name="auto" value="0x00000010" />
+        </attr>
 
         <!-- Boolean that controls whether a view can take focus while in touch mode.
              If this is true for a view, that view can gain focus when clicked on, and can keep
@@ -2495,13 +2500,13 @@
         <!-- Sets the id of a view before which this one is visited in accessibility traversal.
              A screen-reader must visit the content of this view before the content of the one
              it precedes.
-             @see android.view.View#setAccessibilityTraversalBefore(int)} -->
+             {@see android.view.View#setAccessibilityTraversalBefore(int)} -->
         <attr name="accessibilityTraversalBefore" format="integer" />
 
         <!-- Sets the id of a view after which this one is visited in accessibility traversal.
              A screen-reader must visit the content of the other view before the content of
              this one.
-             @see android.view.View#setAccessibilityTraversalAfter(int)} -->
+             {@see android.view.View#setAccessibilityTraversalAfter(int)} -->
         <attr name="accessibilityTraversalAfter" format="integer" />
 
         <!-- Name of the method in this View's context to invoke when the view is
@@ -2888,15 +2893,13 @@
         <attr name="forceHasOverlappingRendering" format="boolean" />
 
         <!-- Defines text displayed in a small popup window on hover or long press. -->
-        <attr name="tooltip" format="string" localization="suggested" />
+        <attr name="tooltipText" format="string" localization="suggested" />
 
         <!-- Whether this view is a root of a keyboard navigation cluster.
              See {@link android.view.View#setKeyboardNavigationCluster(boolean)}. -->
         <attr name="keyboardNavigationCluster" format="boolean" />
 
-        <!-- Whether this view is a root of a keyboard navigation section.
-             See {@link android.view.View#setKeyboardNavigationSection(boolean)}. -->
-        <attr name="keyboardNavigationSection" format="boolean" />
+        <attr name="__removed0" format="boolean" />
 
         <!-- Defines the next keyboard navigation cluster.
 
@@ -2905,12 +2908,7 @@
              will result when the reference is accessed.-->
         <attr name="nextClusterForward" format="reference"/>
 
-        <!-- Defines the next keyboard navigation section.
-
-             If the reference refers to a view that does not exist or is part
-             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}
-             will result when the reference is accessed.-->
-        <attr name="nextSectionForward" format="reference"/>
+        <attr name="__removed1" format="reference"/>
 
         <!-- Whether this view is a default-focus view.
              Only one view per keyboard navigation cluster can have this attribute set to true.
@@ -3268,8 +3266,8 @@
         <!-- The unique id for the subtype. The text service (spell checker) framework keeps track
              of enabled subtypes by ID. When the spell checker package gets upgraded, enabled IDs
              will stay enabled even if other attributes are different. If the ID is unspecified or
-             or explicitly specified to 0 in XML resources,
-             {@code Arrays.hashCode(new Object[] {subtypeLocale, extraValue}) will be used instead.
+             explicitly specified to 0 in XML resources,
+             {@code Arrays.hashCode(new Object[] {subtypeLocale, extraValue})} will be used instead.
               -->
         <attr name="subtypeId" />
         <!-- The BCP-47 Language Tag of the subtype.  This replaces
@@ -4705,7 +4703,8 @@
             screens with limited space for text. -->
             <enum name="full" value="2" />
         </attr>
-        <!-- Specify the type of auto-size. -->
+        <!-- Specify the type of auto-size. Note that this feature is not supported by EditText,
+        works only for TextView -->
         <attr name="autoSizeText" format="enum">
             <!-- No auto-sizing (default). -->
             <enum name="none" value="0" />
@@ -5840,6 +5839,14 @@
         <attr name="color" />
     </declare-styleable>
 
+    <!-- Drawable used to draw masked icons with foreground and background layers. -->
+    <declare-styleable name="MaskableIconDrawableLayer">
+        <!-- The color to use for the layer, only if drawable is not defined. -->
+        <attr name="color" />
+        <!-- The drawable to use for the layer. -->
+        <attr name="drawable" />
+     </declare-styleable>
+
     <!-- Drawable used to show animated touch feedback. -->
     <declare-styleable name="RippleDrawable">
         <!-- The color to use for ripple effects. This attribute is required. -->
@@ -7027,7 +7034,7 @@
         <attr name="contentDescription" format="string" />
 
         <!-- The tooltip text associated with the item. -->
-        <attr name="tooltip" format="string" />
+        <attr name="tooltipText" format="string" />
 
     </declare-styleable>
 
@@ -7615,6 +7622,21 @@
     </declare-styleable>
 
     <!-- =============================== -->
+    <!-- AutoFill attributes -->
+    <!-- =============================== -->
+    <eat-comment />
+
+    <!-- Use <code>autofill-service</code> as the root tag of the XML resource that describes a
+         {@link android.service.autofill.AutoFillService}, which is referenced from its
+         {@link android.service.autofill#SERVICE_META_DATA} meta-data entry.
+    -->
+    <declare-styleable name="AutoFillService">
+        <!-- Fully qualified class name of an activity that allows the user to modify
+             the settings for this service. -->
+        <attr name="settingsActivity" />
+    </declare-styleable>
+
+    <!-- =============================== -->
     <!-- Contacts meta-data attributes -->
     <!-- =============================== -->
     <eat-comment />
@@ -7901,7 +7923,6 @@
         <attr name="queryBackground" format="reference" />
         <!-- Background for the section containing the action (e.g. voice search) -->
         <attr name="submitBackground" format="reference" />
-        <attr name="focusable" />
     </declare-styleable>
 
     <declare-styleable name="Switch">
@@ -8428,7 +8449,7 @@
     <!-- @hide Attributes which will be read by the Activity to intialize the
                base activity TaskDescription. -->
     <declare-styleable name="ActivityTaskDescription">
-        <!-- @hide From Theme.colorPrimary, used for the TaskDescription primary 
+        <!-- @hide From Theme.colorPrimary, used for the TaskDescription primary
                    color. -->
         <attr name="colorPrimary" />
         <!-- @hide From Theme.colorBackground, used for the TaskDescription background
@@ -8459,4 +8480,14 @@
         <attr name="font" format="reference" />
         <attr name="fontWeight" format="integer" />
     </declare-styleable>
+
+    <!-- @hide -->
+    <declare-styleable name="RecyclerView">
+        <attr name="layoutManager" format="string" />
+        <attr name="orientation" />
+        <attr name="descendantFocusability" />
+        <attr name="spanCount" format="integer"/>
+        <attr name="reverseLayout" format="boolean" />
+        <attr name="stackFromEnd" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b961394..82e33f8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -776,6 +776,21 @@
         <enum name="locked" value="14" />
     </attr>
 
+    <!-- Specify the configuration changes that trigger the system to restart the
+         current activity if any of these configuration changes happen in the system.
+         The valid configuration changes include mcc and mnc which are the same with
+         those in configChanges. By default from Android O, we don't restart the activity
+         even the app doesn't specify mcc or mnc in configChanges. If the app wants to
+         restart, specify them in restartOnConfigChanges. -->
+    <attr name="restartOnConfigChanges">
+        <!-- The IMSI MCC has changed, that is a SIM has been detected and
+             updated the Mobile Country Code. -->
+        <flag name="mcc" value="0x0001" />
+        <!-- The IMSI MNC has changed, that is a SIM has been detected and
+             updated the Mobile Network Code. -->
+        <flag name="mnc" value="0x0002" />
+    </attr>
+
     <!-- Specify one or more configuration changes that the activity will
          handle itself.  If not specified, the activity will be restarted
          if any of these configuration changes happen in the system.  Otherwise,
@@ -793,10 +808,16 @@
          include/utils/ResourceTypes.h. -->
     <attr name="configChanges">
         <!-- The IMSI MCC has changed, that is a SIM has been detected and
-             updated the Mobile Country Code. -->
+             updated the Mobile Country Code. By default from Android O, we
+             don't restart the activity even the app doesn't specify mcc in
+             configChanges. If the app wants to restart, specify mcc in
+             restartOnConfigChanges. -->
         <flag name="mcc" value="0x0001" />
         <!-- The IMSI MNC has changed, that is a SIM has been detected and
-             updated the Mobile Network Code. -->
+             updated the Mobile Network Code. By default from Android O, we
+             don't restart the activity even the app doesn't specify mnc in
+             configChanges. If the app wants to restart, specify mnc in
+             restartOnConfigChanges. -->
         <flag name="mnc" value="0x0002" />
         <!-- The locale has changed, that is the user has selected a new
              language that text should be displayed in. -->
@@ -843,6 +864,8 @@
         <flag name="density" value="0x1000" />
         <!-- The layout direction has changed. For example going from LTR to RTL. -->
         <flag name="layoutDirection" value="0x2000" />
+        <!-- The color mode of the screen has changed (color gamut or dynamic range). -->
+        <flag name="colorMode" value="0x4000" />
         <!-- The font scaling factor has changed, that is the user has
              selected a new global font size. -->
         <flag name="fontScale" value="0x40000000" />
@@ -978,6 +1001,13 @@
         <enum name="preferExternal" value="2" />
     </attr>
 
+    <!-- If set to <code>true</code>, indicates to the platform that any split APKs
+         installed for this application should be loaded into their own Context
+         objects and not appear in the base application's Context.
+
+         <p>The default value of this attribute is <code>false</code>. -->
+    <attr name="isolatedSplits" format="boolean" />
+
     <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
          {@code <application>} tag. If specified on the {@code <application>}
          tag these will be considered defaults for all activities in the
@@ -1135,18 +1165,15 @@
          resizeable activities when in multi-window mode. -->
     <attr name="resizeableActivity" format="boolean" />
 
-    <!-- Indicates that the activity supports the picture-in-picture (PiP) form of multi-window.
-         While it makes sense to be able to resize most activities types in multi-window mode when
-         {@link android.R.attr#resizeableActivity} is set. It only makes sense to put specific types
-         of activities in PiP mode of multi-window. For example, activities that play video. When
-         set the activity will be allowed to enter PiP mode when the system deems it appropriate on
-         devices that support PiP.
+    <!-- Indicates that the activity specifically supports the picture-in-picture form of
+         multi-window. If true, this activity will support entering picture-in-picture, but will
+         only support split-screen and other forms of multi-window if
+         {@link android.R.attr#resizeableActivity} is also set to true.
 
-         <p>The default value is <code>false</code> for applications with
-         <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#N} and
-         <code>true</code> otherwise.
+         Note that your activity may still be resized even if this attribute is true and
+         {@link android.R.attr#resizeableActivity} is false.
 
-         <p>NOTE: Attribute is only used if {@link android.R.attr#resizeableActivity} is true. -->
+         <p>The default value is <code>false</code>.  -->
     <attr name="supportsPictureInPicture" format="boolean" />
 
     <!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
@@ -1209,13 +1236,17 @@
          -->
     <attr name="autoVerify" format="boolean" />
 
-    <!-- Specify whether a component should be visible to ephemeral apps.
+    <!-- Specify whether a component should be visible to instant apps.
          -->
-    <attr name="visibleToEphemeral" format="boolean" />
+    <attr name="visibleToInstantApps" format="boolean" />
 
     <!-- An XML resource with the application's Network Security Config. -->
     <attr name="networkSecurityConfig" format="reference" />
 
+    <!-- When an application is partitioned into splits, this is the name of the
+         split that contains the defined component. -->
+    <attr name="splitName" format="string" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -1242,6 +1273,7 @@
         <attr name="sharedUserId" />
         <attr name="sharedUserLabel" />
         <attr name="installLocation" />
+        <attr name="isolatedSplits" />
     </declare-styleable>
 
     <!-- The <code>application</code> tag describes application-level components
@@ -1597,6 +1629,27 @@
         <attr name="name" />
     </declare-styleable>
 
+
+    <!-- The <code>static-library</code> tag declares that this apk is providing itself
+       as a static shared library for other applications to use. Any app can declare such
+       a library and there can be only one static shared library per package. These libraries
+       are updatable, multiple versions can be installed at the same time, and an app links
+       against a specific version simulating static linking while allowing code sharing.
+       Other apks can link to it with the {@link #AndroidManifestUsesLibrary uses-static-library}
+       tag.
+
+     <p>This appears as a child tag of the
+     {@link #AndroidManifestApplication application} tag. -->
+    <declare-styleable name="AndroidManifestStaticLibrary" parent="AndroidManifestApplication">
+        <!-- Required public name of the library, which other components and
+        packages will use when referring to this library.  This is a string using
+        Java-style scoping to ensure it is unique.  The name should typically
+        be the same as the apk's package name. -->
+        <attr name="name" />
+        <!-- Required specific library version. -->
+        <attr name="version" />
+    </declare-styleable>
+
     <!-- The <code>uses-libraries</code> specifies a shared library that this
          package requires to be linked against.  Specifying this flag tells the
          system to include this library's code in your class loader.
@@ -1616,6 +1669,24 @@
         <attr name="required" />
     </declare-styleable>
 
+    <!-- The <code>uses-static-library</code> specifies a shared <strong>static</strong>
+         library that this package requires to be statically linked against. Specifying
+         this tag tells the system to include this library's code in your class loader.
+         Depending on a static shared library is equivalent to statically linking with
+         the library at build time while it offers apps to share code defined in such
+         libraries. Hence, static libraries are strictly required.
+
+         <p>This appears as a child tag of the
+         {@link #AndroidManifestApplication application} tag. -->
+    <declare-styleable name="AndroidManifestUsesStaticLibrary" parent="AndroidManifestApplication">
+        <!-- Required name of the library you use. -->
+        <attr name="name" />
+        <!-- Specify which version of the shared library should be statically linked. -->
+        <attr name="version" />
+        <!-- The SHA-256 digest of the library signing certificate. -->
+        <attr name="certDigest" format="string" />
+    </declare-styleable>
+
     <!-- The <code>supports-screens</code> specifies the screen dimensions an
          application supports.  By default a modern application supports all
          screen sizes and must explicitly disable certain screen sizes here;
@@ -1759,7 +1830,9 @@
         <attr name="exported" />
         <attr name="singleUser" />
         <attr name="directBootAware" />
-        <attr name="visibleToEphemeral" />
+        <attr name="visibleToInstantApps" />
+        <!-- The code for this component is located in the given split. -->
+        <attr name="splitName" />
     </declare-styleable>
 
     <!-- Attributes that can be supplied in an AndroidManifest.xml
@@ -1849,7 +1922,9 @@
              client to bind to the service as if it were running it its own package.  The service
              must also be {@link android.R.attr#exported} if this flag is set. -->
         <attr name="externalService" format="boolean" />
-        <attr name="visibleToEphemeral" />
+        <attr name="visibleToInstantApps" />
+        <!-- The code for this component is located in the given split. -->
+        <attr name="splitName" />
     </declare-styleable>
 
     <!-- The <code>receiver</code> tag declares an
@@ -1917,6 +1992,7 @@
         <attr name="launchMode" />
         <attr name="screenOrientation" />
         <attr name="configChanges" />
+        <attr name="restartOnConfigChanges" />
         <attr name="permission" />
         <attr name="multiprocess" />
         <attr name="process" />
@@ -1971,7 +2047,19 @@
              This attribute is ignored if the activity isn't a launcher. -->
         <attr name="onTopLauncher" format="boolean" />
         <attr name="rotationAnimation" />
-        <attr name="visibleToEphemeral" />
+        <attr name="visibleToInstantApps" />
+        <!-- The code for this component is located in the given split. -->
+        <attr name="splitName" />
+        <!-- Specify the color mode the activity desires. The requested color mode may be ignored
+             depending on the capabilities of the display the activity is displayed on. -->
+        <attr name="colorMode">
+            <!-- The default color mode (typically sRGB, low-dynamic range). -->
+            <enum name="default" value="0" />
+            <!-- Wide color gamut color mode. -->
+            <enum name="wideColorGamut" value="1" />
+            <!-- High dynamic range color mode. -->
+            <enum name="hdr" value="2" />
+        </attr>
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
@@ -2285,7 +2373,7 @@
     </declare-styleable>
 
     <!-- Declaration of an {@link android.content.Intent} object in XML.  May
-         also include zero or more {@link #IntentCategory <category> and
+         also include zero or more {@link #IntentCategory <category>} and
          {@link #Extra <extra>} tags. -->
     <declare-styleable name="Intent">
         <!-- The action name to assign to the Intent, as per
@@ -2382,4 +2470,8 @@
         <attr name="hash" format="string" />
     </declare-styleable>
 
+    <declare-styleable name="AndroidManifestUsesSplit" parent="AndroidManifest">
+        <attr name="name" format="string" />
+    </declare-styleable>
+
 </resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 4164e5d..b28c6f2 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -89,7 +89,7 @@
     <color name="perms_dangerous_perm_color">#33b5e5</color>
     <color name="shadow">#cc222222</color>
     <color name="perms_costs_money">#fff4511e</color>
-    
+
     <!-- For search-related UIs -->
     <color name="search_url_text_normal">#7fa87f</color>
     <color name="search_url_text_selected">@android:color/black</color>
@@ -132,6 +132,10 @@
     <drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
     <drawable name="notification_template_divider">#29000000</drawable>
     <drawable name="notification_template_divider_media">#29ffffff</drawable>
+    <color name="notification_primary_text_color_light">@color/primary_text_default_material_light</color>
+    <color name="notification_primary_text_color_dark">@color/primary_text_default_material_dark</color>
+    <color name="notification_secondary_text_color_light">@color/secondary_text_material_light</color>
+    <color name="notification_secondary_text_color_dark">@color/secondary_text_material_dark</color>
 
     <color name="notification_material_background_color">#ffffffff</color>
 
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 8f0350a..e2498df7 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -20,11 +20,14 @@
     <color name="primary_device_default_dark">@color/primary_material_dark</color>
     <color name="primary_device_default_light">@color/primary_material_light</color>
     <color name="primary_device_default_settings">@color/primary_material_settings</color>
+    <color name="primary_device_default_settings_light">@color/primary_material_settings_light</color>
     <color name="primary_dark_device_default_dark">@color/primary_dark_material_dark</color>
     <color name="primary_dark_device_default_light">@color/primary_dark_material_light</color>
     <color name="primary_dark_device_default_settings">@color/primary_dark_material_settings</color>
+    <color name="primary_dark_device_default_settings_light">@color/primary_dark_material_settings_light</color>
 
     <color name="secondary_device_default_settings">@color/secondary_material_settings</color>
+    <color name="secondary_device_default_settings_light">@color/secondary_material_settings_light</color>
     <color name="tertiary_device_default_settings">@color/tertiary_material_settings</color>
     <color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
 
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 40e7341..db89c22 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -27,12 +27,15 @@
     <color name="primary_material_dark">@color/material_grey_900</color>
     <color name="primary_material_light">@color/material_grey_100</color>
     <color name="primary_material_settings">@color/material_blue_grey_900</color>
+    <color name="primary_material_settings_light">@color/material_grey_100</color>
     <color name="primary_dark_material_dark">@color/black</color>
     <color name="primary_dark_material_light">@color/material_grey_600</color>
     <color name="primary_dark_material_light_light_status_bar">@color/material_grey_300</color>
     <color name="primary_dark_material_settings">@color/material_blue_grey_950</color>
+    <color name="primary_dark_material_settings_light">@color/material_grey_300</color>
 
     <color name="secondary_material_settings">@color/material_blue_grey_800</color>
+    <color name="secondary_material_settings_light">@color/material_grey_200</color>
     <color name="tertiary_material_settings">@color/material_blue_grey_700</color>
     <color name="quaternary_material_settings">@color/material_blue_grey_200</color>
 
@@ -73,6 +76,8 @@
 
     <item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item>
     <item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
+    <item name="primary_content_alpha_material_light" format="float" type="dimen">1</item>
+    <item name="primary_content_alpha_material_dark" format="float" type="dimen">0.87</item>
 
     <item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item>
     <item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
@@ -86,6 +91,7 @@
     <color name="material_grey_800">#ff424242</color>
     <color name="material_grey_600">#ff757575</color>
     <color name="material_grey_300">#ffe0e0e0</color>
+    <color name="material_grey_200">#ffeeeeee</color>
     <color name="material_grey_100">#fff5f5f5</color>
     <color name="material_grey_50">#fffafafa</color>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index db157bf..6a8b556 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2090,6 +2090,7 @@
         <item>com.android.server.notification.ImportanceExtractor</item>
         <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
         <item>com.android.server.notification.VisibilityExtractor</item>
+        <item>com.android.server.notification.BadgeExtractor</item>
     </string-array>
 
     <!-- Flag indicating that this device does not rotate and will always remain in its default
@@ -2624,8 +2625,14 @@
     <!-- Component that is the default launcher when demo mode is enabled. -->
     <string name="config_demoModeLauncherComponent">com.android.retaildemo/.DemoPlayer</string>
 
-    <!-- Hashed password (SHA-256) used to restrict demo mode operation -->
-    <string name="config_demoModePassword" translatable="false"></string>
+    <!-- Hashed password (SHA-256) used to restrict carrier demo mode operation. -->
+    <string name="config_carrierDemoModePassword" translatable="false"></string>
+
+    <!-- Secure setting used to activate carrier demo mode. -->
+    <string name="config_carrierDemoModeSetting" translatable="false"></string>
+
+    <!-- List of packages to enable in carrier demo mode (comma separated). -->
+    <string name="config_carrierDemoModePackages" translatable="false"></string>
 
     <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
     <bool name="config_useRoundIcon">false</bool>
@@ -2710,4 +2717,13 @@
 
     <!-- Component name of the default cell broadcast receiver -->
     <string name="config_defaultCellBroadcastReceiverComponent" translatable="false">com.android.cellbroadcastreceiver/.PrivilegedCellBroadcastReceiver</string>
+
+    <!-- Specifies the path that is used by MaskableIconDrawable class to crop launcher icons. -->
+    <string name="config_icon_mask" translatable="false">"M50,0L100,0 100,100 0,100 0,0z"</string>
+
+    <!-- The component name, flattened to a string, for the default accessibility service to be
+         enabled by the accessibility shortcut. This service must be trusted, as it can be activated
+         without explicit consent of the user. If no accessibility service with the specified name
+         exists on the device, the accessibility shortcut will be disabled by default. -->
+    <string name="config_defaultAccessibilityService" translatable="false"></string>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index bd19521..e6358a3 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -509,4 +509,10 @@
     <dimen name="tooltip_precise_anchor_threshold">96dp</dimen>
     <!-- Extra tooltip offset used when anchoring to the mouse/touch position -->
     <dimen name="tooltip_precise_anchor_extra_offset">8dp</dimen>
+
+    <!-- The max amount of scroll ItemTouchHelper will trigger if dragged view is out of
+         RecyclerView's bounds.-->
+    <dimen name="item_touch_helper_max_drag_scroll_per_frame">20dp</dimen>
+    <dimen name="item_touch_helper_swipe_escape_velocity">120dp</dimen>
+    <dimen name="item_touch_helper_swipe_escape_max_velocity">800dp</dimen>
 </resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 5547706..613616f 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -97,6 +97,7 @@
   <item type="id" name="redo" />
   <item type="id" name="replaceText" />
   <item type="id" name="shareText" />
+  <item type="id" name="textAssist" />
   <item type="id" name="selection_start_handle" />
   <item type="id" name="selection_end_handle" />
   <item type="id" name="insertion_handle" />
@@ -131,4 +132,7 @@
   <item type="id" name="cross_task_transition" />
 
   <item type="id" name="accessibilityActionClickOnClickableSpan" />
+
+  <!-- ItemTouchHelper uses this id to save a View's original elevation. -->
+  <item type="id" name="item_touch_helper_previous_elevation"/>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 064d31e..1146871 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2764,7 +2764,7 @@
         <public name="fontStyle" />
         <public name="font" />
         <public name="fontWeight" />
-        <public name="tooltip" />
+        <public name="tooltipText" />
         <public name="autoSizeText" />
         <public name="autoSizeStepGranularity" />
         <public name="autoSizeStepSizeSet" />
@@ -2775,24 +2775,32 @@
         <public name="layout_marginVertical" />
         <public name="paddingHorizontal" />
         <public name="paddingVertical" />
-        <public name="visibleToEphemeral" />
+        <public name="visibleToInstantApps" />
         <public name="keyboardNavigationCluster" />
-        <public name="keyboardNavigationSection" />
+        <public name="__removed0" />
         <public name="nextClusterForward" />
-        <public name="nextSectionForward" />
+        <public name="__removed1" />
         <public name="textColorError" />
         <public name="focusedByDefault" />
         <public name="appCategory" />
         <public name="autoSizeMaxTextSize" />
         <public name="supportsDismissingWindow" />
+        <public name="restartOnConfigChanges" />
+        <public name="certDigest" />
+        <public name="splitName" />
+        <public name="colorMode" />
+        <public name="isolatedSplits" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
     </public-group>
 
     <public-group type="id" first-id="0x01020041">
+        <public name="textAssist" />
     </public-group>
 
+    <public type="attr" name="primaryContentAlpha" />
+
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 87a4732..ac8c896 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -397,7 +397,7 @@
     <string name="network_logging_notification_title">Network traffic is being monitored</string>
     <!-- Content text for a notification. Tapping opens a dialog with more information on network
         logging. [CHAR LIMIT=NONE]-->
-    <string name="network_logging_notification_text">Tap for more details</string>
+    <string name="network_logging_notification_text">Tap to learn more</string>
 
     <!-- Factory reset warning dialog strings--> <skip />
     <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
@@ -578,6 +578,9 @@
     <!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
     <string name="notification_header_divider_symbol" translatable="false">•</string>
 
+    <!-- The divider symbol between different parts of the notification header including spaces. not translatable [CHAR LIMIT=3] -->
+    <string name="notification_header_divider_symbol_with_spaces" translatable="false">" • "</string>
+
     <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
     <string name="notification_hidden_text">Contents hidden</string>
 
@@ -1019,6 +1022,14 @@
       phone number and device IDs, whether a call is active, and the remote number
       connected by a call.</string>
 
+    <!-- Title of an application permission.  When granted the user is giving access to a third
+         party app to route its calls through the system. -->
+    <string name="permlab_manageOwnCalls">route calls through the system</string>
+    <!-- Description of an application permission.  When granted the user is giving access to a
+         third party app to route its calls through the system. -->
+    <string name="permdesc_manageOwnCalls">Allows the app to route its calls through the system in
+        order to improve the calling experience.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readPhoneNumber">read phone number</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2581,6 +2592,15 @@
     <!-- Title for EditText context menu [CHAR LIMIT=20] -->
     <string name="editTextMenuTitle">Text actions</string>
 
+    <!-- Label for item in the text selection menu to trigger an Email app [CHAR LIMIT=20] -->
+    <string name="email">Email</string>
+
+    <!-- Label for item in the text selection menu to trigger a Dialer app [CHAR LIMIT=20] -->
+    <string name="dial">Dial</string>
+
+    <!-- Label for item in the text selection menu to trigger a Map app [CHAR LIMIT=20] -->
+    <string name="map">Map</string>
+
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Storage space running out</string>
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the message of that notification. -->
@@ -3182,6 +3202,11 @@
     <!-- Description of an application permission that lets it read install sessions. -->
     <string name="permdesc_requestInstallPackages">Allows an application to request installation of packages.</string>
 
+    <!-- Title of an application permission that lets it read install sessions. -->
+    <string name="permlab_requestDeletePackages">request delete packages</string>
+    <!-- Description of an application permission that lets it read install sessions. -->
+    <string name="permdesc_requestDeletePackages">Allows an application to request deletion of packages.</string>
+
     <!-- Title of an application permission that lets it ask user to ignore battery optimizations for that app. -->
     <string name="permlab_requestIgnoreBatteryOptimizations">ask to ignore battery optimizations</string>
     <!-- Description of an application permission that lets it ask user to ignore battery optimizations for that app-->
@@ -3818,12 +3843,35 @@
        "Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."
     </string>
 
-    <!-- Text spoken when the user is performing a gesture that will enable accessibility. [CHAR LIMIT=none] -->
-    <string name="continue_to_enable_accessibility">Keep holding down two fingers to enable accessibility.</string>
-    <!-- Text spoken when the user enabled accessibility. [CHAR LIMIT=none] -->
-    <string name="accessibility_enabled">Accessibility enabled.</string>
-    <!-- Text spoken when the user stops preforming a gesture that would enable accessibility. [CHAR LIMIT=none] -->
-    <string name="enable_accessibility_canceled">Accessibility canceled.</string>
+    <!-- Dialog title for dialog shown when the accessibility shortcut is activated, and we want
+     to confirm that the user understands what's going to happen-->
+    <string name="accessibility_shortcut_warning_dialog_title">Accessibility Shortcut is ON</string>
+
+    <!-- Message shown in dialog when user is in the process of enabling the accessibility
+    service via the volume buttons shortcut for the first time. [CHAR LIMIT=none] -->
+    <string name="accessibility_shortcut_toogle_warning">
+        Turn <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> on or off by holding down
+        both volume buttons for 3 seconds.\n\nYou can change the service in
+        Settings > Accessibility.
+    </string>
+
+    <!-- Text in button that turns off the accessibility shortcut -->
+    <string name="disable_accessibility_shortcut">Turn Off Shortcut</string>
+
+    <!-- Text in button that closes the warning dialog about the accessibility shortcut, leaving the
+    shortcut enabled.-->
+    <string name="leave_accessibility_shortcut_on">Leave on</string>
+
+    <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility
+    service.-->
+    <string name="accessibility_shortcut_enabling_service">Accessibility Shortcut turned
+        <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> on</string>
+
+    <!-- Text in toast to alert the user that the accessibility shortcut turned off an accessibility
+    service.-->
+    <string name="accessibility_shortcut_disabling_service">Accessibility Shortcut turned
+        <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> off</string>
+
     <!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
     <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
     <!-- Message shown when switching to a user [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 842b575..faf451b 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1290,7 +1290,7 @@
     <style name="Widget.ActionButton.Overflow">
         <item name="src">@drawable/ic_menu_more</item>
         <item name="contentDescription">@string/action_menu_overflow_description</item>
-        <item name="tooltip">@string/action_menu_overflow_description</item>
+        <item name="tooltipText">@string/action_menu_overflow_description</item>
     </style>
 
     <style name="Widget.ActionButton.CloseMode">
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index 12b8164..dc32b26 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -660,7 +660,7 @@
         <item name="src">@drawable/ic_menu_moreoverflow_holo_dark</item>
         <item name="background">?attr/actionBarItemBackground</item>
         <item name="contentDescription">@string/action_menu_overflow_description</item>
-        <item name="tooltip">@string/action_menu_overflow_description</item>
+        <item name="tooltipText">@string/action_menu_overflow_description</item>
     </style>
 
     <style name="Widget.Holo.ActionButton.TextButton" parent="Widget.Holo.ButtonBar.Button" />
@@ -995,7 +995,7 @@
     <style name="Widget.Holo.Light.ActionButton.Overflow">
         <item name="src">@drawable/ic_menu_moreoverflow_holo_light</item>
         <item name="contentDescription">@string/action_menu_overflow_description</item>
-        <item name="tooltip">@string/action_menu_overflow_description</item>
+        <item name="tooltipText">@string/action_menu_overflow_description</item>
     </style>
 
     <style name="Widget.Holo.Light.ActionBar.TabView" parent="Widget.Holo.ActionBar.TabView" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 709200e..1e15348 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -451,14 +451,14 @@
     </style>
 
     <style name="TextAppearance.Material.Notification">
-        <item name="textColor">@color/secondary_text_material_light</item>
+        <item name="textColor">@color/notification_secondary_text_color_light</item>
         <item name="textSize">@dimen/notification_text_size</item>
     </style>
 
     <style name="TextAppearance.Material.Notification.Reply" />
 
     <style name="TextAppearance.Material.Notification.Title">
-        <item name="textColor">@color/primary_text_default_material_light</item>
+        <item name="textColor">@color/notification_primary_text_color_light</item>
         <item name="textSize">@dimen/notification_title_text_size</item>
     </style>
 
@@ -467,7 +467,7 @@
     </style>
 
     <style name="TextAppearance.Material.Notification.Info">
-        <item name="textColor">@color/secondary_text_default_material_light</item>
+        <item name="textColor">@color/notification_secondary_text_color_light</item>
         <item name="textSize">@dimen/notification_subtext_size</item>
     </style>
 
@@ -944,7 +944,7 @@
         <item name="src">@drawable/ic_menu_moreoverflow_material</item>
         <item name="background">?attr/actionBarItemBackground</item>
         <item name="contentDescription">@string/action_menu_overflow_description</item>
-        <item name="tooltip">@string/action_menu_overflow_description</item>
+        <item name="tooltipText">@string/action_menu_overflow_description</item>
         <item name="minWidth">@dimen/action_button_min_width_overflow_material</item>
         <item name="minHeight">@dimen/action_button_min_height_material</item>
         <item name="paddingStart">@dimen/action_bar_overflow_padding_start_material</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d46bdcf..a732998 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -476,6 +476,9 @@
   <java-symbol type="string" name="replace" />
   <java-symbol type="string" name="undo" />
   <java-symbol type="string" name="redo" />
+  <java-symbol type="string" name="email" />
+  <java-symbol type="string" name="dial" />
+  <java-symbol type="string" name="map" />
   <java-symbol type="string" name="textSelectionCABTitle" />
   <java-symbol type="string" name="BaMmi" />
   <java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -1133,13 +1136,14 @@
   <java-symbol type="string" name="config_ethernet_tcp_buffers" />
   <java-symbol type="string" name="config_wifi_tcp_buffers" />
   <java-symbol type="string" name="config_demoModeLauncherComponent" />
-  <java-symbol type="string" name="config_demoModePassword" />
+  <java-symbol type="string" name="config_carrierDemoModePassword" />
+  <java-symbol type="string" name="config_carrierDemoModeSetting" />
+  <java-symbol type="string" name="config_carrierDemoModePackages" />
   <java-symbol type="string" name="demo_starting_message" />
   <java-symbol type="string" name="demo_restarting_message" />
   <java-symbol type="string" name="conference_call" />
   <java-symbol type="string" name="tooltip_popup_title" />
 
-
   <java-symbol type="plurals" name="bugreport_countdown" />
   <java-symbol type="plurals" name="last_num_days" />
   <java-symbol type="plurals" name="matches_found" />
@@ -1252,6 +1256,11 @@
   <java-symbol type="drawable" name="platlogo" />
   <java-symbol type="drawable" name="stat_notify_sync_error" />
   <java-symbol type="drawable" name="stat_notify_wifi_in_range" />
+  <java-symbol type="drawable" name="ic_wifi_signal_0" />
+  <java-symbol type="drawable" name="ic_wifi_signal_1" />
+  <java-symbol type="drawable" name="ic_wifi_signal_2" />
+  <java-symbol type="drawable" name="ic_wifi_signal_3" />
+  <java-symbol type="drawable" name="ic_wifi_signal_4" />
   <java-symbol type="drawable" name="ic_signal_wifi_badged_0_bars" />
   <java-symbol type="drawable" name="ic_signal_wifi_badged_1_bar" />
   <java-symbol type="drawable" name="ic_signal_wifi_badged_2_bars" />
@@ -2772,6 +2781,8 @@
 
   <java-symbol type="dimen" name="config_appTransitionAnimationDurationScaleDefault" />
 
+  <java-symbol type="layout" name="notification_template_material_ambient" />
+
   <!-- Network Recommendation -->
   <java-symbol type="array" name="config_networkRecommendationPackageNames" />
 
@@ -2782,8 +2793,14 @@
   <java-symbol type="drawable" name="lockscreen_notselected" />
   <java-symbol type="drawable" name="lockscreen_selected" />
 
+  <java-symbol type="string" name="notification_header_divider_symbol_with_spaces" />
   <java-symbol type="string" name="config_defaultCellBroadcastReceiverComponent" />
 
+  <java-symbol type="color" name="notification_primary_text_color_light" />
+  <java-symbol type="color" name="notification_primary_text_color_dark" />
+  <java-symbol type="color" name="notification_secondary_text_color_light" />
+  <java-symbol type="color" name="notification_secondary_text_color_dark" />
+
   <java-symbol type="string" name="app_category_game" />
   <java-symbol type="string" name="app_category_audio" />
   <java-symbol type="string" name="app_category_video" />
@@ -2795,4 +2812,22 @@
 
   <java-symbol type="raw" name="fallback_categories" />
 
+  <java-symbol type="string" name="config_icon_mask" />
+
+  <java-symbol type="attr" name="primaryContentAlpha" />
+
+  <!-- Accessibility Shortcut -->
+  <java-symbol type="string" name="accessibility_shortcut_warning_dialog_title" />
+  <java-symbol type="string" name="accessibility_shortcut_toogle_warning" />
+  <java-symbol type="string" name="accessibility_shortcut_enabling_service" />
+  <java-symbol type="string" name="accessibility_shortcut_disabling_service" />
+  <java-symbol type="string" name="disable_accessibility_shortcut" />
+  <java-symbol type="string" name="leave_accessibility_shortcut_on" />
+  <java-symbol type="string" name="config_defaultAccessibilityService" />
+
+  <!-- com.android.internal.widget.RecyclerView -->
+  <java-symbol type="id" name="item_touch_helper_previous_elevation"/>
+  <java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>
+  <java-symbol type="dimen" name="item_touch_helper_swipe_escape_velocity"/>
+  <java-symbol type="dimen" name="item_touch_helper_swipe_escape_max_velocity"/>
 </resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 6bd6e1c..5961cb0 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -736,20 +736,12 @@
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
     <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
         <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_settings</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
-        <item name="colorSecondary">@color/secondary_device_default_settings</item>
+        <item name="colorPrimary">@color/primary_device_default_settings_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
+        <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
     </style>
 
-    <!-- @hide DeviceDefault theme that should look like the Settings app. The action bar will use a
-         light background and accent color for text and icon. -->
-    <style name="Theme.DeviceDefault.Settings.LightActionBar">
-        <!-- ActionBar -->
-        <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
-        <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar</item>
-    </style>
-
     <!-- @hide DeviceDefault theme for a window that should use Settings theme colors
          but has a full dark palette. ONLY USED FOR QUICK SETTINGS THEME -->
     <style name="Theme.DeviceDefault.QuickSettings" parent="Theme.Material">
@@ -817,12 +809,6 @@
 
     <style name="ThemeOverlay.DeviceDefault" />
 
-    <!-- @hide Theme overlay that inherits from material actionbar,  and use accent color for
-             primary text -->
-    <style name="ThemeOverlay.DeviceDefault.ActionBar.Accent" parent="ThemeOverlay.Material.ActionBar">
-        <item name="textColorPrimary">@color/btn_colored_borderless_text_material</item>
-    </style>
-
     <style name="ThemeOverlay.DeviceDefault.Accent">
         <item name="colorAccent">@color/accent_device_default_dark</item>
     </style>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 5f0ad8e..3587fec 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -48,13 +48,14 @@
         <item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
         <item name="disabledAlpha">@dimen/disabled_alpha_material_dark</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_material_dark</item>
         <item name="backgroundDimAmount">0.6</item>
 
         <!-- Text styles -->
         <item name="textAppearance">@style/TextAppearance.Material</item>
         <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
 
-        <item name="textColorPrimary">@color/primary_text_material_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary</item>
         <item name="textColorPrimaryInverse">@color/primary_text_material_light</item>
         <item name="textColorPrimaryActivated">@color/primary_text_inverse_when_activated_material</item>
         <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
@@ -413,13 +414,14 @@
         <item name="colorBackgroundFloating">@color/background_floating_material_light</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
         <item name="disabledAlpha">@dimen/disabled_alpha_material_light</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_material_light</item>
         <item name="backgroundDimAmount">0.6</item>
 
         <!-- Text styles -->
         <item name="textAppearance">@style/TextAppearance.Material</item>
         <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
 
-        <item name="textColorPrimary">@color/primary_text_material_light</item>
+        <item name="textColorPrimary">@color/text_color_primary</item>
         <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
         <item name="textColorPrimaryActivated">@color/primary_text_inverse_when_activated_material</item>
         <item name="textColorSecondary">@color/secondary_text_material_light</item>
@@ -805,7 +807,7 @@
         <item name="colorBackgroundFloating">@color/background_floating_material_light</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
 
-        <item name="textColorPrimary">@color/primary_text_material_light</item>
+        <item name="textColorPrimary">@color/text_color_primary</item>
         <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
         <item name="textColorSecondary">@color/secondary_text_material_light</item>
         <item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item>
@@ -839,7 +841,7 @@
         <item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
 
-        <item name="textColorPrimary">@color/primary_text_material_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary</item>
         <item name="textColorPrimaryInverse">@color/primary_text_material_light</item>
         <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
         <item name="textColorSecondary">@color/secondary_text_material_dark</item>
@@ -1321,10 +1323,10 @@
     </style>
 
     <!-- Default theme for Settings and activities launched from Settings. -->
-    <style name="Theme.Material.Settings" parent="Theme.Material.Light.DarkActionBar">
-        <item name="colorPrimary">@color/primary_material_settings</item>
-        <item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
-        <item name="colorSecondary">@color/secondary_material_settings</item>
+    <style name="Theme.Material.Settings" parent="Theme.Material.Light.LightStatusBar">
+        <item name="colorPrimary">@color/primary_material_settings_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_material_settings_light</item>
+        <item name="colorSecondary">@color/secondary_material_settings_light</item>
 
         <item name="presentationTheme">@style/Theme.Material.Settings.Dialog.Presentation</item>
         <item name="searchDialogTheme">@style/Theme.Material.Settings.SearchBar</item>
diff --git a/core/tests/ConnectivityManagerTest/Android.mk b/core/tests/ConnectivityManagerTest/Android.mk
index 56011f7..39cf4a4 100644
--- a/core/tests/ConnectivityManagerTest/Android.mk
+++ b/core/tests/ConnectivityManagerTest/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/core/tests/bandwidthtests/Android.mk b/core/tests/bandwidthtests/Android.mk
index cb44721..2af92df 100644
--- a/core/tests/bandwidthtests/Android.mk
+++ b/core/tests/bandwidthtests/Android.mk
@@ -23,6 +23,7 @@
 	$(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner org.apache.http.legacy
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 LOCAL_PACKAGE_NAME := BandwidthTests
 
 include $(BUILD_PACKAGE)
diff --git a/core/tests/bluetoothtests/Android.mk b/core/tests/bluetoothtests/Android.mk
index 4a1d18c..f53419a 100644
--- a/core/tests/bluetoothtests/Android.mk
+++ b/core/tests/bluetoothtests/Android.mk
@@ -9,6 +9,7 @@
 	$(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 LOCAL_PACKAGE_NAME := BluetoothTests
 LOCAL_CERTIFICATE := platform
 
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index cba485a..91ce7a4 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1168,6 +1168,7 @@
         <activity android:name="android.app.EmptyActivity">
         </activity>
         <activity android:name="com.android.internal.app.ChooserWrapperActivity"/>
+        <activity android:name="com.android.internal.app.ResolverWrapperActivity"/>
 
         <receiver android:name="android.app.activity.AbortReceiver">
             <intent-filter android:priority="1">
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
new file mode 100644
index 0000000..f60bf94
--- /dev/null
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static android.os.storage.VolumeInfo.STATE_MOUNTED;
+import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
+
+public class ApplicationPackageManagerTest extends TestCase {
+    private static final String sInternalVolPath = "/data";
+    private static final String sAdoptedVolPath = "/mnt/expand/123";
+    private static final String sPublicVolPath = "/emulated";
+    private static final String sPrivateUnmountedVolPath = "/private";
+
+    private static final String sInternalVolUuid = null; //StorageManager.UUID_PRIVATE_INTERNAL
+    private static final String sAdoptedVolUuid = "adopted";
+    private static final String sPublicVolUuid = "emulated";
+    private static final String sPrivateUnmountedVolUuid = "private";
+
+    private static final VolumeInfo sInternalVol = new VolumeInfo("private",
+            VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
+
+    private static final VolumeInfo sAdoptedVol = new VolumeInfo("adopted",
+            VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
+
+    private static final VolumeInfo sPublicVol = new VolumeInfo("public",
+            VolumeInfo.TYPE_PUBLIC, null /*DiskInfo*/, null /*partGuid*/);
+
+    private static final VolumeInfo sPrivateUnmountedVol = new VolumeInfo("private2",
+            VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
+
+    private static final List<VolumeInfo> sVolumes = new ArrayList<>();
+
+    static {
+        sInternalVol.path = sInternalVolPath;
+        sInternalVol.state = STATE_MOUNTED;
+        sInternalVol.fsUuid = sInternalVolUuid;
+
+        sAdoptedVol.path = sAdoptedVolPath;
+        sAdoptedVol.state = STATE_MOUNTED;
+        sAdoptedVol.fsUuid = sAdoptedVolUuid;
+
+        sPublicVol.state = STATE_MOUNTED;
+        sPublicVol.path = sPublicVolPath;
+        sPublicVol.fsUuid = sPublicVolUuid;
+
+        sPrivateUnmountedVol.state = STATE_UNMOUNTED;
+        sPrivateUnmountedVol.path = sPrivateUnmountedVolPath;
+        sPrivateUnmountedVol.fsUuid = sPrivateUnmountedVolUuid;
+
+        sVolumes.add(sInternalVol);
+        sVolumes.add(sAdoptedVol);
+        sVolumes.add(sPublicVol);
+        sVolumes.add(sPrivateUnmountedVol);
+    }
+
+    private static final class MockedApplicationPackageManager extends ApplicationPackageManager {
+        private boolean mForceAllowOnExternal = false;
+        private boolean mAllow3rdPartyOnInternal = true;
+
+        public MockedApplicationPackageManager() {
+            super(null, null);
+        }
+
+        public void setForceAllowOnExternal(boolean forceAllowOnExternal) {
+            mForceAllowOnExternal = forceAllowOnExternal;
+        }
+
+        public void setAllow3rdPartyOnInternal(boolean allow3rdPartyOnInternal) {
+            mAllow3rdPartyOnInternal = allow3rdPartyOnInternal;
+        }
+
+        @Override
+        public boolean isForceAllowOnExternal(Context context) {
+            return mForceAllowOnExternal;
+        }
+
+        @Override
+        public boolean isAllow3rdPartyOnInternal(Context context) {
+            return mAllow3rdPartyOnInternal;
+        }
+    }
+
+    private StorageManager getMockedStorageManager() {
+        StorageManager storageManager = Mockito.mock(StorageManager.class);
+        Mockito.when(storageManager.getVolumes()).thenReturn(sVolumes);
+        Mockito.when(storageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL))
+                .thenReturn(sInternalVol);
+        Mockito.when(storageManager.findVolumeByUuid(sAdoptedVolUuid))
+                .thenReturn(sAdoptedVol);
+        Mockito.when(storageManager.findVolumeByUuid(sPublicVolUuid))
+                .thenReturn(sPublicVol);
+        Mockito.when(storageManager.findVolumeByUuid(sPrivateUnmountedVolUuid))
+                .thenReturn(sPrivateUnmountedVol);
+        return storageManager;
+    }
+
+    private void verifyReturnedVolumes(List<VolumeInfo> actualVols, VolumeInfo... exptectedVols) {
+        boolean failed = false;
+        if (actualVols.size() != exptectedVols.length) {
+            failed = true;
+        } else {
+            for (VolumeInfo vol : exptectedVols) {
+                if (!actualVols.contains(vol)) {
+                    failed = true;
+                    break;
+                }
+            }
+        }
+
+        if (failed) {
+            fail("Wrong volumes returned.\n Expected: " + Arrays.toString(exptectedVols)
+                    + "\n Actual: " + Arrays.toString(actualVols.toArray()));
+        }
+    }
+
+    public void testGetCandidateVolumes_systemApp() throws Exception {
+        ApplicationInfo sysAppInfo = new ApplicationInfo();
+        sysAppInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+        StorageManager storageManager = getMockedStorageManager();
+        IPackageManager pm = Mockito.mock(IPackageManager.class);
+
+        MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
+
+        appPkgMgr.setAllow3rdPartyOnInternal(true);
+        appPkgMgr.setForceAllowOnExternal(true);
+        List<VolumeInfo> candidates =
+                appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm);
+        verifyReturnedVolumes(candidates, sInternalVol);
+
+        appPkgMgr.setAllow3rdPartyOnInternal(true);
+        appPkgMgr.setForceAllowOnExternal(false);
+        candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm);
+        verifyReturnedVolumes(candidates, sInternalVol);
+
+        appPkgMgr.setAllow3rdPartyOnInternal(false);
+        appPkgMgr.setForceAllowOnExternal(false);
+        candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm);
+        verifyReturnedVolumes(candidates, sInternalVol);
+
+        appPkgMgr.setAllow3rdPartyOnInternal(false);
+        appPkgMgr.setForceAllowOnExternal(true);
+        candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm);
+        verifyReturnedVolumes(candidates, sInternalVol);
+    }
+
+    public void testGetCandidateVolumes_3rdParty_internalOnly() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        StorageManager storageManager = getMockedStorageManager();
+
+        IPackageManager pm = Mockito.mock(IPackageManager.class);
+        Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
+
+        MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
+
+        // must allow 3rd party on internal, otherwise the app wouldn't have been installed before.
+        appPkgMgr.setAllow3rdPartyOnInternal(true);
+
+        // INSTALL_LOCATION_INTERNAL_ONLY AND INSTALL_LOCATION_UNSPECIFIED are treated the same.
+        int[] locations = {PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED};
+
+        for (int location : locations) {
+            appInfo.installLocation = location;
+            appPkgMgr.setForceAllowOnExternal(true);
+            List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(
+                    appInfo, storageManager, pm);
+            verifyReturnedVolumes(candidates, sInternalVol, sAdoptedVol);
+
+            appPkgMgr.setForceAllowOnExternal(false);
+            candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
+            verifyReturnedVolumes(candidates, sInternalVol);
+        }
+    }
+
+    public void testGetCandidateVolumes_3rdParty_auto() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        StorageManager storageManager = getMockedStorageManager();
+
+        IPackageManager pm = Mockito.mock(IPackageManager.class);
+
+        MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
+        appPkgMgr.setForceAllowOnExternal(true);
+
+        // INSTALL_LOCATION_AUTO AND INSTALL_LOCATION_PREFER_EXTERNAL are treated the same.
+        int[] locations = {PackageInfo.INSTALL_LOCATION_AUTO,
+                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL};
+
+        for (int location : locations) {
+            appInfo.installLocation = location;
+            appInfo.flags = 0;
+
+            appInfo.volumeUuid = sInternalVolUuid;
+            Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
+            appPkgMgr.setAllow3rdPartyOnInternal(true);
+            List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(
+                    appInfo, storageManager, pm);
+            verifyReturnedVolumes(candidates, sInternalVol, sAdoptedVol);
+
+            appInfo.volumeUuid = sInternalVolUuid;
+            appPkgMgr.setAllow3rdPartyOnInternal(true);
+            Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(true);
+            candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
+            verifyReturnedVolumes(candidates, sInternalVol);
+
+            appInfo.flags = ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+            appInfo.volumeUuid = sAdoptedVolUuid;
+            appPkgMgr.setAllow3rdPartyOnInternal(false);
+            candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
+            verifyReturnedVolumes(candidates, sAdoptedVol);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 2a3c22c..ca4141a 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -215,4 +215,46 @@
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
     }
+
+    /**
+     * Unit test for PackageParser.getActivityConfigChanges().
+     * If the bit is 1 in the original configChanges, it is still 1 in the final configChanges.
+     * If the bit is 0 in the original configChanges and the bit is not set to 1 in
+     * restartOnConfigChanges, the bit is changed to 1 in the final configChanges by default.
+     */
+    @Test
+    public void testGetActivityConfigChanges() {
+        // Not set in either configChanges or restartOnConfigChanges.
+        int configChanges = 0x0000; // 00000000.
+        int restartOnConfigChanges = 0x0000; // 00000000.
+        int finalConfigChanges =
+                PackageParser.getActivityConfigChanges(configChanges, restartOnConfigChanges);
+        assertEquals(0x0003, finalConfigChanges); // Should be 00000011.
+
+        // Not set in configChanges, but set in restartOnConfigChanges.
+        configChanges = 0x0000; // 00000000.
+        restartOnConfigChanges = 0x0003; // 00000011.
+        finalConfigChanges =
+                PackageParser.getActivityConfigChanges(configChanges, restartOnConfigChanges);
+        assertEquals(0x0000, finalConfigChanges); // Should be 00000000.
+
+        // Set in configChanges.
+        configChanges = 0x0003; // 00000011.
+        restartOnConfigChanges = 0X0000; // 00000000.
+        finalConfigChanges =
+                PackageParser.getActivityConfigChanges(configChanges, restartOnConfigChanges);
+        assertEquals(0x0003, finalConfigChanges); // Should be 00000011.
+
+        restartOnConfigChanges = 0x0003; // 00000011.
+        finalConfigChanges =
+                PackageParser.getActivityConfigChanges(configChanges, restartOnConfigChanges);
+        assertEquals(0x0003, finalConfigChanges); // Should still be 00000011.
+
+        // Other bit set in configChanges.
+        configChanges = 0x0080; // 10000000, orientation.
+        restartOnConfigChanges = 0x0000; // 00000000.
+        finalConfigChanges =
+                PackageParser.getActivityConfigChanges(configChanges, restartOnConfigChanges);
+        assertEquals(0x0083, finalConfigChanges); // Should be 10000011.
+    }
 }
diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java
new file mode 100644
index 0000000..35d8d93
--- /dev/null
+++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.metrics;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import junit.framework.TestCase;
+
+public class LogMakerTest extends TestCase {
+
+    public void testSerialize() {
+        LogMaker builder = new LogMaker(0);
+        builder.addTaggedData(1, "one");
+        builder.addTaggedData(2, "two");
+        Object[] out = builder.serialize();
+        assertEquals(1, out[0]);
+        assertEquals("one", out[1]);
+        assertEquals(2, out[2]);
+        assertEquals("two", out[3]);
+    }
+
+    public void testSerializeDeserialize() {
+        int category = 10;
+        int type = 11;
+        int subtype = 12;
+        long timestamp = 1484669007890L;
+        String packageName = "com.foo.bar";
+        String counterName = "sheep";
+        int bucket = 13;
+        int value = 14;
+
+        LogMaker builder = new LogMaker(category);
+        builder.setType(type);
+        builder.setSubtype(subtype);
+        builder.setTimestamp(timestamp);
+        builder.setPackageName(packageName);
+        builder.setCounterName(counterName);
+        builder.setCounterBucket(bucket);
+        builder.setCounterValue(value);
+        builder.addTaggedData(1, "one");
+        builder.addTaggedData(2, "two");
+
+        Object[] out = builder.serialize();
+        LogMaker parsed = new LogMaker(out);
+
+        assertEquals(category, parsed.getCategory());
+        assertEquals(type, parsed.getType());
+        assertEquals(subtype, parsed.getSubtype());
+        assertEquals(timestamp, parsed.getTimestamp());
+        assertEquals(packageName, parsed.getPackageName());
+        assertEquals(counterName, parsed.getCounterName());
+        assertEquals(bucket, parsed.getCounterBucket());
+        assertEquals(value, parsed.getCounterValue());
+        assertEquals("one", parsed.getTaggedData(1));
+        assertEquals("two", parsed.getTaggedData(2));
+    }
+
+    public void testIntBucket() {
+        LogMaker builder = new LogMaker(0);
+        builder.setCounterBucket(100);
+        assertEquals(100, builder.getCounterBucket());
+        assertEquals(false, builder.isLongCounterBucket());
+    }
+
+    public void testLongBucket() {
+        long longBucket = Long.MAX_VALUE;
+        LogMaker builder = new LogMaker(0);
+        builder.setCounterBucket(longBucket);
+        assertEquals(longBucket, builder.getCounterBucket());
+        assertEquals(true, builder.isLongCounterBucket());
+    }
+
+    public void testInvalidInputThrows() {
+        LogMaker builder = new LogMaker(0);
+        boolean threw = false;
+        try {
+            builder.addTaggedData(0, new Object());
+        } catch (IllegalArgumentException e) {
+            threw = true;
+        }
+        assertTrue(threw);
+        assertEquals(2, builder.serialize().length);
+    }
+
+    public void testValidInputTypes() {
+        LogMaker builder = new LogMaker(0);
+        builder.addTaggedData(1, "onetwothree");
+        builder.addTaggedData(2, 123);
+        builder.addTaggedData(3, 123L);
+        builder.addTaggedData(4, 123.0F);
+        builder.addTaggedData(5, null);
+        Object[] out = builder.serialize();
+        assertEquals("onetwothree", out[1]);
+        assertEquals(123, out[3]);
+        assertEquals(123L, out[5]);
+        assertEquals(123.0F, out[7]);
+    }
+
+    public void testCategoryDefault() {
+        LogMaker builder = new LogMaker(10);
+        Object[] out = builder.serialize();
+        assertEquals(MetricsEvent.RESERVED_FOR_LOGBUILDER_CATEGORY, out[0]);
+        assertEquals(10, out[1]);
+    }
+
+    public void testGiantLogOmitted() {
+        LogMaker badBuilder = new LogMaker(0);
+        StringBuilder b = new StringBuilder();
+        for (int i = 0; i < 4000; i++) {
+            b.append("test, " + i);
+        }
+        badBuilder.addTaggedData(100, b.toString());
+        assertTrue(badBuilder.serialize().length < LogMaker.MAX_SERIALIZED_SIZE);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
new file mode 100644
index 0000000..1afe9da
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java
@@ -0,0 +1,75 @@
+package android.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.when;
+
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiSsid;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class NetworkKeyTest {
+    private static final String VALID_SSID = "\"ssid1\"";
+    private static final String VALID_BSSID = "00:00:00:00:00:00";
+    @Mock private WifiInfo mWifiInfo;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void createFromWifi_nullInput() throws Exception {
+        assertNull(NetworkKey.createFromWifiInfo(null));
+    }
+
+    @Test
+    public void createFromWifi_nullSsid() throws Exception {
+        when(mWifiInfo.getBSSID()).thenReturn(VALID_BSSID);
+        assertNull(NetworkKey.createFromWifiInfo(mWifiInfo));
+    }
+
+    @Test
+    public void createFromWifi_emptySsid() throws Exception {
+        when(mWifiInfo.getSSID()).thenReturn("");
+        when(mWifiInfo.getBSSID()).thenReturn(VALID_BSSID);
+        assertNull(NetworkKey.createFromWifiInfo(mWifiInfo));
+    }
+
+    @Test
+    public void createFromWifi_noneSsid() throws Exception {
+        when(mWifiInfo.getSSID()).thenReturn(WifiSsid.NONE);
+        when(mWifiInfo.getBSSID()).thenReturn(VALID_BSSID);
+        assertNull(NetworkKey.createFromWifiInfo(mWifiInfo));
+    }
+
+    @Test
+    public void createFromWifi_nullBssid() throws Exception {
+        when(mWifiInfo.getSSID()).thenReturn(VALID_SSID);
+        assertNull(NetworkKey.createFromWifiInfo(mWifiInfo));
+    }
+
+    @Test
+    public void createFromWifi_emptyBssid() throws Exception {
+        when(mWifiInfo.getSSID()).thenReturn(VALID_SSID);
+        when(mWifiInfo.getBSSID()).thenReturn("");
+        assertNull(NetworkKey.createFromWifiInfo(mWifiInfo));
+    }
+
+    @Test
+    public void createFromWifi_validWifiInfo() throws Exception {
+        when(mWifiInfo.getSSID()).thenReturn(VALID_SSID);
+        when(mWifiInfo.getBSSID()).thenReturn(VALID_BSSID);
+
+        NetworkKey expected = new NetworkKey(new WifiKey(VALID_SSID, VALID_BSSID));
+        final NetworkKey actual = NetworkKey.createFromWifiInfo(mWifiInfo);
+        assertEquals(expected, actual);
+    }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 29020ba..5bfff26 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -169,25 +169,6 @@
         assertNull(activeScorer);
     }
 
-    public void testIsCallerActiveScorer_providerNotAvailable() throws Exception {
-        ContentResolver cr = mTargetContext.getContentResolver();
-        Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
-
-        assertFalse(mNetworkScorerAppManager.isCallerActiveScorer(924));
-    }
-
-    public void testIsCallerActiveScorer_providerAvailable() throws Exception {
-        setNetworkRecommendationPackageNames("package1");
-        mockScoreNetworksGranted("package1");
-        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
-
-        ContentResolver cr = mTargetContext.getContentResolver();
-        Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
-
-        assertTrue(mNetworkScorerAppManager.isCallerActiveScorer(924));
-        assertFalse(mNetworkScorerAppManager.isCallerActiveScorer(925));
-    }
-
     private void setNetworkRecommendationPackageNames(String... names) {
         if (names == null) {
             names = new String[0];
diff --git a/core/tests/coretests/src/android/net/RecommendationRequestTest.java b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
index 31560b0..bd25500 100644
--- a/core/tests/coretests/src/android/net/RecommendationRequestTest.java
+++ b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
@@ -3,12 +3,16 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.os.Parcel;
+import android.os.SystemClock;
 import android.test.AndroidTestCase;
 
 public class RecommendationRequestTest extends AndroidTestCase {
     private ScanResult[] mScanResults;
-    private WifiConfiguration mConfiguration;
-    private NetworkCapabilities mCapabilities;
+    private WifiConfiguration mDefaultConfig;
+    private WifiConfiguration mConnectedConfig;
+    private WifiConfiguration[] mConnectableConfigs;
+    private int mLastSelectedNetworkId;
+    private long mLastSelectedNetworkTimestamp;
 
     @Override
     public void setUp() throws Exception {
@@ -29,45 +33,73 @@
                 8 /*centerFreq0*/,
                 9 /*centerFreq1*/,
                 false /*is80211McRTTResponder*/);
-        mConfiguration = new WifiConfiguration();
-        mConfiguration.SSID = "RecommendationRequestTest";
-        mCapabilities = new NetworkCapabilities()
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
+        mDefaultConfig = new WifiConfiguration();
+        mDefaultConfig.SSID = "default_config";
+        mConnectedConfig = new WifiConfiguration();
+        mConnectedConfig.SSID = "connected_config";
+        mConnectableConfigs = new WifiConfiguration[] {mDefaultConfig, mConnectedConfig};
+        mLastSelectedNetworkId = 5;
+        mLastSelectedNetworkTimestamp = SystemClock.elapsedRealtime();
     }
 
     public void testParceling() throws Exception {
         RecommendationRequest request = new RecommendationRequest.Builder()
-                .setCurrentRecommendedWifiConfig(mConfiguration)
+                .setDefaultWifiConfig(mDefaultConfig)
                 .setScanResults(mScanResults)
-                .setNetworkCapabilities(mCapabilities)
+                .setConnectedWifiConfig(mConnectedConfig)
+                .setConnectableConfigs(mConnectableConfigs)
+                .setLastSelectedNetwork(mLastSelectedNetworkId, mLastSelectedNetworkTimestamp)
                 .build();
 
         RecommendationRequest parceled = passThroughParcel(request);
-        assertEquals(request.getCurrentSelectedConfig().SSID,
-                parceled.getCurrentSelectedConfig().SSID);
-        assertEquals(request.getRequiredCapabilities(), parceled.getRequiredCapabilities());
+        assertEquals(request.getDefaultWifiConfig().SSID,
+                parceled.getDefaultWifiConfig().SSID);
+        assertEquals(request.getConnectedConfig().SSID,
+                parceled.getConnectedConfig().SSID);
         ScanResult[] parceledScanResults = parceled.getScanResults();
         assertNotNull(parceledScanResults);
         assertEquals(mScanResults.length, parceledScanResults.length);
         for (int i = 0; i < mScanResults.length; i++) {
             assertEquals(mScanResults[i].SSID, parceledScanResults[i].SSID);
         }
+        WifiConfiguration[] parceledConfigs = parceled.getConnectableConfigs();
+        for (int i = 0; i < parceledConfigs.length; i++) {
+            assertEquals(mConnectableConfigs[i].SSID, parceledConfigs[i].SSID);
+        }
+        assertEquals(mLastSelectedNetworkId, parceled.getLastSelectedNetworkId());
+        assertEquals(mLastSelectedNetworkTimestamp, parceled.getLastSelectedNetworkTimestamp());
     }
 
     public void testParceling_nullScanResults() throws Exception {
         RecommendationRequest request = new RecommendationRequest.Builder()
-                .setCurrentRecommendedWifiConfig(mConfiguration)
-                .setNetworkCapabilities(mCapabilities)
+                .setDefaultWifiConfig(mDefaultConfig)
                 .build();
 
         RecommendationRequest parceled = passThroughParcel(request);
-        assertEquals(request.getCurrentSelectedConfig().SSID,
-                parceled.getCurrentSelectedConfig().SSID);
-        assertEquals(request.getRequiredCapabilities(), parceled.getRequiredCapabilities());
         ScanResult[] parceledScanResults = parceled.getScanResults();
         assertNull(parceledScanResults);
     }
 
+    public void testParceling_nullWifiConfigArray() throws Exception {
+        RecommendationRequest request = new RecommendationRequest.Builder()
+                .setDefaultWifiConfig(mDefaultConfig)
+                .build();
+
+        RecommendationRequest parceled = passThroughParcel(request);
+        WifiConfiguration[] parceledConfigs = parceled.getConnectableConfigs();
+        assertNull(parceledConfigs);
+    }
+
+    public void testParceling_unsetLastSelectedNetwork() throws Exception {
+        RecommendationRequest request = new RecommendationRequest.Builder()
+                .build();
+
+        RecommendationRequest parceled = passThroughParcel(request);
+
+        assertEquals(0, parceled.getLastSelectedNetworkId());
+        assertEquals(0, parceled.getLastSelectedNetworkTimestamp());
+    }
+
     private RecommendationRequest passThroughParcel(RecommendationRequest request) {
         Parcel p = Parcel.obtain();
         RecommendationRequest output = null;
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index 37f0007..ff98eb7 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -18,15 +18,20 @@
 
 import android.content.Context;
 import android.os.Environment;
+import android.os.ProxyFileDescriptorCallback;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
 import com.android.frameworks.coretests.R;
-
+import com.android.internal.os.FuseAppLoop;
 import java.io.DataInputStream;
 import java.io.IOException;
+import java.util.concurrent.ThreadFactory;
 import java.io.File;
 import java.io.FileInputStream;
 
@@ -243,4 +248,47 @@
         verifyObb1Contents(filePath);
         unmountObb(filePath, DONT_FORCE);
     }
+
+    @LargeTest
+    public void testOpenProxyFileDescriptor() throws Exception {
+        final ProxyFileDescriptorCallback callback = new ProxyFileDescriptorCallback() {
+            @Override
+            public long onGetSize() throws ErrnoException {
+                return 0;
+            }
+
+            @Override
+            public void onRelease() {}
+        };
+
+        final MyThreadFactory factory = new MyThreadFactory();
+        int firstMountId;
+        try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor(
+                ParcelFileDescriptor.MODE_READ_ONLY, callback, factory)) {
+            assertNotSame(Thread.State.TERMINATED, factory.thread.getState());
+            firstMountId = mSm.getProxyFileDescriptorMountPointId();
+            assertNotSame(-1, firstMountId);
+        }
+
+        // After closing descriptor, the loop should terminate.
+        factory.thread.join(3000);
+        assertEquals(Thread.State.TERMINATED, factory.thread.getState());
+
+        // StorageManager should mount another bridge on the next open request.
+        try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor(
+                ParcelFileDescriptor.MODE_WRITE_ONLY, callback, factory)) {
+            assertNotSame(Thread.State.TERMINATED, factory.thread.getState());
+            assertNotSame(firstMountId, mSm.getProxyFileDescriptorMountPointId());
+        }
+    }
+
+    private static class MyThreadFactory implements ThreadFactory {
+        Thread thread = null;
+
+        @Override
+        public Thread newThread(Runnable r) {
+            thread = new Thread(r);
+            return thread;
+        }
+    }
 }
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index b0ce2c8..3fbf169 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -364,4 +364,20 @@
         // one or more activity can handle this intent.
         assertTrue(resolveInfoList.size() > 0);
     }
+
+    @SmallTest
+    public void testValidSsaid() {
+        ContentResolver r = getContext().getContentResolver();
+
+        // Verify ssaid
+        String ssaid = Settings.Secure.getString(r, Settings.Secure.ANDROID_ID);
+        assertTrue(ssaid != null);
+        assertTrue(ssaid.length() == 16);
+
+        String ssaid2 = Settings.Secure.getString(r, Settings.Secure.ANDROID_ID);
+        assertTrue(ssaid2 != null);
+        assertTrue(ssaid2.length() == 16);
+
+        assertTrue(ssaid.equals(ssaid2));
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index aab4698..daebf88 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -222,7 +222,7 @@
     private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
         List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
         for (int i = 0; i < numberOfResults; i++) {
-            infoList.add(ChooserDataProvider.createResolvedComponentInfo(i));
+            infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
         }
         return infoList;
     }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 41016e1..c446f3c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -24,11 +24,10 @@
 
 import static org.mockito.Mockito.mock;
 
-
-/**
- * Simple wrapper around chooser activity to be able to initiate it under test
- */
 public class ChooserWrapperActivity extends ChooserActivity {
+    /*
+     * Simple wrapper around chooser activity to be able to initiate it under test
+     */
     static final OverrideData sOverrides = new OverrideData();
     private UsageStatsManager mUsm;
 
@@ -94,4 +93,4 @@
             resolverListController = mock(ResolverListController.class);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
new file mode 100644
index 0000000..84b844a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static android.os.SystemClock.sleep;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Resolver activity instrumentation tests
+ */
+@RunWith(AndroidJUnit4.class)
+public class ResolverActivityTest {
+    @Rule
+    public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
+            new ActivityTestRule<>(ResolverWrapperActivity.class, false,
+                    false);
+
+    @Before
+    public void cleanOverrideData() {
+        sOverrides.reset();
+    }
+
+    @Test
+    public void twoOptionsAndUserSelectsOne() throws InterruptedException {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+
+        assertThat(activity.getAdapter().getCount(), is(2));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+        onView(withText(toChoose.activityInfo.name))
+                .perform(click());
+        onView(withId(R.id.button_once))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+    @Test
+    public void hasLastChosenActivity() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        when(sOverrides.resolverListController.getLastChosen())
+                .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+
+        // The other entry is filtered to the last used slot
+        assertThat(activity.getAdapter().getCount(), is(1));
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+        onView(withId(R.id.title)).perform(click());
+        onView(withId(R.id.button_once))
+                .perform(click());
+        waitForIdle();
+        assertThat(chosen[0], is(toChoose));
+    }
+
+    private Intent createSendImageIntent() {
+        Intent sendIntent = new Intent();
+        sendIntent.setAction(Intent.ACTION_SEND);
+        sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+        sendIntent.setType("image/jpeg");
+        return sendIntent;
+    }
+
+    private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
+        List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+        for (int i = 0; i < numberOfResults; i++) {
+            infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+        }
+        return infoList;
+    }
+
+    private void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
similarity index 94%
rename from core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java
rename to core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
index f6f63f1..ae06306 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
@@ -22,16 +22,15 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
-import android.service.chooser.ChooserTarget;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * Utility class used by chooser tests to create mock data
+ * Utility class used by resolver tests to create mock data
  */
-class ChooserDataProvider {
+class ResolverDataProvider {
 
     static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfo(int i) {
         return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
new file mode 100644
index 0000000..163211e
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import java.util.function.Function;
+
+import static org.mockito.Mockito.mock;
+
+
+/*
+ * Simple wrapper around chooser activity to be able to initiate it under test
+ */
+public class ResolverWrapperActivity extends ResolverActivity {
+    static final OverrideData sOverrides = new OverrideData();
+    private UsageStatsManager mUsm;
+
+    ResolveListAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    @Override
+    public boolean isVoiceInteraction() {
+        if (sOverrides.isVoiceInteraction != null) {
+            return sOverrides.isVoiceInteraction;
+        }
+        return super.isVoiceInteraction();
+    }
+
+    @Override
+    public void safelyStartActivity(TargetInfo cti) {
+        if (sOverrides.onSafelyStartCallback != null &&
+                sOverrides.onSafelyStartCallback.apply(cti)) {
+            return;
+        }
+        super.safelyStartActivity(cti);
+    }
+
+    @Override
+    protected ResolverListController createListController() {
+        return sOverrides.resolverListController;
+    }
+
+    @Override
+    public PackageManager getPackageManager() {
+        if (sOverrides.createPackageManager != null) {
+            return sOverrides.createPackageManager.apply(super.getPackageManager());
+        }
+        return super.getPackageManager();
+    }
+
+    /**
+     * We cannot directly mock the activity created since instrumentation creates it.
+     * <p>
+     * Instead, we use static instances of this object to modify behavior.
+     */
+    static class OverrideData {
+        @SuppressWarnings("Since15")
+        public Function<PackageManager, PackageManager> createPackageManager;
+        public Function<TargetInfo, Boolean> onSafelyStartCallback;
+        public ResolverListController resolverListController;
+        public Boolean isVoiceInteraction;
+
+        public void reset() {
+            onSafelyStartCallback = null;
+            isVoiceInteraction = null;
+            createPackageManager = null;
+            resolverListController = mock(ResolverListController.class);
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 34c34d7..dc75417 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -33,7 +33,8 @@
 import java.util.List;
 
 public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTestCase {
-    private static final String DUMMY_PACKAGE_NAME = "dymmy package name";
+    private static final String DUMMY_PACKAGE_NAME = "dummy package name";
+    private static final String DUMMY_IME_LABEL = "dummy ime label";
     private static final String DUMMY_SETTING_ACTIVITY_NAME = "";
     private static final boolean DUMMY_IS_AUX_IME = false;
     private static final boolean DUMMY_FORCE_DEFAULT = false;
@@ -88,6 +89,35 @@
         }
     }
 
+    private static ImeSubtypeListItem createDummyItem(String imeName,
+            String subtypeName, String subtypeLocale, int subtypeIndex, String systemLocale) {
+        final ResolveInfo ri = new ResolveInfo();
+        final ServiceInfo si = new ServiceInfo();
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = DUMMY_PACKAGE_NAME;
+        ai.enabled = true;
+        si.applicationInfo = ai;
+        si.enabled = true;
+        si.packageName = DUMMY_PACKAGE_NAME;
+        si.name = imeName;
+        si.exported = true;
+        si.nonLocalizedLabel = DUMMY_IME_LABEL;
+        ri.serviceInfo = si;
+        ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
+        subtypes.add(new InputMethodSubtypeBuilder()
+                .setSubtypeNameResId(0)
+                .setSubtypeIconResId(0)
+                .setSubtypeLocale(subtypeLocale)
+                .setIsAsciiCapable(true)
+                .build());
+        final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
+                DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
+                DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */,
+                false /* supportsDismissingWindow */);
+        return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale,
+                systemLocale);
+    }
+
     private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
         final List<ImeSubtypeListItem> items = new ArrayList<>();
         addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
@@ -329,4 +359,56 @@
         assertFalse(item_e.mIsSystemLocale);
         assertFalse(item_EN_US.mIsSystemLocale);
     }
+
+    @SmallTest
+    public void testImeSubtypeListComparator() throws Exception {
+        {
+            final List<ImeSubtypeListItem> items = Arrays.asList(
+                    createDummyItem("X", "A", "en_US", 0, "en_US"),
+                    createDummyItem("X", "A", "en", 1, "en_US"),
+                    createDummyItem("X", "A", "ja", 2, "en_US"),
+                    createDummyItem("X", "Z", "en_US", 3, "en_US"),
+                    createDummyItem("X", "Z", "en", 4, "en_US"),
+                    createDummyItem("X", "Z", "ja", 5, "en_US"),
+                    createDummyItem("X", "", "en_US", 6, "en_US"),
+                    createDummyItem("X", "", "en", 7, "en_US"),
+                    createDummyItem("X", "", "ja", 8, "en_US"),
+                    createDummyItem("Y", "A", "en_US", 9, "en_US"),
+                    createDummyItem("Y", "A", "en", 10, "en_US"),
+                    createDummyItem("Y", "A", "ja", 11, "en_US"),
+                    createDummyItem("Y", "Z", "en_US", 12, "en_US"),
+                    createDummyItem("Y", "Z", "en", 13, "en_US"),
+                    createDummyItem("Y", "Z", "ja", 14, "en_US"),
+                    createDummyItem("Y", "", "en_US", 15, "en_US"),
+                    createDummyItem("Y", "", "en", 16, "en_US"),
+                    createDummyItem("Y", "", "ja", 17, "en_US"),
+                    createDummyItem("", "A", "en_US", 18, "en_US"),
+                    createDummyItem("", "A", "en", 19, "en_US"),
+                    createDummyItem("", "A", "ja", 20, "en_US"),
+                    createDummyItem("", "Z", "en_US", 21, "en_US"),
+                    createDummyItem("", "Z", "en", 22, "en_US"),
+                    createDummyItem("", "Z", "ja", 23, "en_US"),
+                    createDummyItem("", "", "en_US", 24, "en_US"),
+                    createDummyItem("", "", "en", 25, "en_US"),
+                    createDummyItem("", "", "ja", 26, "en_US"));
+
+            for (int i = 0; i < items.size(); ++i) {
+                assertEquals(0, items.get(i).compareTo(items.get(i)));
+                for (int j = i + 1; j < items.size(); ++j) {
+                    assertTrue(items.get(i).compareTo(items.get(j)) < 0);
+                    assertTrue(items.get(j).compareTo(items.get(i)) > 0);
+                }
+            }
+        }
+
+        {
+            // Following two items have the same priority.
+            final ImeSubtypeListItem nonSystemLocale1 =
+                    createDummyItem("X", "A", "ja_JP", 0, "en_us");
+            final ImeSubtypeListItem nonSystemLocale2 =
+                    createDummyItem("X", "A", "hi_IN", 1, "en_us");
+            assertEquals(0, nonSystemLocale1.compareTo(nonSystemLocale2));
+            assertEquals(0, nonSystemLocale2.compareTo(nonSystemLocale1));
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
deleted file mode 100644
index e3f754c..0000000
--- a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.android.internal.logging;
-
-import junit.framework.TestCase;
-
-public class LogBuilderTest extends TestCase {
-
-    public void testSerialize() {
-        LogBuilder builder = new LogBuilder(0);
-        builder.addTaggedData(1, "one");
-        builder.addTaggedData(2, "two");
-        Object[] out = builder.serialize();
-        assertEquals(1, out[0]);
-        assertEquals("one", out[1]);
-        assertEquals(2, out[2]);
-        assertEquals("two", out[3]);
-    }
-
-    public void testInvalidInputThrows() {
-        LogBuilder builder = new LogBuilder(0);
-        boolean threw = false;
-        try {
-            builder.addTaggedData(0, new Object());
-        } catch (IllegalArgumentException e) {
-            threw = true;
-        }
-        assertTrue(threw);
-        assertEquals(0, builder.serialize().length);
-    }
-
-    public void testValidInputTypes() {
-        LogBuilder builder = new LogBuilder(0);
-        builder.addTaggedData(1, "onetwothree");
-        builder.addTaggedData(2, 123);
-        builder.addTaggedData(3, 123L);
-        builder.addTaggedData(4, 123.0F);
-        Object[] out = builder.serialize();
-        assertEquals("onetwothree", out[1]);
-        assertEquals(123, out[3]);
-        assertEquals(123L, out[5]);
-        assertEquals(123.0F, out[7]);
-    }
-
-}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/CounterParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/CounterParserTest.java
new file mode 100644
index 0000000..5a7766b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/CounterParserTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+public class CounterParserTest extends ParserTest {
+
+    public CounterParserTest() {
+        mParser = new CounterParser();
+    }
+
+    public void testGoodData() throws Throwable {
+        String name = "foo";
+        int value = 5;
+        Object[] objects = new Object[2];
+        objects[0] = name;
+        objects[1] = value;
+
+        validateGoodData(name, value, objects);
+    }
+
+    private void validateGoodData(String name, int value, Object[] objects) {
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, times(1)).incrementBy(mNameCaptor.capture(), mCountCaptor.capture());
+
+        assertEquals(TronCounters.TRON_AOSP_PREFIX + name, mNameCaptor.getValue());
+        assertEquals(value, mCountCaptor.getValue().intValue());
+    }
+
+    public void testMissingName() throws Throwable {
+        Object[] objects = new Object[1];
+        objects[0] = 5;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testWrongTypes() throws Throwable {
+        String name = "foo";
+        int value = 5;
+        Object[] objects = new Object[2];
+        objects[0] = value;
+        objects[1] = name;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreMissingInput() throws Throwable {
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        String name = "foo";
+        int value = 5;
+        Object[] objects = new Object[3];
+        objects[0] = name;
+        objects[1] = value;
+        objects[2] = "foo";
+
+        validateGoodData(name, value, objects);
+    }
+
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/HistogramParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/HistogramParserTest.java
new file mode 100644
index 0000000..1bd9d83
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/HistogramParserTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+public class HistogramParserTest extends ParserTest {
+
+    public HistogramParserTest() {
+        mParser = new HistogramParser();
+    }
+
+    public void testGoodData() throws Throwable {
+        String name = "foo";
+        int bucket = 5;
+        Object[] objects = new Object[2];
+        objects[0] = name;
+        objects[1] = bucket;
+
+        validateGoodData(name, bucket, objects);
+    }
+
+    private void validateGoodData(String name, int bucket, Object[] objects) {
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, times(1))
+                .incrementIntHistogram(mNameCaptor.capture(), mCountCaptor.capture());
+
+        assertEquals(TronCounters.TRON_AOSP_PREFIX + name, mNameCaptor.getValue());
+        assertEquals(bucket, mCountCaptor.getValue().intValue());
+    }
+
+    public void testMissingName() throws Throwable {
+        Object[] objects = new Object[1];
+        objects[0] = 5;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testWrongTypes() throws Throwable {
+        String name = "foo";
+        int value = 5;
+        Object[] objects = new Object[2];
+        objects[0] = value;
+        objects[1] = name;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreMissingInput() throws Throwable {
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        String name = "foo";
+        int bucket = 5;
+        Object[] objects = new Object[3];
+        objects[0] = name;
+        objects[1] = bucket;
+        objects[2] = "foo";
+
+        validateGoodData(name, bucket, objects);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/LockscreenGestureParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/LockscreenGestureParserTest.java
new file mode 100644
index 0000000..c023b57
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/LockscreenGestureParserTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class LockscreenGestureParserTest extends ParserTest {
+
+    public LockscreenGestureParserTest() {
+        mParser = new LockscreenGestureParser();
+    }
+
+    public void testSwipeUpUnlock() throws Throwable {
+        validate(MetricsEvent.ACTION_LS_UNLOCK, 1, 359, 6382);
+    }
+
+    public void testSwipeToShade() throws Throwable {
+        validate(MetricsEvent.ACTION_LS_SHADE, 2, 324, 0);
+    }
+
+    public void testTapLockHint() throws Throwable {
+        validate(MetricsEvent.ACTION_LS_HINT, 3, 0, 0);
+    }
+
+    public void testCamera() throws Throwable {
+        validate(MetricsEvent.ACTION_LS_CAMERA, 4, 223, 1756);
+    }
+
+    public void testDialer() throws Throwable {
+        validate(MetricsEvent.ACTION_LS_DIALER, 5, 163, 861);
+    }
+
+    public void testTapToLock() throws Throwable {
+        validate(MetricsEvent.ACTION_LS_LOCK, 6, 0, 0);
+    }
+
+    public void testTapOnNotification() throws Throwable {
+        validate(MetricsEvent.ACTION_LS_NOTE, 7, 0, 0);
+    }
+
+    public void testLockscreenQuickSettings() throws Throwable {
+        validate(MetricsEvent.ACTION_LS_QS, 8, 284, 3824);
+    }
+
+    public void testShadePullQuickSettings() throws Throwable {
+        validate(MetricsEvent.ACTION_SHADE_QS_PULL, 9, 175, 3444);
+    }
+
+    public void testShadeTapQuickSettings() throws Throwable {
+        validate(MetricsEvent.ACTION_SHADE_QS_TAP, 10, 0, 0);
+    }
+
+    private void validate(int view, int type, int len, int vel) {
+        int t = 1000;
+        Object[] objects = new Object[3];
+        objects[0] = type;
+        objects[1] = len;
+        objects[2] = vel;
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(view, proto.getCategory());
+        assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        int t = 1000;
+        Object[] objects = new Object[4];
+        objects[0] = 1;
+        objects[1] = 0;
+        objects[2] = 0;
+        objects[3] = "foo";
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent((LogMaker) anyObject());
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationActionClickedParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationActionClickedParserTest.java
new file mode 100644
index 0000000..f05205d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationActionClickedParserTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationActionClickedParserTest extends ParserTest {
+
+    public NotificationActionClickedParserTest() {
+        mParser = new NotificationActionClickedParser();
+    }
+
+    public void testGoodData() throws Throwable {
+        int t = 1000;
+        int index = 1;
+        Object[] objects = new Object[2];
+        objects[0] = mKey;
+        objects[1] = index;
+
+        validateGoodData(t, "", index, objects);
+    }
+
+    public void testTagged() throws Throwable {
+        int t = 1000;
+        int index = 1;
+        Object[] objects = new Object[2];
+        objects[0] = mTaggedKey;
+        objects[1] = index;
+
+        validateGoodData(t, mTag, index, objects);
+    }
+
+    private LogMaker validateGoodData(int t, String tag, int index, Object[] objects) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_ITEM_ACTION, proto.getCategory());
+        assertEquals(mKeyPackage, proto.getPackageName());
+        validateNotificationIdAndTag(proto, mId, tag);
+        assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+        assertEquals(index, proto.getSubtype());
+        return proto;
+    }
+
+    public void testMissingData() throws Throwable {
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testWrongType() throws Throwable {
+        Object[] objects = new Object[2];
+        objects[0] = 2;
+        objects[1] = 5;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testBadKey() throws Throwable {
+        Object[] objects = new Object[2];
+        objects[0] = "foo";
+        objects[1] = 5;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testMncTimestamps() throws Throwable {
+        int t = 1000;
+        int index = 1;
+        Object[] objects = new Object[5];
+        objects[0] = mKey;
+        objects[1] = index;
+        objects[2] = mSinceCreationMillis;
+        objects[3] = mSinceUpdateMillis;
+        objects[4] = mSinceVisibleMillis;
+
+        LogMaker proto = validateGoodData(t, "", index, objects);
+        validateNotificationTimes(proto, mSinceCreationMillis, mSinceUpdateMillis,
+                mSinceVisibleMillis);
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        int t = 1000;
+        int index = 1;
+        Object[] objects = new Object[6];
+        objects[0] = mKey;
+        objects[1] = index;
+        objects[2] = mSinceCreationMillis;
+        objects[3] = mSinceUpdateMillis;
+        objects[4] = mSinceVisibleMillis;
+        objects[5] = "foo";
+
+        validateGoodData(t, "", index, objects);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationAlertParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationAlertParserTest.java
new file mode 100644
index 0000000..7771e84
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationAlertParserTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import org.mockito.ArgumentCaptor;
+
+public class NotificationAlertParserTest extends ParserTest {
+    protected ArgumentCaptor<Boolean> mConfigCaptor;
+
+    private final int mTime = 1000;
+
+    public NotificationAlertParserTest() {
+        mParser = new NotificationAlertParser();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mConfigCaptor = ArgumentCaptor.forClass(Boolean.class);
+        when(mLogger.getConfig(anyString())).thenReturn(false);
+    }
+
+    public void testBuzzOnly() throws Throwable {
+        Object[] objects = new Object[4];
+        objects[0] = mTaggedKey;
+        objects[1] = 1;
+        objects[2] = 0;
+        objects[3] = 0;
+
+        validateInteraction(true, false, false, objects);
+    }
+
+    public void testBeepOnly() throws Throwable {
+        Object[] objects = new Object[4];
+        objects[0] = mTaggedKey;
+        objects[1] = 0;
+        objects[2] = 1;
+        objects[3] = 0;
+
+        validateInteraction(false, true, false, objects);
+    }
+
+    public void testBlinkOnly() throws Throwable {
+        Object[] objects = new Object[4];
+        objects[0] = mTaggedKey;
+        objects[1] = 0;
+        objects[2] = 0;
+        objects[3] = 1;
+
+        validateInteraction(false, false, true, objects);
+    }
+
+    public void testBuzzBlink() throws Throwable {
+        Object[] objects = new Object[4];
+        objects[0] = mTaggedKey;
+        objects[1] = 1;
+        objects[2] = 0;
+        objects[3] = 1;
+
+        validateInteraction(true, false, true, objects);
+    }
+
+    public void testBeepBlink() throws Throwable {
+        Object[] objects = new Object[4];
+        objects[0] = mTaggedKey;
+        objects[1] = 0;
+        objects[2] = 1;
+        objects[3] = 1;
+
+        validateInteraction(false, true, true, objects);
+    }
+
+    public void testIgnoreExtraArgs() throws Throwable {
+        Object[] objects = new Object[5];
+        objects[0] = mTaggedKey;
+        objects[1] = 0;
+        objects[2] = 1;
+        objects[3] = 1;
+        objects[4] = "foo";
+
+        validateInteraction(false, true, true, objects);
+    }
+
+    private void validateInteraction(boolean buzz, boolean beep, boolean blink, Object[] objects) {
+        int flags = 0;
+        int counts = 0;
+        if (buzz) {
+            counts++;
+            flags |= NotificationAlertParser.BUZZ;
+        }
+        if (beep) {
+            counts++;
+            flags |= NotificationAlertParser.BEEP;
+        }
+        if (blink) {
+            counts++;
+            flags |= NotificationAlertParser.BLINK;
+        }
+
+        mParser.parseEvent(mLogger, mTime, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(mTime, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_ALERT, proto.getCategory());
+        assertEquals(mKeyPackage, proto.getPackageName());
+        validateNotificationIdAndTag(proto, mId, mTag);
+        assertEquals(flags, proto.getSubtype());
+        assertEquals(MetricsEvent.TYPE_OPEN, proto.getType());
+
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationCanceledParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationCanceledParserTest.java
new file mode 100644
index 0000000..77b2ed6
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationCanceledParserTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationCanceledParserTest extends ParserTest {
+
+    public NotificationCanceledParserTest() {
+        mParser = new NotificationCanceledParser();
+    }
+
+    public void testGoodProto() throws Throwable {
+        int t = 1000;
+        int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+        Object[] objects = new Object[2];
+        objects[0] = mKey;
+        objects[1] = reason;
+
+        validateGoodData(t, "", reason, objects);
+    }
+
+    public void testTagged() throws Throwable {
+        int t = 1000;
+        int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+        Object[] objects = new Object[2];
+        objects[0] = mTaggedKey;
+        objects[1] = reason;
+
+        validateGoodData(t, mTag, reason, objects);
+    }
+
+    private void validateGoodData(int t, String tag, int reason, Object[] objects) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_ITEM, proto.getCategory());
+        assertEquals(mKeyPackage, proto.getPackageName());
+        validateNotificationIdAndTag(proto, mId, tag);
+        assertEquals(MetricsEvent.TYPE_DISMISS, proto.getType());
+        assertEquals(reason, proto.getSubtype());
+    }
+
+    public void testLifetime() throws Throwable {
+        int t = 1000;
+        int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+        Object[] objects = new Object[3];
+        objects[0] = mKey;
+        objects[1] = reason;
+        objects[2] = mSinceCreationMillis;
+
+        validateTimers(t, objects, mSinceCreationMillis, 0, 0);
+    }
+
+    public void testExposure() throws Throwable {
+        int t = 1000;
+        int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+        Object[] objects = new Object[4];
+        objects[0] = mKey;
+        objects[1] = reason;
+        objects[2] = mSinceCreationMillis;
+        objects[3] = mSinceVisibleMillis;
+
+
+        validateTimers(t, objects, mSinceCreationMillis, 0, mSinceVisibleMillis);
+    }
+
+    public void testFreshness() throws Throwable {
+        int t = 1000;
+        int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+        Object[] objects = new Object[5];
+        objects[0] = mKey;
+        objects[1] = reason;
+        objects[2] = mSinceCreationMillis;
+        objects[3] = mSinceUpdateMillis;
+        objects[4] = mSinceVisibleMillis;
+
+        validateTimers(t, objects, mSinceCreationMillis, mSinceUpdateMillis, mSinceVisibleMillis);
+    }
+
+    private void validateTimers(int t, Object[] objects, int life, int freshness, int exposure) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        validateNotificationTimes(proto, life, freshness, exposure);
+    }
+
+    public void verifyReason(int reason, boolean intentional, boolean important, String counter)
+            throws Throwable {
+        Object[] objects = new Object[2];
+        objects[0] = mKey;
+        objects[1] = reason;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        if (intentional) {
+            verify(mLogger, times(1)).addEvent((LogMaker) anyObject());
+        }
+    }
+
+    public void testDelegateCancel() throws Throwable {
+        verifyReason(NotificationCanceledParser.REASON_DELEGATE_CANCEL,
+                true, true, TronCounters.TRON_NOTE_DISMISS_BY_USER);
+    }
+
+    public void testDelegateCancelAll() throws Throwable {
+        verifyReason(NotificationCanceledParser.REASON_DELEGATE_CANCEL_ALL,
+                true, true, TronCounters.TRON_NOTE_DISMISS_BY_USER);
+    }
+
+    public void testListenerCancel() throws Throwable {
+        verifyReason(NotificationCanceledParser.REASON_LISTENER_CANCEL,
+                false, true, TronCounters.TRON_NOTE_DISMISS_BY_LISTENER);
+    }
+
+    public void testListenerCancelAll() throws Throwable {
+        verifyReason(NotificationCanceledParser.REASON_LISTENER_CANCEL_ALL,
+                false, true, TronCounters.TRON_NOTE_DISMISS_BY_LISTENER);
+    }
+
+    public void testDelegateClick() throws Throwable {
+        verifyReason(NotificationCanceledParser.REASON_DELEGATE_CLICK,
+                true, true, TronCounters.TRON_NOTE_DISMISS_BY_CLICK);
+    }
+
+    public void testBanned() throws Throwable {
+        verifyReason(NotificationCanceledParser.REASON_PACKAGE_BANNED,
+                false, true, TronCounters.TRON_NOTE_DISMISS_BY_BAN);
+    }
+
+    public void testUnknownReason() throws Throwable {
+        verifyReason(1001010, false, false, TronCounters.TRON_NOTE_DISMISS_BY_BAN);
+    }
+
+    public void testMissingData() throws Throwable {
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testWrongType() throws Throwable {
+        Object[] objects = new Object[2];
+        objects[0] = 2;
+        objects[1] = 5;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testBadKey() throws Throwable {
+        Object[] objects = new Object[2];
+        objects[0] = "foo";
+        objects[1] = 5;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        int t = 1000;
+        int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+        Object[] objects = new Object[3];
+        objects[0] = mKey;
+        objects[1] = reason;
+        objects[2] = "foo";
+
+        validateGoodData(t, "", reason, objects);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationClickedParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationClickedParserTest.java
new file mode 100644
index 0000000..cc65132
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationClickedParserTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationClickedParserTest extends ParserTest {
+
+    public NotificationClickedParserTest() {
+        mParser = new NotificationClickedParser();
+    }
+
+    public void testGoodData() throws Throwable {
+        int t = 1000;
+        Object[] objects = new Object[1];
+        objects[0] = mKey;
+
+        validateGoodData(t, "", objects);
+    }
+
+    public void testTagged() throws Throwable {
+        int t = 1000;
+        Object[] objects = new Object[1];
+        objects[0] = mTaggedKey;
+
+        validateGoodData(t, mTag, objects);
+    }
+
+    private LogMaker validateGoodData(int t, String tag, Object[] objects) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_ITEM, proto.getCategory());
+        assertEquals(mKeyPackage, proto.getPackageName());
+        validateNotificationIdAndTag(proto, mId, tag);
+        assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+        assertEquals(0, proto.getSubtype());
+        return proto;
+    }
+
+    public void testMissingKey() throws Throwable {
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testWrongType() throws Throwable {
+        Object[] objects = new Object[1];
+        objects[0] = 5;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testBadKey() throws Throwable {
+        Object[] objects = new Object[1];
+        objects[0] = "foo";
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testMncTimestamps() throws Throwable {
+        int t = 1000;
+        Object[] objects = new Object[4];
+        objects[0] = mKey;
+        objects[1] = mSinceCreationMillis;
+        objects[2] = mSinceUpdateMillis;
+        objects[3] = mSinceVisibleMillis;
+
+        LogMaker proto = validateGoodData(t, "", objects);
+        validateNotificationTimes(proto, mSinceCreationMillis, mSinceUpdateMillis,
+                mSinceVisibleMillis);
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        int t = 1000;
+        Object[] objects = new Object[5];
+        objects[0] = mKey;
+        objects[1] = mSinceCreationMillis;
+        objects[2] = mSinceUpdateMillis;
+        objects[3] = mSinceVisibleMillis;
+        objects[4] = "foo";
+
+        validateGoodData(t, "", objects);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationExpansionParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationExpansionParserTest.java
new file mode 100644
index 0000000..f337f91
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationExpansionParserTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationExpansionParserTest extends ParserTest {
+
+    public NotificationExpansionParserTest() {
+        mParser = new NotificationExpansionParser();
+    }
+
+    public void testExpandedByUser() throws Throwable {
+        int t = 1000;
+        int byUser = 1;
+        int expanded = 1;
+        Object[] objects = new Object[3];
+        objects[0] = mKey;
+        objects[1] = byUser;
+        objects[2] = expanded;
+
+        validateGoodData(t, "", objects);
+    }
+
+    public void testTagged() throws Throwable {
+        int t = 1000;
+        int byUser = 1;
+        int expanded = 1;
+        Object[] objects = new Object[3];
+        objects[0] = mTaggedKey;
+        objects[1] = byUser;
+        objects[2] = expanded;
+
+        validateGoodData(t, mTag, objects);
+    }
+
+    private LogMaker validateGoodData(int t, String tag, Object[] objects) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_ITEM, proto.getCategory());
+        assertEquals(mKeyPackage, proto.getPackageName());
+        validateNotificationIdAndTag(proto, mId, tag);
+        assertEquals(MetricsEvent.TYPE_DETAIL, proto.getType());
+        return proto;
+    }
+
+    public void testAutoExpand() throws Throwable {
+        int t = 1000;
+        int byUser = 0;
+        int expanded = 1;
+        Object[] objects = new Object[3];
+        objects[0] = mKey;
+        objects[1] = byUser;
+        objects[2] = expanded;
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testCollapsedByUser() throws Throwable {
+        int t = 1000;
+        int byUser = 1;
+        int expanded = 0;
+        Object[] objects = new Object[3];
+        objects[0] = mKey;
+        objects[1] = byUser;
+        objects[2] = expanded;
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testAutoCollapsed() throws Throwable {
+        int t = 1000;
+        int byUser = 0;
+        int expanded = 0;
+        Object[] objects = new Object[3];
+        objects[0] = mKey;
+        objects[1] = byUser;
+        objects[2] = expanded;
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testMissingData() throws Throwable {
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testWrongType() throws Throwable {
+        Object[] objects = new Object[3];
+        objects[0] = 2;
+        objects[1] = 5;
+        objects[2] = 7;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testBadKey() throws Throwable {
+        Object[] objects = new Object[3];
+        objects[0] = "foo";
+        objects[1] = 5;
+        objects[2] = 2;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+    }
+
+    public void testMncTimestamps() throws Throwable {
+        int t = 1000;
+        int byUser = 1;
+        int expanded = 1;
+        Object[] objects = new Object[6];
+        objects[0] = mKey;
+        objects[1] = byUser;
+        objects[2] = expanded;
+        objects[3] = mSinceCreationMillis;
+        objects[4] = mSinceUpdateMillis;
+        objects[5] = mSinceVisibleMillis;
+
+        LogMaker proto = validateGoodData(t, "", objects);
+        validateNotificationTimes(proto, mSinceCreationMillis, mSinceUpdateMillis,
+                mSinceVisibleMillis);
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        int t = 1000;
+        int byUser = 1;
+        int expanded = 1;
+        Object[] objects = new Object[7];
+        objects[0] = mKey;
+        objects[1] = byUser;
+        objects[2] = expanded;
+        objects[3] = mSinceCreationMillis;
+        objects[4] = mSinceUpdateMillis;
+        objects[5] = mSinceVisibleMillis;
+        objects[6] = "foo";
+
+        validateGoodData(t, "", objects);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationKeyTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationKeyTest.java
new file mode 100644
index 0000000..b509700
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationKeyTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.test.InstrumentationTestCase;
+
+public class NotificationKeyTest extends InstrumentationTestCase {
+
+    private final NotificationKey mKey;
+
+    public NotificationKeyTest() {
+        mKey = new NotificationKey();
+    }
+
+    public void testGoodKey() throws Throwable {
+        assertTrue(mKey.parse("1|com.android.example.notificationshowcase|31338|null|10090"));
+
+        assertEquals("com.android.example.notificationshowcase", mKey.mPackageName);
+        assertEquals("", mKey.mTag);
+        assertEquals(31338, mKey.mId);
+        assertEquals(1, mKey.mUser);
+        assertEquals(10090, mKey.mUid);
+    }
+
+    public void testTaggedKey() throws Throwable {
+        assertTrue(mKey.parse("1|com.android.example.notificationshowcase|31338|foo|10090"));
+
+        assertEquals("com.android.example.notificationshowcase", mKey.mPackageName);
+        assertEquals("foo", mKey.mTag);
+        assertEquals(31338, mKey.mId);
+        assertEquals(1, mKey.mUser);
+        assertEquals(10090, mKey.mUid);
+    }
+
+    public void testEmptyTag() throws Throwable {
+        assertTrue(mKey.parse("1|com.android.example.notificationshowcase|31338||10090"));
+
+        assertEquals("com.android.example.notificationshowcase", mKey.mPackageName);
+        assertEquals("", mKey.mTag);
+        assertEquals(31338, mKey.mId);
+        assertEquals(1, mKey.mUser);
+        assertEquals(10090, mKey.mUid);
+    }
+
+    public void testBadKeys() throws Throwable {
+        assertFalse(mKey.parse(null));
+        assertFalse(mKey.parse(""));
+        assertFalse(mKey.parse("foo"));  // not a key
+        assertFalse(mKey.parse("1|com.android.example.notificationshowcase|31338|null"));
+        assertFalse(mKey.parse("bar|com.android.example.notificationshowcase|31338|null|10090"));
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelHiddenParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelHiddenParserTest.java
new file mode 100644
index 0000000..ce6f1f4
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelHiddenParserTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationPanelHiddenParserTest extends ParserTest {
+
+    public NotificationPanelHiddenParserTest() {
+        mParser = new NotificationPanelHiddenParser();
+    }
+
+    public void testNoInput() throws Throwable {
+        int t = 1000;
+        Object[] objects = new Object[0];
+
+        validateGoodData(t, objects);
+
+    }
+
+    public void testIgnoreExtraneousInput() throws Throwable {
+        Object[] objects = new Object[1];
+        objects[0] = "nothing to see here";
+
+        validateGoodData(0, objects);
+    }
+
+    private void validateGoodData(int t, Object[] objects) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_PANEL, proto.getCategory());
+        assertEquals(MetricsEvent.TYPE_CLOSE, proto.getType());
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelRevealedParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelRevealedParserTest.java
new file mode 100644
index 0000000..9e15812
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelRevealedParserTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationPanelRevealedParserTest extends ParserTest {
+
+    public NotificationPanelRevealedParserTest() {
+        mParser = new NotificationPanelRevealedParser();
+    }
+
+    public void testLollipopInput() throws Throwable {
+        int t = 1000;
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_PANEL, proto.getCategory());
+        assertEquals(MetricsEvent.TYPE_OPEN, proto.getType());
+    }
+
+    public void testMncData() throws Throwable {
+        int t = 1000;
+        int n = 5;
+        Object[] objects = new Object[1];
+        objects[0] = Integer.valueOf(n);
+
+        validateMncData(t, n, objects);
+    }
+
+    private void validateMncData(int t, int n, Object[] objects) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_PANEL, proto.getCategory());
+        assertEquals(MetricsEvent.TYPE_OPEN, proto.getType());
+    }
+
+    public void testBadInput() throws Throwable {
+        Object[] objects = new Object[1];
+        objects[0] = "This is not the integer you're looking for.";
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, times(1)).addEvent((LogMaker) anyObject());
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        int t = 1000;
+        int n = 5;
+        Object[] objects = new Object[2];
+        objects[0] = Integer.valueOf(n);
+        objects[1] = "foo";
+
+        validateMncData(t, n, objects);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationVisibilityParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationVisibilityParserTest.java
new file mode 100644
index 0000000..7fef929
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationVisibilityParserTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationVisibilityParserTest extends ParserTest {
+    private final int mCreationTime = 23124;
+    private final int mUpdateTime = 3412;
+    private final int mTime = 1000;
+
+    public NotificationVisibilityParserTest() {
+        mParser = new NotificationVisibilityParser();
+    }
+
+    public void testReveal() throws Throwable {
+        Object[] objects = new Object[4];
+        objects[0] = mTaggedKey;
+        objects[1] = 1;
+        objects[2] = mCreationTime;
+        objects[3] = mUpdateTime;
+
+        validateInteraction(true, mUpdateTime, 0, objects);
+    }
+
+    public void testHide() throws Throwable {
+        Object[] objects = new Object[4];
+        objects[0] = mTaggedKey;
+        objects[1] = 0;
+        objects[2] = mCreationTime;
+        objects[3] = mUpdateTime;
+
+        validateInteraction(false, mUpdateTime, 0, objects);
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        Object[] objects = new Object[5];
+        objects[0] = mTaggedKey;
+        objects[1] = 1;
+        objects[2] = mCreationTime;
+        objects[3] = mUpdateTime;
+        objects[4] = "foo";
+
+        validateInteraction(true, mUpdateTime, 0, objects);
+    }
+
+    public void testMarshmallowIndexData() throws Throwable {
+        Object[] objects = new Object[6];
+        objects[0] = mTaggedKey;
+        objects[1] = 1;
+        objects[2] = mCreationTime;
+        objects[3] = mUpdateTime;
+        objects[4] = 0;
+        objects[5] = 3;
+
+        validateInteraction(true, mUpdateTime, 3, objects);
+    }
+
+    private void validateInteraction(boolean visible, int freshness, int index, Object[] objects)    {
+        mParser.parseEvent(mLogger, mTime, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(mTime, proto.getTimestamp());
+        assertEquals(MetricsEvent.NOTIFICATION_ITEM, proto.getCategory());
+        assertEquals(mKeyPackage, proto.getPackageName());
+        validateNotificationIdAndTag(proto, mId, mTag);
+        validateNotificationTimes(proto, mCreationTime, mUpdateTime);
+        assertEquals(index, proto.getTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX));
+        assertEquals(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE, proto.getType());
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java
new file mode 100644
index 0000000..4adf629
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+
+import junit.framework.TestCase;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.OngoingStubbing;
+
+/**
+ * Common functions and temporaries for parser tests.
+ */
+public class ParserTest extends TestCase {
+    @Mock
+    protected TronLogger mLogger;
+
+    protected TagParser mParser;
+
+    protected LogMaker[] mProto;
+    protected ArgumentCaptor<LogMaker> mProtoCaptor;
+    protected ArgumentCaptor<String> mNameCaptor;
+    protected ArgumentCaptor<Integer> mCountCaptor;
+    protected String mKey = "0|com.android.example.notificationshowcase|31338|null|10090";
+    protected String mTaggedKey = "0|com.android.example.notificationshowcase|31338|badger|10090";
+    protected String mKeyPackage = "com.android.example.notificationshowcase";
+    protected String mTag = "badger";
+    protected int mId = 31338;
+    protected int mSinceCreationMillis = 5000;
+    protected int mSinceUpdateMillis = 1012;
+    protected int mSinceVisibleMillis = 323;
+
+
+    public ParserTest() {
+        mProto = new LogMaker[5];
+        for (int i = 0; i < mProto.length; i++) {
+            mProto[i] = new LogMaker(MetricsEvent.VIEW_UNKNOWN);
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        MockitoAnnotations.initMocks(this);
+
+        mProtoCaptor = ArgumentCaptor.forClass(LogMaker.class);
+        mNameCaptor = ArgumentCaptor.forClass(String.class);
+        mCountCaptor = ArgumentCaptor.forClass(Integer.class);
+
+        OngoingStubbing<LogMaker> stub = when(mLogger.obtain()).thenReturn(mProto[0]);
+        for (int i = 1; i < mProto.length; i++) {
+            stub.thenReturn(mProto[i]);
+        }
+        doNothing().when(mLogger).addEvent(any(LogMaker.class));
+        doNothing().when(mLogger).incrementBy(anyString(), anyInt());
+    }
+
+    protected void validateNotificationTimes(LogMaker proto, int life, int freshness,
+            int exposure) {
+        validateNotificationTimes(proto, life, freshness);
+        if (exposure != 0) {
+            assertEquals(exposure,
+                proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS));
+        } else {
+            assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS));
+        }
+    }
+
+    protected void validateNotificationTimes(LogMaker proto, int life, int freshness) {
+        if (life != 0) {
+            assertEquals(life,
+                proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS));
+        } else {
+            assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS));
+        }
+        if (freshness != 0) {
+            assertEquals(freshness,
+                proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS));
+        } else {
+            assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS));
+        }
+    }
+
+    protected void validateNotificationIdAndTag(LogMaker proto, int id, String tag) {
+        assertEquals(tag, proto.getTaggedData(MetricsEvent.NOTIFICATION_TAG));
+        assertEquals(id, proto.getTaggedData(MetricsEvent.NOTIFICATION_ID));
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java
new file mode 100644
index 0000000..b480e61
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class PowerScreenStateParserTest extends ParserTest {
+
+    public PowerScreenStateParserTest() {
+        mParser = new PowerScreenStateParser();
+    }
+
+    public void testScreenOn() throws Throwable {
+        validate(MetricsEvent.TYPE_OPEN, 0, "1,0,0,0");
+    }
+
+    public void testTimeout() throws Throwable {
+        validate(MetricsEvent.TYPE_CLOSE, 3, "0,3,0,0");
+    }
+
+    public void testUser() throws Throwable {
+        validate(MetricsEvent.TYPE_CLOSE, 2, "0,2,0,0");
+    }
+
+    public void testAdmin() throws Throwable {
+        validate(MetricsEvent.TYPE_CLOSE, 1, "0,1,0,0");
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        validate(MetricsEvent.TYPE_OPEN, 0, "1,0,0,0,5");
+    }
+
+    private void validate(int type, int subType, String log) {
+        String[] parts = log.split(",");
+        int t = 1000;
+        Object[] objects = new Object[parts.length];
+        for (int i = 0; i < parts.length; i++) {
+            objects[i] = Integer.valueOf(parts[i]);
+        }
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(type, proto.getType());
+        assertEquals(MetricsEvent.SCREEN, proto.getCategory());
+        assertEquals(subType, proto.getSubtype());
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/StatusBarStateParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/StatusBarStateParserTest.java
new file mode 100644
index 0000000..def9628
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/StatusBarStateParserTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class StatusBarStateParserTest extends ParserTest {
+
+    public StatusBarStateParserTest() {
+        mParser = new StatusBarStateParser();
+    }
+
+    public void testLockScreen() throws Throwable {
+        validate(MetricsEvent.LOCKSCREEN, MetricsEvent.TYPE_OPEN, 1, "1,1,0,0,1,0");
+    }
+
+    public void testBounce() throws Throwable {
+        validate(MetricsEvent.BOUNCER, MetricsEvent.TYPE_OPEN, 1, "1,1,0,1,1,0");
+    }
+
+    public void testUnlock() throws Throwable {
+        validate(MetricsEvent.LOCKSCREEN, MetricsEvent.TYPE_CLOSE, 1, "0,0,0,0,1,0");
+    }
+
+    public void testSecure() throws Throwable {
+        validate(MetricsEvent.BOUNCER, MetricsEvent.TYPE_OPEN, 1, "2,1,0,1,1,0");
+    }
+
+    public void testInsecure() throws Throwable {
+        validate(MetricsEvent.LOCKSCREEN, MetricsEvent.TYPE_OPEN, 0, "1,1,0,0,0,0");
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        validate(MetricsEvent.LOCKSCREEN, MetricsEvent.TYPE_OPEN, 0, "1,1,0,0,0,0,5");
+    }
+
+    private void validate(int view, int type, int subType, String log) {
+        String[] parts = log.split(",");
+        int t = 1000;
+        Object[] objects = new Object[parts.length];
+        for (int i = 0; i < parts.length; i++) {
+            objects[i] = Integer.valueOf(parts[i]);
+        }
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(view, proto.getCategory());
+        assertEquals(type, proto.getType());
+        assertEquals(subType, proto.getSubtype());
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiActionParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiActionParserTest.java
new file mode 100644
index 0000000..2ad76c1
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiActionParserTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class SysuiActionParserTest extends ParserTest {
+
+    public SysuiActionParserTest() {
+        mParser = new SysuiActionParser();
+    }
+
+    public void testGoodDatal() throws Throwable {
+        int t = 1000;
+        int view = 10;
+        Object[] objects = new Object[1];
+        objects[0] = view;
+
+        validateGoodData(t, view, objects);
+    }
+
+    private void validateGoodData(int t, int view, Object[] objects) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(view, proto.getCategory());
+        assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+    }
+
+    public void testGoodDataWithPackage() throws Throwable {
+        int t = 1000;
+        int view = 10;
+        String packageName = "com.foo";
+        Object[] objects = new Object[2];
+        objects[0] = view;
+        objects[1] = packageName;
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(view, proto.getCategory());
+        assertEquals(packageName, proto.getPackageName());
+        assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+    }
+
+    public void testGoodDataWithTrue() throws Throwable {
+        validateSubType(Boolean.toString(true), 1);
+    }
+
+    public void testGoodDataWithFalse() throws Throwable {
+        validateSubType(Boolean.toString(false), 0);
+    }
+
+    public void testGoodDataWithIntZero() throws Throwable {
+        validateSubType(Integer.toString(0), 0);
+    }
+
+    public void testGoodDataWithIntONe() throws Throwable {
+        validateSubType(Integer.toString(1), 1);
+    }
+
+    public void testGoodDataWithIntTwo() throws Throwable {
+        validateSubType(Integer.toString(2), 2);
+    }
+
+    public void testGoodDataWithNegativeInt() throws Throwable {
+        validateSubType(Integer.toString(-1), -1);
+    }
+
+    public void testGoodDataWithIntLarge() throws Throwable {
+        validateSubType(Integer.toString(120312), 120312);
+    }
+
+    public void testGoodDataWithNegativeIntLarge() throws Throwable {
+        validateSubType(Integer.toString(-120312), -120312);
+    }
+
+    private void validateSubType(String arg, int expectedValue) {
+        int t = 1000;
+        int view = 10;
+        Object[] objects = new Object[2];
+        objects[0] = view;
+        objects[1] = arg;
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(view, proto.getCategory());
+        assertEquals(expectedValue, proto.getSubtype());
+        assertNull(proto.getPackageName());
+        assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+    }
+
+    public void testIgnoreMissingInput() throws Throwable {
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreWrongInputs() throws Throwable {
+        Object[] objects = new Object[2];
+        objects[0] = "nothing to see here";
+        objects[1] = 10;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreStringViewInput() throws Throwable {
+        Object[] objects = new Object[1];
+        objects[0] = "this is not the input you are looking for";
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        int t = 1000;
+        int view = 10;
+        Object[] objects = new Object[2];
+        objects[0] = view;
+        objects[1] = "foo";
+
+        validateGoodData(t, view, objects);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java
new file mode 100644
index 0000000..e7a05d8
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+
+public class SysuiMultiActionParserTest extends ParserTest {
+
+    public SysuiMultiActionParserTest() {
+        mParser = new SysuiMultiActionParser();
+    }
+
+    public void testParseAllFields() {
+        int category = 10;
+        int type = 11;
+        int subtype = 12;
+        long timestamp = 1484669007890L;
+        String packageName = "com.foo.bar";
+        String counterName = "sheep";
+        int bucket = 13;
+        int value = 14;
+        LogMaker builder = new LogMaker(category);
+        builder.setType(type);
+        builder.setSubtype(subtype);
+        builder.setPackageName(packageName);
+        builder.setCounterName(counterName);
+        builder.setCounterBucket(bucket);
+        builder.setCounterValue(value);
+        builder.addTaggedData(1, "one");
+        builder.addTaggedData(2, "two");
+        Object[] out = builder.serialize();
+        int t = 1000;
+
+        mParser.parseEvent(mLogger, timestamp, out);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(category, proto.getCategory());
+        assertEquals(type, proto.getType());
+        assertEquals(subtype, proto.getSubtype());
+        assertEquals(timestamp, proto.getTimestamp());
+        assertEquals(packageName, proto.getPackageName());
+        assertEquals(counterName, proto.getCounterName());
+        assertEquals(bucket, proto.getCounterBucket());
+        assertEquals(value, proto.getCounterValue());
+        assertEquals("one", proto.getTaggedData(1));
+        assertEquals("two", proto.getTaggedData(2));
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiViewVisibilityParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiViewVisibilityParserTest.java
new file mode 100644
index 0000000..64d69a4
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiViewVisibilityParserTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.metrics.LogMaker;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class SysuiViewVisibilityParserTest extends ParserTest {
+
+    public SysuiViewVisibilityParserTest() {
+        mParser = new SysuiViewVisibilityParser();
+    }
+
+    public void testViewReveal() throws Throwable {
+        int t = 1000;
+        int view = 10;
+        Object[] objects = new Object[2];
+        objects[0] = view;
+        objects[1] = 100;
+
+        validateViewReveal(t, view, objects);
+    }
+
+    private void validateViewReveal(int t, int view, Object[] objects) {
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(t, proto.getTimestamp());
+        assertEquals(view, proto.getCategory());
+        assertEquals(MetricsEvent.TYPE_OPEN, proto.getType());
+    }
+
+    public void testViewHidden() throws Throwable {
+        int t = 1000;
+        int view = 10;
+        Object[] objects = new Object[2];
+        objects[0] = view;
+        objects[1] = 0;
+
+        mParser.parseEvent(mLogger, t, objects);
+
+        verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+        LogMaker proto = mProtoCaptor.getValue();
+        assertEquals(MetricsEvent.TYPE_CLOSE, proto.getType());
+    }
+
+    public void testIgnoreMissingInput() throws Throwable {
+        Object[] objects = new Object[0];
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreStringInARgOne() throws Throwable {
+        Object[] objects = new Object[2];
+        objects[0] = "nothing to see here";
+        objects[1] = 100;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreStringInArgTwo() throws Throwable {
+        Object[] objects = new Object[2];
+        objects[0] = 100;
+        objects[1] = "nothing to see here";
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testOneInput() throws Throwable {
+        Object[] objects = new Object[1];
+        objects[0] = 100;
+
+        mParser.parseEvent(mLogger, 0, objects);
+
+        verify(mLogger, never()).addEvent((LogMaker) anyObject());
+        verify(mLogger, never()).incrementBy(anyString(), anyInt());
+    }
+
+    public void testIgnoreUnexpectedData() throws Throwable {
+        int t = 1000;
+        int view = 10;
+        Object[] objects = new Object[3];
+        objects[0] = view;
+        objects[1] = 100;
+        objects[2] = "foo";
+
+        validateViewReveal(t, view, objects);
+    }
+}
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
index 97e8b1f..47ee2cf 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver
+LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver junit legacy-android-test
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_PACKAGE_NAME := DownloadManagerTestApp
diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk
index 702218c..0551eb6 100644
--- a/core/tests/notificationtests/Android.mk
+++ b/core/tests/notificationtests/Android.mk
@@ -12,6 +12,8 @@
 LOCAL_PACKAGE_NAME := NotificationStressTests
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    junit \
+    legacy-android-test \
     ub-uiautomator
 
 include $(BUILD_PACKAGE)
diff --git a/core/tests/utillib/Android.mk b/core/tests/utillib/Android.mk
index 299ea5a..8811256 100644
--- a/core/tests/utillib/Android.mk
+++ b/core/tests/utillib/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_MODULE := frameworks-core-util-lib
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 6718259..b2c6840 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -18,6 +18,14 @@
 
 ########################
 include $(CLEAR_VARS)
+LOCAL_MODULE := framework-sysconfig.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+########################
+include $(CLEAR_VARS)
 LOCAL_MODULE := platform.xml
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
@@ -31,4 +39,3 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 include $(BUILD_PREBUILT)
-
diff --git a/data/etc/framework-sysconfig.xml b/data/etc/framework-sysconfig.xml
new file mode 100644
index 0000000..62ef25b
--- /dev/null
+++ b/data/etc/framework-sysconfig.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- These are configurations that must exist on all Android devices. -->
+<config>
+
+    <!-- Broadcast actions that are currently exempted from O+ background
+         delivery restrictions. -->
+    <allow-implicit-broadcast action="android.intent.action.SIM_STATE_CHANGED" />
+    <allow-implicit-broadcast action="android.intent.action.PACKAGE_CHANGED" />
+    <allow-implicit-broadcast action="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
+
+</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 05db9b0..c5961ab 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -110,11 +110,21 @@
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.omadm.service">
+        <permission name="android.permission.CHANGE_CONFIGURATION"/>
+        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.packageinstaller">
         <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.INSTALL_PACKAGES"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
     </privapp-permissions>
 
@@ -122,6 +132,7 @@
         <permission name="android.permission.ACCESS_IMS_CALL_SERVICE"/>
         <permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
+        <permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
         <permission name="android.permission.CALL_PRIVILEGED"/>
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
         <permission name="android.permission.CHANGE_CONFIGURATION"/>
@@ -292,6 +303,7 @@
         <permission name="android.permission.BIND_APPWIDGET"/>
         <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
+        <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
         <permission name="android.permission.CONTROL_VPN"/>
         <permission name="android.permission.DUMP"/>
@@ -327,4 +339,4 @@
         <permission name="android.permission.CONTROL_VPN"/>
     </privapp-permissions>
 
-</permissions>
+</permissions>
\ No newline at end of file
diff --git a/docs/html/about/versions/android-4.0.jd b/docs/html/about/versions/android-4.0.jd
index 48afcd4..421019c 100644
--- a/docs/html/about/versions/android-4.0.jd
+++ b/docs/html/about/versions/android-4.0.jd
@@ -309,7 +309,7 @@
 android.media.effect.EffectContext#getFactory EffectContext.getFactory()}, which returns an instance
 of {@link android.media.effect.EffectFactory}.</li>
 <li>Call {@link android.media.effect.EffectFactory#createEffect createEffect()}, passing it an
-effect name from @link android.media.effect.EffectFactory}, such as {@link
+effect name from {@link android.media.effect.EffectFactory}, such as {@link
 android.media.effect.EffectFactory#EFFECT_FISHEYE} or {@link
 android.media.effect.EffectFactory#EFFECT_VIGNETTE}.</li>
 </ol>
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 77e8d77..021a07e 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -565,6 +565,38 @@
 method, which indicates whether the screen is round.</p>
       </td>
     </tr>
+    <tr id="WideColorGamutQualifier">
+      <td>Wide Color Gamut</td>
+      <td>
+        <code>widecg</code><br/>
+        <code>nowidecg</code>
+      </td>
+      <td>
+        <ul class="nolist">
+          <li>{@code widecg}: Displays with a wide color gamut such as Display P3 or AdobeRGB</li>
+          <li>{@code nowidecg}: Displays with a narrow color gamut such as sRGB</li>
+        </ul>
+        <p><em>Added in API level 26.</em></p>
+        <p>Also see the {@link android.content.res.Configuration#isScreenWideColorGamut()} configuration
+        method, which indicates whether the screen has a wide color gamut.</p>
+      </td>
+    </tr>
+    <tr id="HDRQualifier">
+      <td>High Dynamic Range (HDR)</td>
+      <td>
+        <code>highdr</code><br/>
+        <code>lowdr</code>
+      </td>
+      <td>
+        <ul class="nolist">
+          <li>{@code highdr}: Displays with a high-dynamic range</li>
+          <li>{@code lowdr}: Displays with a low/standard dynamic range</li>
+        </ul>
+        <p><em>Added in API level 26.</em></p>
+        <p>Also see the {@link android.content.res.Configuration#isScreenHdr()} configuration
+        method, which indicates whether the screen has a HDR capabilities.</p>
+      </td>
+    </tr>
     <tr id="OrientationQualifier">
       <td>Screen orientation</td>
       <td>
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index f135484..2ebd2cc 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -58,6 +58,7 @@
             throw new RuntimeException("Canvas: trying to use a non-premultiplied bitmap "
                     + bitmap);
         }
+        throwIfHwBitmapInSwMode(bitmap);
     }
 
     protected final static void checkRange(int length, int offset, int count) {
@@ -80,12 +81,14 @@
 
     public void drawArc(float left, float top, float right, float bottom, float startAngle,
             float sweepAngle, boolean useCenter, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
                 useCenter, paint.getNativeInstance());
     }
 
     public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
             @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
                 paint);
     }
@@ -96,12 +99,14 @@
 
     public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
         throwIfCannotDraw(bitmap);
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawBitmap(mNativeCanvasWrapper, bitmap, left, top,
                 paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity,
                 bitmap.mDensity);
     }
 
     public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap, matrix.ni(),
                 paint != null ? paint.getNativeInstance() : 0);
     }
@@ -112,6 +117,7 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
+        throwIfHasHwBitmapInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
 
         int left, top, right, bottom;
@@ -137,6 +143,7 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
+        throwIfHasHwBitmapInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
 
         float left, top, right, bottom;
@@ -175,6 +182,7 @@
                 || (lastScanline + width > length)) {
             throw new ArrayIndexOutOfBoundsException();
         }
+        throwIfHasHwBitmapInSwMode(paint);
         // quick escape if there's nothing to draw
         if (width == 0 || height == 0) {
             return;
@@ -198,6 +206,7 @@
         if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
             throw new ArrayIndexOutOfBoundsException();
         }
+        throwIfHasHwBitmapInSwMode(paint);
         if (meshWidth == 0 || meshHeight == 0) {
             return;
         }
@@ -214,6 +223,7 @@
     }
 
     public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
     }
 
@@ -227,19 +237,23 @@
 
     public void drawLine(float startX, float startY, float stopX, float stopY,
             @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
     }
 
     public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
             @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
     }
 
     public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         drawLines(pts, 0, pts.length, paint);
     }
 
     public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
     }
 
@@ -247,6 +261,7 @@
         if (oval == null) {
             throw new NullPointerException();
         }
+        throwIfHasHwBitmapInSwMode(paint);
         drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
     }
 
@@ -257,6 +272,7 @@
     public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
+        throwIfHasHwBitmapInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
         nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -266,6 +282,7 @@
     public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
+        throwIfHasHwBitmapInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
         nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -273,6 +290,7 @@
     }
 
     public void drawPath(@NonNull Path path, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         if (path.isSimplePath && path.rects != null) {
             nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
         } else {
@@ -281,15 +299,18 @@
     }
 
     public void drawPoint(float x, float y, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
     }
 
     public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
             @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
     }
 
     public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         drawPoints(pts, 0, pts.length, paint);
     }
 
@@ -300,6 +321,7 @@
         if (index < 0 || index + count > text.length || count * 2 > pos.length) {
             throw new IndexOutOfBoundsException();
         }
+        throwIfHasHwBitmapInSwMode(paint);
         for (int i = 0; i < count; i++) {
             drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
         }
@@ -308,18 +330,22 @@
     @Deprecated
     public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
             @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
     }
 
     public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
     }
 
     public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         drawRect(r.left, r.top, r.right, r.bottom, paint);
     }
 
     public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawRect(mNativeCanvasWrapper,
                 rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
     }
@@ -330,11 +356,13 @@
 
     public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
             @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
                 paint.getNativeInstance());
     }
 
     public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
     }
 
@@ -344,6 +372,7 @@
                 (text.length - index - count)) < 0) {
             throw new IndexOutOfBoundsException();
         }
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
                 paint.getNativeInstance(), paint.mNativeTypeface);
     }
@@ -353,6 +382,7 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
+        throwIfHasHwBitmapInSwMode(paint);
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
             nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
@@ -370,6 +400,7 @@
     }
 
     public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
                 paint.getNativeInstance(), paint.mNativeTypeface);
     }
@@ -379,6 +410,7 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
                 paint.getNativeInstance(), paint.mNativeTypeface);
     }
@@ -388,6 +420,7 @@
         if (index < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
                 path.readOnlyNI(), hOffset, vOffset,
                 paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
@@ -396,6 +429,7 @@
     public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
             float vOffset, @NonNull Paint paint) {
         if (text.length() > 0) {
+            throwIfHasHwBitmapInSwMode(paint);
             nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
                     paint.mBidiFlags, paint.getNativeInstance(), paint.mNativeTypeface);
         }
@@ -416,6 +450,7 @@
             throw new IndexOutOfBoundsException();
         }
 
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
                 x, y, isRtl, paint.getNativeInstance(), paint.mNativeTypeface);
     }
@@ -434,6 +469,7 @@
             throw new IndexOutOfBoundsException();
         }
 
+        throwIfHasHwBitmapInSwMode(paint);
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
             nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
@@ -469,11 +505,38 @@
         if (indices != null) {
             checkRange(indices.length, indexOffset, indexCount);
         }
+        throwIfHasHwBitmapInSwMode(paint);
         nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
                 vertOffset, texs, texOffset, colors, colorOffset,
                 indices, indexOffset, indexCount, paint.getNativeInstance());
     }
 
+    private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
+        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+            throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
+        }
+    }
+
+    private void throwIfHasHwBitmapInSwMode(Paint p) {
+        if (isHardwareAccelerated() || p == null) {
+            return;
+        }
+        throwIfHasHwBitmapInSwMode(p.getShader());
+    }
+
+    private void throwIfHasHwBitmapInSwMode(Shader shader) {
+        if (shader == null) {
+            return;
+        }
+        if (shader instanceof BitmapShader) {
+            throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap);
+        }
+        if (shader instanceof ComposeShader) {
+            throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderA);
+            throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderB);
+        }
+    }
+
     private static native void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top,
             long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity);
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index cc5cc7b..8572345 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -21,11 +21,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
-import android.text.GraphicsOperations;
-import android.text.SpannableString;
-import android.text.SpannedString;
-import android.text.TextUtils;
 
+import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
 import libcore.util.NativeAllocationRegistry;
@@ -501,8 +498,10 @@
      * an error to call restore() more times than save() was called.
      */
     public void restore() {
-        boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated();
-        nRestore(mNativeCanvasWrapper, throwOnUnderflow);
+        if (!nRestore(mNativeCanvasWrapper)
+                && (!sCompatibilityRestore || !isHardwareAccelerated())) {
+            throw new IllegalStateException("Underflow in restore - more restores than saves");
+        }
     }
 
     /**
@@ -527,8 +526,16 @@
      * @param saveCount The save level to restore to.
      */
     public void restoreToCount(int saveCount) {
-        boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated();
-        nRestoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow);
+        if (saveCount < 1) {
+            if (!sCompatibilityRestore || !isHardwareAccelerated()) {
+                // do nothing and throw without restoring
+                throw new IllegalArgumentException(
+                        "Underflow in restoreToCount - more restores than saves");
+            }
+            // compat behavior - restore as far as possible
+            saveCount = 1;
+        }
+        nRestoreToCount(mNativeCanvasWrapper, saveCount);
     }
 
     /**
@@ -643,7 +650,7 @@
      */
     @Deprecated
     public void getMatrix(@NonNull Matrix ctm) {
-        nGetCTM(mNativeCanvasWrapper, ctm.native_instance);
+        nGetMatrix(mNativeCanvasWrapper, ctm.native_instance);
     }
 
     /**
@@ -1059,79 +1066,66 @@
     // ---------------- @FastNative -------------------
 
     @FastNative
-    private static native void nSetBitmap(long canvasHandle,
-                                                Bitmap bitmap);
+    private static native void nSetBitmap(long canvasHandle, Bitmap bitmap);
+
     @FastNative
+    private static native boolean nGetClipBounds(long nativeCanvas, Rect bounds);
+
+    // ---------------- @CriticalNative -------------------
+
+    @CriticalNative
     private static native boolean nIsOpaque(long canvasHandle);
-    @FastNative
+    @CriticalNative
     private static native void nSetHighContrastText(long renderer, boolean highContrastText);
-    @FastNative
+    @CriticalNative
     private static native int nGetWidth(long canvasHandle);
-    @FastNative
+    @CriticalNative
     private static native int nGetHeight(long canvasHandle);
 
-    @FastNative
+    @CriticalNative
     private static native int nSave(long canvasHandle, int saveFlags);
-    @FastNative
-    private static native int nSaveLayer(long nativeCanvas, float l,
-                                               float t, float r, float b,
-                                               long nativePaint,
-                                               int layerFlags);
-    @FastNative
-    private static native int nSaveLayerAlpha(long nativeCanvas, float l,
-                                                    float t, float r, float b,
-                                                    int alpha, int layerFlags);
-    @FastNative
-    private static native void nRestore(long canvasHandle, boolean tolerateUnderflow);
-    @FastNative
-    private static native void nRestoreToCount(long canvasHandle,
-                                                     int saveCount,
-                                                     boolean tolerateUnderflow);
-    @FastNative
+    @CriticalNative
+    private static native int nSaveLayer(long nativeCanvas, float l, float t, float r, float b,
+            long nativePaint, int layerFlags);
+    @CriticalNative
+    private static native int nSaveLayerAlpha(long nativeCanvas, float l, float t, float r, float b,
+            int alpha, int layerFlags);
+    @CriticalNative
+    private static native boolean nRestore(long canvasHandle);
+    @CriticalNative
+    private static native void nRestoreToCount(long canvasHandle, int saveCount);
+    @CriticalNative
     private static native int nGetSaveCount(long canvasHandle);
 
-    @FastNative
-    private static native void nTranslate(long canvasHandle,
-                                                float dx, float dy);
-    @FastNative
-    private static native void nScale(long canvasHandle,
-                                            float sx, float sy);
-    @FastNative
+    @CriticalNative
+    private static native void nTranslate(long canvasHandle, float dx, float dy);
+    @CriticalNative
+    private static native void nScale(long canvasHandle, float sx, float sy);
+    @CriticalNative
     private static native void nRotate(long canvasHandle, float degrees);
-    @FastNative
-    private static native void nSkew(long canvasHandle,
-                                           float sx, float sy);
-    @FastNative
-    private static native void nConcat(long nativeCanvas,
-                                             long nativeMatrix);
-    @FastNative
-    private static native void nSetMatrix(long nativeCanvas,
-                                                long nativeMatrix);
-    @FastNative
+    @CriticalNative
+    private static native void nSkew(long canvasHandle, float sx, float sy);
+    @CriticalNative
+    private static native void nConcat(long nativeCanvas, long nativeMatrix);
+    @CriticalNative
+    private static native void nSetMatrix(long nativeCanvas, long nativeMatrix);
+    @CriticalNative
     private static native boolean nClipRect(long nativeCanvas,
-                                                  float left, float top,
-                                                  float right, float bottom,
-                                                  int regionOp);
-    @FastNative
-    private static native boolean nClipPath(long nativeCanvas,
-                                                  long nativePath,
-                                                  int regionOp);
-    @FastNative
-    private static native void nSetDrawFilter(long nativeCanvas,
-                                                   long nativeFilter);
-    @FastNative
-    private static native boolean nGetClipBounds(long nativeCanvas,
-                                                       Rect bounds);
-    @FastNative
-    private static native void nGetCTM(long nativeCanvas,
-                                             long nativeMatrix);
-    @FastNative
-    private static native boolean nQuickReject(long nativeCanvas,
-                                                     long nativePath);
-    @FastNative
-    private static native boolean nQuickReject(long nativeCanvas,
-                                                     float left, float top,
-                                                     float right, float bottom);
+            float left, float top, float right, float bottom, int regionOp);
+    @CriticalNative
+    private static native boolean nClipPath(long nativeCanvas, long nativePath, int regionOp);
+    @CriticalNative
+    private static native void nSetDrawFilter(long nativeCanvas, long nativeFilter);
+    @CriticalNative
+    private static native void nGetMatrix(long nativeCanvas, long nativeMatrix);
+    @CriticalNative
+    private static native boolean nQuickReject(long nativeCanvas, long nativePath);
+    @CriticalNative
+    private static native boolean nQuickReject(long nativeCanvas, float left, float top,
+            float right, float bottom);
+
+
+    // ---------------- Draw Methods -------------------
 
     /**
      * <p>
diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java
index a2c104a..ff21cac 100644
--- a/graphics/java/android/graphics/Color.java
+++ b/graphics/java/android/graphics/Color.java
@@ -16,27 +16,278 @@
 
 package android.graphics;
 
+import android.annotation.AnyThread;
 import android.annotation.ColorInt;
+import android.annotation.ColorLong;
+import android.annotation.HalfFloat;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.Size;
 
+import android.util.Half;
 import com.android.internal.util.XmlUtils;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.function.DoubleUnaryOperator;
 
 /**
- * The Color class defines methods for creating and converting color ints.
- * Colors are represented as packed ints, made up of 4 bytes: alpha, red,
- * green, blue. The values are unpremultiplied, meaning any transparency is
- * stored solely in the alpha component, and not in the color components. The
- * components are stored as follows (alpha << 24) | (red << 16) |
- * (green << 8) | blue. Each component ranges between 0..255 with 0
- * meaning no contribution for that component, and 255 meaning 100%
- * contribution. Thus opaque-black would be 0xFF000000 (100% opaque but
- * no contributions from red, green, or blue), and opaque-white would be
- * 0xFFFFFFFF
+ * {@usesMathJax}
+ *
+ * <p>The <code>Color</code> class provides methods for creating, converting and
+ * manipulating colors. Colors have three different representations:</p>
+ * <ul>
+ *     <li>Color ints, the most common representation</li>
+ *     <li>Color longs</li>
+ *     <li><code>Color</code> instances</li>
+ * </ul>
+ * <p>The section below describe each representation in detail.</p>
+ *
+ * <h3>Color ints</h3>
+ * <p>Color ints are the most common representation of colors on Android and
+ * have been used since {@link android.os.Build.VERSION_CODES#BASE API level 1}.</p>
+ *
+ * <p>A color int always defines a color in the {@link ColorSpace.Named#SRGB sRGB}
+ * color space using 4 components packed in a single 32 bit integer value:</p>
+ *
+ * <table summary="Color int definition">
+ *     <tr>
+ *         <th>Component</th><th>Name</th><th>Size</th><th>Range</th>
+ *     </tr>
+ *     <tr><td>A</td><td>Alpha</td><td>8 bits</td><td>\([0..255]\)</td></tr>
+ *     <tr><td>R</td><td>Red</td><td>8 bits</td><td>\([0..255]\)</td></tr>
+ *     <tr><td>G</td><td>Green</td><td>8 bits</td><td>\([0..255]\)</td></tr>
+ *     <tr><td>B</td><td>Blue</td><td>8 bits</td><td>\([0..255]\)</td></tr>
+ * </table>
+ *
+ * <p>The components in this table are listed in encoding order (see below),
+ * which is why color ints are called ARGB colors.</p>
+ *
+ * <h4>Usage in code</h4>
+ * <p>To avoid confusing color ints with arbitrary integer values, it is a
+ * good practice to annotate them with the <code>@ColorInt</code> annotation
+ * found in the Android Support Library.</p>
+ *
+ * <h4>Encoding</h4>
+ * <p>The four components of a color int are encoded in the following way:</p>
+ * <pre class="prettyprint">
+ * int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 16 | (B & 0xff);
+ * </pre>
+ *
+ * <p>Because of this encoding, color ints can easily be described as an integer
+ * constant in source. For instance, opaque blue is <code>0xff0000ff</code>
+ * and yellow is <code>0xffffff00</code>.</p>
+ *
+ * <p>To easily encode color ints, it is recommended to use the static methods
+ * {@link #argb(int, int, int, int)} and {@link #rgb(int, int, int)}. The second
+ * method omits the alpha component and assumes the color is opaque (alpha is 255).
+ * As a convenience this class also offers methods to encode color ints from components
+ * defined in the \([0..1]\) range: {@link #argb(float, float, float, float)} and
+ * {@link #rgb(float, float, float)}.</p>
+ *
+ * <p>Color longs (defined below) can be easily converted to color ints by invoking
+ * the {@link #toArgb(long)} method. This method performs a color space conversion
+ * if needed.</p>
+ *
+ * <p>It is also possible to create a color int by invoking the method {@link #toArgb()}
+ * on a color instance.</p>
+ *
+ * <h4>Decoding</h4>
+ * <p>The four ARGB components can be individually extracted from a color int
+ * using the following expressions:</p>
+ * <pre class="prettyprint">
+ * int A = (color >> 24) & 0xff; // or color >>> 24
+ * int R = (color >> 16) & 0xff;
+ * int G = (color >>  8) & 0xff;
+ * int B = (color      ) & 0xff;
+ * </pre>
+ *
+ * <p>This class offers convenience methods to easily extract these components:</p>
+ * <ul>
+ *     <li>{@link #alpha(int)} to extract the alpha component</li>
+ *     <li>{@link #red(int)} to extract the red component</li>
+ *     <li>{@link #green(int)} to extract the green component</li>
+ *     <li>{@link #blue(int)} to extract the blue component</li>
+ * </ul>
+ *
+ * <h3>Color longs</h3>
+ * <p>Color longs are a representation introduced in
+ * {@link android.os.Build.VERSION_CODES#O Android O} to store colors in different
+ * {@link ColorSpace color spaces}, with more precision than color ints.</p>
+ *
+ * <p>A color long always defines a color using 4 components packed in a single
+ * 64 bit long value. One of these components is always alpha while the other
+ * three components depend on the color space's {@link ColorSpace.Model color model}.
+ * The most common color model is the {@link ColorSpace.Model#RGB RGB} model in
+ * which the components represent red, green and blue values.</p>
+ *
+ * <p class="note"><b>Component ranges:</b> the ranges defined in the tables
+ * below indicate the ranges that can be encoded in a color long. They do not
+ * represent the actual ranges as they may differ per color space. For instance,
+ * the RGB components of a color in the {@link ColorSpace.Named#DISPLAY_P3 Display P3}
+ * color space use the \([0..1]\) range. Please refer to the documentation of the
+ * various {@link ColorSpace.Named color spaces} to find their respective ranges.</p>
+ *
+ * <p class="note"><b>Alpha range:</b> while alpha is encoded in a color long using
+ * a 10 bit integer (thus using a range of \([0..1023]\)), it is converted to and
+ * from \([0..1]\) float values when decoding and encoding color longs.</p>
+ *
+ * <p class="note"><b>sRGB color space:</b> for compatibility reasons and ease of
+ * use, color longs encoding {@link ColorSpace.Named#SRGB sRGB} colors do not
+ * use the same encoding as other color longs.</p>
+ *
+ * <table summary="Color long definition">
+ *     <tr>
+ *         <th>Component</th><th>Name</th><th>Size</th><th>Range</th>
+ *     </tr>
+ *     <tr><td colspan="4">{@link ColorSpace.Model#RGB RGB} color model</td></tr>
+ *     <tr><td>R</td><td>Red</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>G</td><td>Green</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>B</td><td>Blue</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>A</td><td>Alpha</td><td>10 bits</td><td>\([0..1023]\)</td></tr>
+ *     <tr><td></td><td>Color space</td><td>6 bits</td><td>\([0..63]\)</td></tr>
+ *     <tr><td colspan="4">{@link ColorSpace.Named#SRGB sRGB} color space</td></tr>
+ *     <tr><td>A</td><td>Alpha</td><td>8 bits</td><td>\([0..255]\)</td></tr>
+ *     <tr><td>R</td><td>Red</td><td>8 bits</td><td>\([0..255]\)</td></tr>
+ *     <tr><td>G</td><td>Green</td><td>8 bits</td><td>\([0..255]\)</td></tr>
+ *     <tr><td>B</td><td>Blue</td><td>8 bits</td><td>\([0..255]\)</td></tr>
+ *     <tr><td>X</td><td>Unused</td><td>32 bits</td><td>\(0\)</td></tr>
+ *     <tr><td colspan="4">{@link ColorSpace.Model#XYZ XYZ} color model</td></tr>
+ *     <tr><td>X</td><td>X</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>Y</td><td>Y</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>Z</td><td>Z</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>A</td><td>Alpha</td><td>10 bits</td><td>\([0..1023]\)</td></tr>
+ *     <tr><td></td><td>Color space</td><td>6 bits</td><td>\([0..63]\)</td></tr>
+ *     <tr><td colspan="4">{@link ColorSpace.Model#XYZ Lab} color model</td></tr>
+ *     <tr><td>L</td><td>L</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>a</td><td>a</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>b</td><td>b</td><td>16 bits</td><td>\([-65504.0, 65504.0]\)</td></tr>
+ *     <tr><td>A</td><td>Alpha</td><td>10 bits</td><td>\([0..1023]\)</td></tr>
+ *     <tr><td></td><td>Color space</td><td>6 bits</td><td>\([0..63]\)</td></tr>
+ *     <tr><td colspan="4">{@link ColorSpace.Model#CMYK CMYK} color model</td></tr>
+ *     <tr><td colspan="4">Unsupported</td></tr>
+ * </table>
+ *
+ * <p>The components in this table are listed in encoding order (see below),
+ * which is why color longs in the RGB model are called RGBA colors (even if
+ * this doesn't quite hold for the special case of sRGB colors).</p>
+ *
+ * <p>The color long encoding relies on half-precision float values (fp16). If you
+ * wish to know more about the limitations of half-precision float values, please
+ * refer to the documentation of the {@link Half} class.</p>
+ *
+ * <h4>Usage in code</h4>
+ * <p>To avoid confusing color longs with arbitrary long values, it is a
+ * good practice to annotate them with the <code>@ColorLong</code> annotation
+ * found in the Android Support Library.</p>
+ *
+ * <h4>Encoding</h4>
+ *
+ * <p>Given the complex nature of color longs, it is strongly encouraged to use
+ * the various methods provided by this class to encode them.</p>
+ *
+ * <p>The most flexible way to encode a color long is to use the method
+ * {@link #pack(float, float, float, float, ColorSpace)}. This method allows you
+ * to specify three color components (typically RGB), an alpha component and a
+ * color space. To encode sRGB colors, use {@link #pack(float, float, float)}
+ * and {@link #pack(float, float, float, float)} which are the
+ * equivalent of {@link #rgb(int, int, int)} and {@link #argb(int, int, int, int)}
+ * for color ints. If you simply need to convert a color int into a color long,
+ * use {@link #pack(int)}.</p>
+ *
+ * <p>It is also possible to create a color long value by invoking the method
+ * {@link #pack()} on a color instance.</p>
+ *
+ * <h4>Decoding</h4>
+ *
+ * <p>This class offers convenience methods to easily extract the components
+ * of a color long:</p>
+ * <ul>
+ *     <li>{@link #alpha(long)} to extract the alpha component</li>
+ *     <li>{@link #red(long)} to extract the red/X/L component</li>
+ *     <li>{@link #green(long)} to extract the green/Y/a component</li>
+ *     <li>{@link #blue(long)} to extract the blue/Z/b component</li>
+ * </ul>
+ *
+ * <p>The values returned by these methods depend on the color space encoded
+ * in the color long. The values are however typically in the \([0..1]\) range
+ * for RGB colors. Please refer to the documentation of the various
+ * {@link ColorSpace.Named color spaces} for the exact ranges.</p>
+ *
+ * <h3>Color instances</h3>
+ * <p>Color instances are a representation introduced in
+ * {@link android.os.Build.VERSION_CODES#O Android O} to store colors in different
+ * {@link ColorSpace color spaces}, with more precision than both color ints and
+ * color longs. Color instances also offer the ability to store more than 4
+ * components if necessary.</p>
+ *
+ * <p>Colors instances are immutable and can be created using one of the various
+ * <code>valueOf</code> methods. For instance:</p>
+ * <pre class="prettyprint">
+ * // sRGB
+ * Color opaqueRed = Color.valueOf(0xffff0000); // from a color int
+ * Color translucentRed = Color.valueOf(1.0f, 0.0f, 0.0f, 0.5f);
+ *
+ * // Wide gamut color
+ * {@literal @}ColorLong long p3 = pack(1.0f, 1.0f, 0.0f, 1.0f, colorSpaceP3);
+ * Color opaqueYellow = Color.valueOf(p3); // from a color long
+ *
+ * // CIE L*a*b* color space
+ * ColorSpace lab = ColorSpace.get(ColorSpace.Named.LAB);
+ * Color green = Color.valueOf(100.0f, -128.0f, 128.0f, 1.0f, lab);
+ * </pre>
+ *
+ * <p>Color instances can be converted to color ints ({@link #toArgb()}) or
+ * color longs ({@link #pack()}). They also offer easy access to their various
+ * components using the following methods:</p>
+ * <ul>
+ *     <li>{@link #alpha()}, returns the alpha component value</li>
+ *     <li>{@link #red()}, returns the red component value (or first
+ *     component value in non-RGB models)</li>
+ *     <li>{@link #green()}, returns the green component value (or second
+ *     component value in non-RGB models)</li>
+ *     <li>{@link #blue()}, returns the blue component value (or third
+ *     component value in non-RGB models)</li>
+ *     <li>{@link #getComponent(int)}, returns a specific component value</li>
+ *     <li>{@link #getComponents()}, returns all component values as an array</li>
+ * </ul>
+ *
+ * <h3>Color space conversions</h3>
+ * <p>You can convert colors from one color space to another using
+ * {@link ColorSpace#connect(ColorSpace, ColorSpace)} and its variants. However,
+ * the <code>Color</code> class provides a few convenience methods to simplify
+ * the process. Here is a brief description of some of them:</p>
+ * <ul>
+ *     <li>{@link #convert(ColorSpace)} to convert a color instance in a color
+ *     space to a new color instance in a different color space</li>
+ *     <li>{@link #convert(float, float, float, float, ColorSpace, ColorSpace)} to
+ *     convert a color from a source color space to a destination color space</li>
+ *     <li>{@link #convert(long, ColorSpace)} to convert a color long from its
+ *     built-in color space to a destination color space</li>
+ *     <li>{@link #convert(int, ColorSpace)} to convert a color int from sRGB
+ *     to a destination color space</li>
+ * </ul>
+ *
+ * <p>Please refere to the {@link ColorSpace} documentation for more
+ * information.</p>
+ *
+ * <h3>Alpha and transparency</h3>
+ * <p>The alpha component of a color defines the level of transparency of a
+ * color. When the alpha component is 0, the color is completely transparent.
+ * When the alpha is component is 1 (in the \([0..1]\) range) or 255 (in the
+ * \([0..255]\) range), the color is completely opaque.</p>
+ *
+ * <p>The color representations described above do not use pre-multiplied
+ * color components (a pre-multiplied color component is a color component
+ * that has been multiplied by the value of the alpha component).
+ * For instance, the color int representation of opaque red is
+ * <code>0xffff0000</code>. For semi-transparent (50%) red, the
+ * representation becomes <code>0x80ff0000</code>. The equivalent color
+ * instance representations would be <code>(1.0, 0.0, 0.0, 1.0)</code>
+ * and <code>(1.0, 0.0, 0.0, 0.5)</code>.</p>
  */
+@AnyThread
 public class Color {
     @ColorInt public static final int BLACK       = 0xFF000000;
     @ColorInt public static final int DKGRAY      = 0xFF444444;
@@ -51,10 +302,905 @@
     @ColorInt public static final int MAGENTA     = 0xFFFF00FF;
     @ColorInt public static final int TRANSPARENT = 0;
 
+    @NonNull
+    @Size(min = 4, max = 5)
+    private final float[] mComponents;
+
+    @NonNull
+    private final ColorSpace mColorSpace;
+
+    /**
+     * Creates a new color instance set to opaque black in the
+     * {@link ColorSpace.Named#SRGB sRGB} color space.
+     *
+     * @see #valueOf(float, float, float)
+     * @see #valueOf(float, float, float, float)
+     * @see #valueOf(float, float, float, float, ColorSpace)
+     * @see #valueOf(float[], ColorSpace)
+     * @see #valueOf(int)
+     * @see #valueOf(long)
+     */
+    public Color() {
+        // This constructor is required for compatibility with previous APIs
+        mComponents = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
+        mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+    }
+
+    /**
+     * Creates a new color instance in the {@link ColorSpace.Named#SRGB sRGB}
+     * color space.
+     *
+     * @param r The value of the red channel, must be in [0..1] range
+     * @param g The value of the green channel, must be in [0..1] range
+     * @param b The value of the blue channel, must be in [0..1] range
+     * @param a The value of the alpha channel, must be in [0..1] range
+     */
+    private Color(float r, float g, float b, float a) {
+        this(r, g, b, a, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    /**
+     * Creates a new color instance in the specified color space. The color space
+     * must have a 3 components model.
+     *
+     * @param r The value of the red channel, must be in the color space defined range
+     * @param g The value of the green channel, must be in the color space defined range
+     * @param b The value of the blue channel, must be in the color space defined range
+     * @param a The value of the alpha channel, must be in [0..1] range
+     * @param colorSpace This color's color space, cannot be null
+     */
+    private Color(float r, float g, float b, float a, @NonNull ColorSpace colorSpace) {
+        mComponents = new float[] { r, g, b, a };
+        mColorSpace = colorSpace;
+    }
+
+    /**
+     * Creates a new color instance in the specified color space.
+     *
+     * @param components An array of color components, plus alpha
+     * @param colorSpace This color's color space, cannot be null
+     */
+    private Color(@Size(min = 4, max = 5) float[] components, @NonNull ColorSpace colorSpace) {
+        mComponents = components;
+        mColorSpace = colorSpace;
+    }
+
+    /**
+     * Returns this color's color space.
+     *
+     * @return A non-null instance of {@link ColorSpace}
+     */
+    @NonNull
+    public ColorSpace getColorSpace() {
+        return mColorSpace;
+    }
+
+    /**
+     * Returns the color model of this color.
+     *
+     * @return A non-null {@link ColorSpace.Model}
+     */
+    public ColorSpace.Model getModel() {
+        return mColorSpace.getModel();
+    }
+
+    /**
+     * Indicates whether this color color is in a wide-gamut color space.
+     * See {@link ColorSpace#isWideGamut()} for a definition of a wide-gamut
+     * color space.
+     *
+     * @return True if this color is in a wide-gamut color space, false otherwise
+     *
+     * @see #isSrgb()
+     * @see ColorSpace#isWideGamut()
+     */
+    public boolean isWideGamut() {
+        return getColorSpace().isWideGamut();
+    }
+
+    /**
+     * Indicates whether this color is in the {@link ColorSpace.Named#SRGB sRGB}
+     * color space.
+     *
+     * @return True if this color is in the sRGB color space, false otherwise
+     *
+     * @see #isWideGamut()
+     */
+    public boolean isSrgb() {
+        return getColorSpace().isSrgb();
+    }
+
+    /**
+     * Returns the number of components that form a color value according
+     * to this color space's color model, plus one extra component for
+     * alpha.
+     *
+     * @return An integer between 4 and 5
+     */
+    @IntRange(from = 4, to = 5)
+    public int getComponentCount() {
+        return mColorSpace.getComponentCount() + 1;
+    }
+
+    /**
+     * Packs this color into a color long. See the documentation of this class
+     * for a description of the color long format.
+     *
+     * @return A color long
+     *
+     * @throws IllegalArgumentException If this color's color space has the id
+     * {@link ColorSpace#MIN_ID} or if this color has more than 4 components
+     */
+    @ColorLong
+    public long pack() {
+        return pack(mComponents[0], mComponents[1], mComponents[2], mComponents[3], mColorSpace);
+    }
+
+    /**
+     * Converts this color from its color space to the specified color space.
+     * The conversion is done using the default rendering intent as specified
+     * by {@link ColorSpace#connect(ColorSpace, ColorSpace)}.
+     *
+     * @param colorSpace The destination color space, cannot be null
+     *
+     * @return A non-null color instance in the specified color space
+     */
+    @NonNull
+    public Color convert(@NonNull ColorSpace colorSpace) {
+        ColorSpace.Connector connector = ColorSpace.connect(mColorSpace, colorSpace);
+        float[] color = new float[] {
+                mComponents[0], mComponents[1], mComponents[2], mComponents[3]
+        };
+        connector.transform(color);
+        return new Color(color, colorSpace);
+    }
+
+    /**
+     * Converts this color to an ARGB color int. A color int is always in
+     * the {@link ColorSpace.Named#SRGB sRGB} color space. This implies
+     * a color space conversion is applied if needed.
+     *
+     * @return An ARGB color in the sRGB color space
+     */
+    @ColorInt
+    public int toArgb() {
+        if (mColorSpace.isSrgb()) {
+            return ((int) (mComponents[3] * 255.0f + 0.5f) << 24) |
+                   ((int) (mComponents[0] * 255.0f + 0.5f) << 16) |
+                   ((int) (mComponents[1] * 255.0f + 0.5f) <<  8) |
+                    (int) (mComponents[2] * 255.0f + 0.5f);
+        }
+
+        float[] color = new float[] {
+                mComponents[0], mComponents[1], mComponents[2], mComponents[3]
+        };
+        // The transformation saturates the output
+        ColorSpace.connect(mColorSpace).transform(color);
+
+        return ((int) (color[3] * 255.0f + 0.5f) << 24) |
+               ((int) (color[0] * 255.0f + 0.5f) << 16) |
+               ((int) (color[1] * 255.0f + 0.5f) <<  8) |
+                (int) (color[2] * 255.0f + 0.5f);
+    }
+
+    /**
+     * <p>Returns the value of the red component in the range defined by this
+     * color's color space (see {@link ColorSpace#getMinValue(int)} and
+     * {@link ColorSpace#getMaxValue(int)}).</p>
+     *
+     * <p>If this color's color model is not {@link ColorSpace.Model#RGB RGB},
+     * calling this method is equivalent to <code>getComponent(0)</code>.</p>
+     *
+     * @see #alpha()
+     * @see #red()
+     * @see #green
+     * @see #getComponents()
+     */
+    public float red() {
+        return mComponents[0];
+    }
+
+    /**
+     * <p>Returns the value of the green component in the range defined by this
+     * color's color space (see {@link ColorSpace#getMinValue(int)} and
+     * {@link ColorSpace#getMaxValue(int)}).</p>
+     *
+     * <p>If this color's color model is not {@link ColorSpace.Model#RGB RGB},
+     * calling this method is equivalent to <code>getComponent(1)</code>.</p>
+     *
+     * @see #alpha()
+     * @see #red()
+     * @see #green
+     * @see #getComponents()
+     */
+    public float green() {
+        return mComponents[1];
+    }
+
+    /**
+     * <p>Returns the value of the blue component in the range defined by this
+     * color's color space (see {@link ColorSpace#getMinValue(int)} and
+     * {@link ColorSpace#getMaxValue(int)}).</p>
+     *
+     * <p>If this color's color model is not {@link ColorSpace.Model#RGB RGB},
+     * calling this method is equivalent to <code>getComponent(2)</code>.</p>
+     *
+     * @see #alpha()
+     * @see #red()
+     * @see #green
+     * @see #getComponents()
+     */
+    public float blue() {
+        return mComponents[2];
+    }
+
+    /**
+     * Returns the value of the alpha component in the range \([0..1]\).
+     * Calling this method is equivalent to
+     * <code>getComponent(getComponentCount())</code>.
+     *
+     * @see #red()
+     * @see #green()
+     * @see #blue()
+     * @see #getComponents()
+     * @see #getComponent(int)
+     */
+    public float alpha() {
+        return mComponents[mComponents.length - 1];
+    }
+
+    /**
+     * Returns this color's components as a new array. The last element of the
+     * array is always the alpha component.
+     *
+     * @return A new, non-null array whose size is equal to {@link #getComponentCount()}
+     *
+     * @see #getComponent(int)
+     */
+    @NonNull
+    @Size(min = 4, max = 5)
+    public float[] getComponents() {
+        return Arrays.copyOf(mComponents, mColorSpace.getComponentCount() + 1);
+    }
+
+    /**
+     * <p>Returns the value of the specified component in the range defined by
+     * this color's color space (see {@link ColorSpace#getMinValue(int)} and
+     * {@link ColorSpace#getMaxValue(int)}).</p>
+     *
+     * <p>If the requested component index is {@link #getComponentCount()},
+     * this method returns the alpha component, always in the range
+     * \([0..1\).</p>
+     *
+     * @see #getComponents()
+     *
+     * @throws ArrayIndexOutOfBoundsException If the specified component index
+     * is < 0 or >= {@link #getComponentCount()}
+     */
+    public float getComponent(@IntRange(from = 0, to = 4) int component) {
+        return mComponents[component];
+    }
+
+    /**
+     * <p>Returns the relative luminance of this color.</p>
+     *
+     * <p>Based on the formula for relative luminance defined in WCAG 2.0,
+     * W3C Recommendation 11 December 2008.</p>
+     *
+     * @return A value between 0 (darkest black) and 1 (lightest white)
+     *
+     * @throws IllegalArgumentException If the this color's color space
+     * does not use the {@link ColorSpace.Model#RGB RGB} color model
+     */
+    public float luminance() {
+        if (mColorSpace.getModel() != ColorSpace.Model.RGB) {
+            throw new IllegalArgumentException("The specified color must be encoded in an RGB " +
+                    "color space. The supplied color space is " + mColorSpace.getModel());
+        }
+
+        DoubleUnaryOperator eotf = ((ColorSpace.Rgb) mColorSpace).getEotf();
+        double r = eotf.applyAsDouble(mComponents[0]);
+        double g = eotf.applyAsDouble(mComponents[1]);
+        double b = eotf.applyAsDouble(mComponents[2]);
+
+        return saturate((float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b)));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Color color = (Color) o;
+
+        //noinspection SimplifiableIfStatement
+        if (!Arrays.equals(mComponents, color.mComponents)) return false;
+        return mColorSpace.equals(color.mColorSpace);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Arrays.hashCode(mComponents);
+        result = 31 * result + mColorSpace.hashCode();
+        return result;
+    }
+
+    /**
+     * <p>Returns a string representation of the object. This method returns
+     * a string equal to the value of:</p>
+     *
+     * <pre class="prettyprint">
+     * "Color(" + r + ", " + g + ", " + b + ", " + a +
+     *         ", " + getColorSpace().getName + ')'
+     * </pre>
+     *
+     * <p>For instance, the string representation of opaque black in the sRGB
+     * color space is equal to the following value:</p>
+     *
+     * <pre>
+     * Color(0.0, 0.0, 0.0, 1.0, sRGB IEC61966-2.1)
+     * </pre>
+     *
+     * @return A non-null string representation of the object
+     */
+    @Override
+    @NonNull
+    public String toString() {
+        StringBuilder b = new StringBuilder("Color(");
+        for (float c : mComponents) {
+            b.append(c).append(", ");
+        }
+        b.append(mColorSpace.getName());
+        b.append(')');
+        return b.toString();
+    }
+
+    /**
+     * Returns the color space encoded in the specified color long.
+     *
+     * @param color The color long whose color space to extract
+     * @return A non-null color space instance. If the color long encodes
+     * an unknown or invalid color space, the {@link ColorSpace.Named#SRGB sRGB}
+     * color space is returned
+     *
+     * @see #red(long)
+     * @see #green(long)
+     * @see #blue(long)
+     * @see #alpha(long)
+     */
+    @NonNull
+    public static ColorSpace colorSpace(@ColorLong long color) {
+        return ColorSpace.get((int) (color & 0x3fL));
+    }
+
+    /**
+     * Returns the red component encoded in the specified color long.
+     * The range of the returned value depends on the color space
+     * associated with the specified color. The color space can be
+     * queried by calling {@link #colorSpace(long)}.
+     *
+     * @param color The color long whose red channel to extract
+     * @return A float value with a range defined by the specified color's
+     * color space
+     *
+     * @see #colorSpace(long)
+     * @see #green(long)
+     * @see #blue(long)
+     * @see #alpha(long)
+     */
+    public static float red(@ColorLong long color) {
+        if ((color & 0x3fL) == 0L) return ((color >> 48) & 0xff) / 255.0f;
+        return Half.toFloat((short) ((color >> 48) & 0xffff));
+    }
+
+    /**
+     * Returns the green component encoded in the specified color long.
+     * The range of the returned value depends on the color space
+     * associated with the specified color. The color space can be
+     * queried by calling {@link #colorSpace(long)}.
+     *
+     * @param color The color long whose green channel to extract
+     * @return A float value with a range defined by the specified color's
+     * color space
+     *
+     * @see #colorSpace(long)
+     * @see #red(long)
+     * @see #blue(long)
+     * @see #alpha(long)
+     */
+    public static float green(@ColorLong long color) {
+        if ((color & 0x3fL) == 0L) return ((color >> 40) & 0xff) / 255.0f;
+        return Half.toFloat((short) ((color >> 32) & 0xffff));
+    }
+
+    /**
+     * Returns the blue component encoded in the specified color long.
+     * The range of the returned value depends on the color space
+     * associated with the specified color. The color space can be
+     * queried by calling {@link #colorSpace(long)}.
+     *
+     * @param color The color long whose blue channel to extract
+     * @return A float value with a range defined by the specified color's
+     * color space
+     *
+     * @see #colorSpace(long)
+     * @see #red(long)
+     * @see #green(long)
+     * @see #alpha(long)
+     */
+    public static float blue(@ColorLong long color) {
+        if ((color & 0x3fL) == 0L) return ((color >> 32) & 0xff) / 255.0f;
+        return Half.toFloat((short) ((color >> 16) & 0xffff));
+    }
+
+    /**
+     * Returns the alpha component encoded in the specified color long.
+     * The returned value is always in the range \([0..1]\).
+     *
+     * @param color The color long whose blue channel to extract
+     * @return A float value in the range \([0..1]\)
+     *
+     * @see #colorSpace(long)
+     * @see #red(long)
+     * @see #green(long)
+     * @see #blue(long)
+     */
+    public static float alpha(@ColorLong long color) {
+        if ((color & 0x3fL) == 0L) return ((color >> 56) & 0xff) / 255.0f;
+        return ((color >> 6) & 0x3ff) / 1023.0f;
+    }
+
+    /**
+     * Indicates whether the specified color is in the
+     * {@link ColorSpace.Named#SRGB sRGB} color space.
+     *
+     * @param color The color to test
+     * @return True if the color is in the sRGB color space, false otherwise
+     *
+     * @see #isInColorSpace(long, ColorSpace)
+     * @see #isWideGamut(long)
+     */
+    public static boolean isSrgb(@ColorLong long color) {
+        return colorSpace(color).isSrgb();
+    }
+
+    /**
+     * Indicates whether the specified color is in a wide-gamut color space.
+     * See {@link ColorSpace#isWideGamut()} for a definition of a wide-gamut
+     * color space.
+     *
+     * @param color The color to test
+     * @return True if the color is in a wide-gamut color space, false otherwise
+     *
+     * @see #isInColorSpace(long, ColorSpace)
+     * @see #isSrgb(long)
+     * @see ColorSpace#isWideGamut()
+     */
+    public static boolean isWideGamut(@ColorLong long color) {
+        return colorSpace(color).isWideGamut();
+    }
+
+    /**
+     * Indicates whether the specified color is in the specified color space.
+     *
+     * @param color The color to test
+     * @param colorSpace The color space to test against
+     * @return True if the color is in the specified color space, false otherwise
+     *
+     * @see #isSrgb(long)
+     * @see #isWideGamut(long)
+     */
+    public static boolean isInColorSpace(@ColorLong long color, @NonNull ColorSpace colorSpace) {
+        return (int) (color & 0x3fL) == colorSpace.getId();
+    }
+
+    /**
+     * Converts the specified color long to an ARGB color int. A color int is
+     * always in the {@link ColorSpace.Named#SRGB sRGB} color space. This implies
+     * a color space conversion is applied if needed.
+     *
+     * @return An ARGB color in the sRGB color space
+     */
+    @ColorInt
+    public static int toArgb(@ColorLong long color) {
+        if ((color & 0x3fL) == 0L) return (int) (color >> 32);
+
+        float r = red(color);
+        float g = green(color);
+        float b = blue(color);
+        float a = alpha(color);
+
+        // The transformation saturates the output
+        float[] c = ColorSpace.connect(colorSpace(color)).transform(r, g, b);
+
+        return ((int) (a    * 255.0f + 0.5f) << 24) |
+               ((int) (c[0] * 255.0f + 0.5f) << 16) |
+               ((int) (c[1] * 255.0f + 0.5f) <<  8) |
+                (int) (c[2] * 255.0f + 0.5f);
+    }
+
+    /**
+     * Creates a new <code>Color</code> instance from an ARGB color int.
+     * The resulting color is in the {@link ColorSpace.Named#SRGB sRGB}
+     * color space.
+     *
+     * @param color The ARGB color int to create a <code>Color</code> from
+     * @return A non-null instance of {@link Color}
+     */
+    @NonNull
+    public static Color valueOf(@ColorInt int color) {
+        float r = ((color >> 16) & 0xff) / 255.0f;
+        float g = ((color >>  8) & 0xff) / 255.0f;
+        float b = ((color      ) & 0xff) / 255.0f;
+        float a = ((color >> 24) & 0xff) / 255.0f;
+        return new Color(r, g, b, a, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    /**
+     * Creates a new <code>Color</code> instance from a color long.
+     * The resulting color is in the same color space as the specified color long.
+     *
+     * @param color The color long to create a <code>Color</code> from
+     * @return A non-null instance of {@link Color}
+     */
+    @NonNull
+    public static Color valueOf(@ColorLong long color) {
+        return new Color(red(color), green(color), blue(color), alpha(color), colorSpace(color));
+    }
+
+    /**
+     * Creates a new opaque <code>Color</code> in the {@link ColorSpace.Named#SRGB sRGB}
+     * color space with the specified red, green and blue component values. The component
+     * values must be in the range \([0..1]\).
+     *
+     * @param r The red component of the opaque sRGB color to create, in \([0..1]\)
+     * @param g The green component of the opaque sRGB color to create, in \([0..1]\)
+     * @param b The blue component of the opaque sRGB color to create, in \([0..1]\)
+     * @return A non-null instance of {@link Color}
+     */
+    @NonNull
+    public static Color valueOf(float r, float g, float b) {
+        return new Color(r, g, b, 1.0f);
+    }
+
+    /**
+     * Creates a new <code>Color</code> in the {@link ColorSpace.Named#SRGB sRGB}
+     * color space with the specified red, green, blue and alpha component values.
+     * The component values must be in the range \([0..1]\).
+     *
+     * @param r The red component of the sRGB color to create, in \([0..1]\)
+     * @param g The green component of the sRGB color to create, in \([0..1]\)
+     * @param b The blue component of the sRGB color to create, in \([0..1]\)
+     * @param a The alpha component of the sRGB color to create, in \([0..1]\)
+     * @return A non-null instance of {@link Color}
+     */
+    @NonNull
+    public static Color valueOf(float r, float g, float b, float a) {
+        return new Color(saturate(r), saturate(g), saturate(b), saturate(a));
+    }
+
+    /**
+     * Creates a new <code>Color</code> in the specified color space with the
+     * specified red, green, blue and alpha component values. The range of the
+     * components is defined by {@link ColorSpace#getMinValue(int)} and
+     * {@link ColorSpace#getMaxValue(int)}. The values passed to this method
+     * must be in the proper range.
+     *
+     * @param r The red component of the color to create
+     * @param g The green component of the color to create
+     * @param b The blue component of the color to create
+     * @param a The alpha component of the color to create, in \([0..1]\)
+     * @param colorSpace The color space of the color to create
+     * @return A non-null instance of {@link Color}
+     *
+     * @throws IllegalArgumentException If the specified color space uses a
+     * color model with more than 3 components
+     */
+    @NonNull
+    public static Color valueOf(float r, float g, float b, float a, @NonNull ColorSpace colorSpace) {
+        if (colorSpace.getComponentCount() > 3) {
+            throw new IllegalArgumentException("The specified color space must use a color model " +
+                    "with at most 3 color components");
+        }
+        return new Color(r, g, b, a, colorSpace);
+    }
+
+    /**
+     * <p>Creates a new <code>Color</code> in the specified color space with the
+     * specified component values. The range of the components is defined by
+     * {@link ColorSpace#getMinValue(int)} and {@link ColorSpace#getMaxValue(int)}.
+     * The values passed to this method must be in the proper range. The alpha
+     * component is always in the range \([0..1]\).</p>
+     *
+     * <p>The length of the array of components must be at least
+     * <code>{@link ColorSpace#getComponentCount()} + 1</code>. The component at index
+     * {@link ColorSpace#getComponentCount()} is always alpha.</p>
+     *
+     * @param components The components of the color to create, with alpha as the last component
+     * @param colorSpace The color space of the color to create
+     * @return A non-null instance of {@link Color}
+     *
+     * @throws IllegalArgumentException If the array of components is smaller than
+     * required by the color space
+     */
+    @NonNull
+    public static Color valueOf(@NonNull @Size(min = 4, max = 5) float[] components,
+            @NonNull ColorSpace colorSpace) {
+        if (components.length < colorSpace.getComponentCount() + 1) {
+            throw new IllegalArgumentException("Received a component array of length " +
+                    components.length + " but the color model requires " +
+                    (colorSpace.getComponentCount() + 1) + " (including alpha)");
+        }
+        return new Color(Arrays.copyOf(components, colorSpace.getComponentCount() + 1), colorSpace);
+    }
+
+    /**
+     * Converts the specified ARGB color int to an RGBA color long in the sRGB
+     * color space. See the documentation of this class for a description of
+     * the color long format.
+     *
+     * @param color The ARGB color int to convert to an RGBA color long in sRGB
+     *
+     * @return A color long
+     */
+    @ColorLong
+    public static long pack(@ColorInt int color) {
+        return (color & 0xffffffffL) << 32;
+    }
+
+    /**
+     * Packs the sRGB color defined by the specified red, green and blue component
+     * values into an RGBA color long in the sRGB color space. The alpha component
+     * is set to 1.0. See the documentation of this class for a description of the
+     * color long format.
+     *
+     * @param red The red component of the sRGB color to create, in \([0..1]\)
+     * @param green The green component of the sRGB color to create, in \([0..1]\)
+     * @param blue The blue component of the sRGB color to create, in \([0..1]\)
+     *
+     * @return A color long
+     */
+    @ColorLong
+    public static long pack(float red, float green, float blue) {
+        return pack(red, green, blue, 1.0f, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    /**
+     * Packs the sRGB color defined by the specified red, green, blue and alpha
+     * component values into an RGBA color long in the sRGB color space. See the
+     * documentation of this class for a description of the color long format.
+     *
+     * @param red The red component of the sRGB color to create, in \([0..1]\)
+     * @param green The green component of the sRGB color to create, in \([0..1]\)
+     * @param blue The blue component of the sRGB color to create, in \([0..1]\)
+     * @param alpha The alpha component of the sRGB color to create, in \([0..1]\)
+     *
+     * @return A color long
+     */
+    @ColorLong
+    public static long pack(float red, float green, float blue, float alpha) {
+        return pack(red, green, blue, alpha, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    /**
+     * <p>Packs the 3 component color defined by the specified red, green, blue and
+     * alpha component values into a color long in the specified color space. See the
+     * documentation of this class for a description of the color long format.</p>
+     *
+     * <p>The red, green and blue components must be in the range defined by the
+     * specified color space. See {@link ColorSpace#getMinValue(int)} and
+     * {@link ColorSpace#getMaxValue(int)}.</p>
+     *
+     * @param red The red component of the color to create
+     * @param green The green component of the color to create
+     * @param blue The blue component of the color to create
+     * @param alpha The alpha component of the color to create, in \([0..1]\)
+     *
+     * @return A color long
+     *
+     * @throws IllegalArgumentException If the color space's id is {@link ColorSpace#MIN_ID}
+     * or if the color space's color model has more than 3 components
+     */
+    @ColorLong
+    public static long pack(float red, float green, float blue, float alpha,
+            @NonNull ColorSpace colorSpace) {
+        if (colorSpace.isSrgb()) {
+            int argb =
+                    ((int) (alpha * 255.0f + 0.5f) << 24) |
+                    ((int) (red   * 255.0f + 0.5f) << 16) |
+                    ((int) (green * 255.0f + 0.5f) <<  8) |
+                     (int) (blue  * 255.0f + 0.5f);
+            return (argb & 0xffffffffL) << 32;
+        }
+
+        int id = colorSpace.getId();
+        if (id == ColorSpace.MIN_ID) {
+            throw new IllegalArgumentException(
+                    "Unknown color space, please use a color space returned by ColorSpace.get()");
+        }
+        if (colorSpace.getComponentCount() > 3) {
+            throw new IllegalArgumentException(
+                    "The color space must use a color model with at most 3 components");
+        }
+
+        @HalfFloat short r = Half.valueOf(red);
+        @HalfFloat short g = Half.valueOf(green);
+        @HalfFloat short b = Half.valueOf(blue);
+
+        int a = (int) (Math.max(0.0f, Math.min(alpha, 1.0f)) * 1023.0f + 0.5f);
+
+        // Suppress sign extension
+        return  (r & 0xffffL) << 48 |
+                (g & 0xffffL) << 32 |
+                (b & 0xffffL) << 16 |
+                (a & 0x3ffL ) <<  6 |
+                id & 0x3fL;
+    }
+
+    /**
+     * Converts the specified ARGB color int from the {@link ColorSpace.Named#SRGB sRGB}
+     * color space into the specified destination color space. The resulting color is
+     * returned as a color long. See the documentation of this class for a description
+     * of the color long format.
+     *
+     * @param color The sRGB color int to convert
+     * @param colorSpace The destination color space
+     * @return A color long in the destination color space
+     */
+    @ColorLong
+    public static long convert(@ColorInt int color, @NonNull ColorSpace colorSpace) {
+        float r = ((color >> 16) & 0xff) / 255.0f;
+        float g = ((color >>  8) & 0xff) / 255.0f;
+        float b = ((color      ) & 0xff) / 255.0f;
+        float a = ((color >> 24) & 0xff) / 255.0f;
+        ColorSpace source = ColorSpace.get(ColorSpace.Named.SRGB);
+        return convert(r, g, b, a, source, colorSpace);
+    }
+
+    /**
+     * <p>Converts the specified color long from its color space into the specified
+     * destination color space. The resulting color is returned as a color long. See
+     * the documentation of this class for a description of the color long format.</p>
+     *
+     * <p>When converting several colors in a row, it is recommended to use
+     * {@link #convert(long, ColorSpace.Connector)} instead to
+     * avoid the creation of a {@link ColorSpace.Connector} on every invocation.</p>
+     *
+     * @param color The color long to convert
+     * @param colorSpace The destination color space
+     * @return A color long in the destination color space
+     */
+    @ColorLong
+    public static long convert(@ColorLong long color, @NonNull ColorSpace colorSpace) {
+        float r = red(color);
+        float g = green(color);
+        float b = blue(color);
+        float a = alpha(color);
+        ColorSpace source = colorSpace(color);
+        return convert(r, g, b, a, source, colorSpace);
+    }
+
+    /**
+     * <p>Converts the specified 3 component color from the source color space to the
+     * destination color space. The resulting color is returned as a color long. See
+     * the documentation of this class for a description of the color long format.</p>
+     *
+     * <p>When converting multiple colors in a row, it is recommended to use
+     * {@link #convert(float, float, float, float, ColorSpace.Connector)} instead to
+     * avoid the creation of a {@link ColorSpace.Connector} on every invocation.</p>
+     *
+     * <p>The red, green and blue components must be in the range defined by the
+     * specified color space. See {@link ColorSpace#getMinValue(int)} and
+     * {@link ColorSpace#getMaxValue(int)}.</p>
+     *
+     * @param r The red component of the color to convert
+     * @param g The green component of the color to convert
+     * @param b The blue component of the color to convert
+     * @param a The alpha component of the color to convert, in \([0..1]\)
+     * @param source The source color space, cannot be null
+     * @param destination The destination color space, cannot be null
+     * @return A color long in the destination color space
+     *
+     * @see #convert(float, float, float, float, ColorSpace.Connector)
+     */
+    @ColorLong
+    public static long convert(float r, float g, float b, float a,
+            @NonNull ColorSpace source, @NonNull ColorSpace destination) {
+        float[] c = ColorSpace.connect(source, destination).transform(r, g, b);
+        return pack(c[0], c[1], c[2], a, destination);
+    }
+
+    /**
+     * <p>Converts the specified color long from a color space to another using the
+     * specified color space {@link ColorSpace.Connector connector}. The resulting
+     * color is returned as a color long. See the documentation of this class for a
+     * description of the color long format.</p>
+     *
+     * <p>When converting several colors in a row, this method is preferable to
+     * {@link #convert(long, ColorSpace)} as it prevents a new connector from being
+     * created on every invocation.</p>
+     *
+     * <p class="note">The connector's source color space should match the color long's
+     * color space.</p>
+     *
+     * @param color The color long to convert
+     * @param connector A color space connector, cannot be null
+     * @return A color long in the destination color space of the connector
+     */
+    @ColorLong
+    public static long convert(@ColorLong long color, @NonNull ColorSpace.Connector connector) {
+        float r = red(color);
+        float g = green(color);
+        float b = blue(color);
+        float a = alpha(color);
+        return convert(r, g, b, a, connector);
+    }
+
+    /**
+     * <p>Converts the specified 3 component color from a color space to another using
+     * the specified color space {@link ColorSpace.Connector connector}. The resulting
+     * color is returned as a color long. See the documentation of this class for a
+     * description of the color long format.</p>
+     *
+     * <p>When converting several colors in a row, this method is preferable to
+     * {@link #convert(float, float, float, float, ColorSpace, ColorSpace)} as
+     * it prevents a new connector from being created on every invocation.</p>
+     *
+     * <p>The red, green and blue components must be in the range defined by the
+     * source color space of the connector. See {@link ColorSpace#getMinValue(int)}
+     * and {@link ColorSpace#getMaxValue(int)}.</p>
+     *
+     * @param r The red component of the color to convert
+     * @param g The green component of the color to convert
+     * @param b The blue component of the color to convert
+     * @param a The alpha component of the color to convert, in \([0..1]\)
+     * @param connector A color space connector, cannot be null
+     * @return A color long in the destination color space of the connector
+     *
+     * @see #convert(float, float, float, float, ColorSpace, ColorSpace)
+     */
+    @ColorLong
+    public static long convert(float r, float g, float b, float a,
+            @NonNull ColorSpace.Connector connector) {
+        float[] c = connector.transform(r, g, b);
+        return pack(c[0], c[1], c[2], a, connector.getDestination());
+    }
+
+    /**
+     * <p>Returns the relative luminance of a color.</p>
+     *
+     * <p>Based on the formula for relative luminance defined in WCAG 2.0,
+     * W3C Recommendation 11 December 2008.</p>
+     *
+     * @return A value between 0 (darkest black) and 1 (lightest white)
+     *
+     * @throws IllegalArgumentException If the specified color's color space
+     * does not use the {@link ColorSpace.Model#RGB RGB} color model
+     */
+    public static float luminance(@ColorLong long color) {
+        ColorSpace colorSpace = colorSpace(color);
+        if (colorSpace.getModel() != ColorSpace.Model.RGB) {
+            throw new IllegalArgumentException("The specified color must be encoded in an RGB " +
+                    "color space. The supplied color space is " + colorSpace.getModel());
+        }
+
+        DoubleUnaryOperator eotf = ((ColorSpace.Rgb) colorSpace).getEotf();
+        double r = eotf.applyAsDouble(red(color));
+        double g = eotf.applyAsDouble(green(color));
+        double b = eotf.applyAsDouble(blue(color));
+
+        return saturate((float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b)));
+    }
+
+    private static float saturate(float v) {
+        return v <= 0.0f ? 0.0f : (v >= 1.0f ? 1.0f : v);
+    }
+
     /**
      * Return the alpha component of a color int. This is the same as saying
      * color >>> 24
      */
+    @IntRange(from = 0, to = 255)
     public static int alpha(int color) {
         return color >>> 24;
     }
@@ -63,6 +1209,7 @@
      * Return the red component of a color int. This is the same as saying
      * (color >> 16) & 0xFF
      */
+    @IntRange(from = 0, to = 255)
     public static int red(int color) {
         return (color >> 16) & 0xFF;
     }
@@ -71,6 +1218,7 @@
      * Return the green component of a color int. This is the same as saying
      * (color >> 8) & 0xFF
      */
+    @IntRange(from = 0, to = 255)
     public static int green(int color) {
         return (color >> 8) & 0xFF;
     }
@@ -79,41 +1227,86 @@
      * Return the blue component of a color int. This is the same as saying
      * color & 0xFF
      */
+    @IntRange(from = 0, to = 255)
     public static int blue(int color) {
         return color & 0xFF;
     }
 
     /**
      * Return a color-int from red, green, blue components.
-     * The alpha component is implicity 255 (fully opaque).
-     * These component values should be [0..255], but there is no
+     * The alpha component is implicitly 255 (fully opaque).
+     * These component values should be \([0..255]\), but there is no
      * range check performed, so if they are out of range, the
      * returned color is undefined.
-     * @param red  Red component [0..255] of the color
-     * @param green Green component [0..255] of the color
-     * @param blue  Blue component [0..255] of the color
+     *
+     * @param red  Red component \([0..255]\) of the color
+     * @param green Green component \([0..255]\) of the color
+     * @param blue  Blue component \([0..255]\) of the color
      */
     @ColorInt
-    public static int rgb(int red, int green, int blue) {
+    public static int rgb(
+            @IntRange(from = 0, to = 255) int red,
+            @IntRange(from = 0, to = 255) int green,
+            @IntRange(from = 0, to = 255) int blue) {
         return 0xff000000 | (red << 16) | (green << 8) | blue;
     }
 
     /**
-     * Return a color-int from alpha, red, green, blue components.
-     * These component values should be [0..255], but there is no
-     * range check performed, so if they are out of range, the
+     * Return a color-int from red, green, blue float components
+     * in the range \([0..1]\). The alpha component is implicitly
+     * 1.0 (fully opaque). If the components are out of range, the
      * returned color is undefined.
-     * @param alpha Alpha component [0..255] of the color
-     * @param red   Red component [0..255] of the color
-     * @param green Green component [0..255] of the color
-     * @param blue  Blue component [0..255] of the color
+     *
+     * @param red Red component \([0..1]\) of the color
+     * @param green Green component \([0..1]\) of the color
+     * @param blue Blue component \([0..1]\) of the color
      */
     @ColorInt
-    public static int argb(int alpha, int red, int green, int blue) {
+    public static int rgb(float red, float green, float blue) {
+        return 0xff000000 |
+               ((int) (red   * 255.0f + 0.5f) << 16) |
+               ((int) (green * 255.0f + 0.5f) <<  8) |
+                (int) (blue  * 255.0f + 0.5f);
+    }
+
+    /**
+     * Return a color-int from alpha, red, green, blue components.
+     * These component values should be \([0..255]\), but there is no
+     * range check performed, so if they are out of range, the
+     * returned color is undefined.
+     * @param alpha Alpha component \([0..255]\) of the color
+     * @param red Red component \([0..255]\) of the color
+     * @param green Green component \([0..255]\) of the color
+     * @param blue Blue component \([0..255]\) of the color
+     */
+    @ColorInt
+    public static int argb(
+            @IntRange(from = 0, to = 255) int alpha,
+            @IntRange(from = 0, to = 255) int red,
+            @IntRange(from = 0, to = 255) int green,
+            @IntRange(from = 0, to = 255) int blue) {
         return (alpha << 24) | (red << 16) | (green << 8) | blue;
     }
 
     /**
+     * Return a color-int from alpha, red, green, blue float components
+     * in the range \([0..1]\). If the components are out of range, the
+     * returned color is undefined.
+     *
+     * @param alpha Alpha component \([0..1]\) of the color
+     * @param red Red component \([0..1]\) of the color
+     * @param green Green component \([0..1]\) of the color
+     * @param blue Blue component \([0..1]\) of the color
+     */
+    @ColorInt
+    public static int argb(float alpha, float red, float green, float blue) {
+        return ((int) (alpha * 255.0f + 0.5f) << 24) |
+               ((int) (red   * 255.0f + 0.5f) << 16) |
+               ((int) (green * 255.0f + 0.5f) <<  8) |
+                (int) (blue  * 255.0f + 0.5f);
+    }
+
+    /**
      * Returns the relative luminance of a color.
      * <p>
      * Assumes sRGB encoding. Based on the formula for relative luminance
@@ -124,23 +1317,31 @@
     public static float luminance(@ColorInt int color) {
         ColorSpace.Rgb cs = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
         DoubleUnaryOperator eotf = cs.getEotf();
-        double red = eotf.applyAsDouble(Color.red(color) / 255.0);
-        double green = eotf.applyAsDouble(Color.green(color) / 255.0);
-        double blue = eotf.applyAsDouble(Color.blue(color) / 255.0);
-        return (float) ((0.2126 * red) + (0.7152 * green) + (0.0722 * blue));
+
+        double r = eotf.applyAsDouble(red(color) / 255.0);
+        double g = eotf.applyAsDouble(green(color) / 255.0);
+        double b = eotf.applyAsDouble(blue(color) / 255.0);
+
+        return (float) ((0.2126 * r) + (0.7152 * g) + (0.0722 * b));
     }
 
     /**
-     * Parse the color string, and return the corresponding color-int.
+     * </p>Parse the color string, and return the corresponding color-int.
      * If the string cannot be parsed, throws an IllegalArgumentException
-     * exception. Supported formats are:
-     * #RRGGBB
-     * #AARRGGBB
-     * or one of the following names:
-     * 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta',
-     * 'yellow', 'lightgray', 'darkgray', 'grey', 'lightgrey', 'darkgrey',
-     * 'aqua', 'fuchsia', 'lime', 'maroon', 'navy', 'olive', 'purple',
-     * 'silver', 'teal'.
+     * exception. Supported formats are:</p>
+     *
+     * <ul>
+     *   <li><code>#RRGGBB</code></li>
+     *   <li><code>#AARRGGBB</code></li>
+     * </ul>
+     *
+     * <p>The following names are also accepted: <code>red</code>, <code>blue</code>,
+     * <code>green</code>, <code>black</code>, <code>white</code>, <code>gray</code>,
+     * <code>cyan</code>, <code>magenta</code>, <code>yellow</code>, <code>lightgray</code>,
+     * <code>darkgray</code>, <code>grey</code>, <code>lightgrey</code>, <code>darkgrey</code>,
+     * <code>aqua</code>, <code>fuchsia</code>, <code>lime</code>, <code>maroon</code>,
+     * <code>navy</code>, <code>olive</code>, <code>purple</code>, <code>silver</code>,
+     * and <code>teal</code>.</p>
      */
     @ColorInt
     public static int parseColor(@Size(min=1) String colorString) {
@@ -165,15 +1366,20 @@
 
     /**
      * Convert RGB components to HSV.
-     *     hsv[0] is Hue [0 .. 360)
-     *     hsv[1] is Saturation [0...1]
-     *     hsv[2] is Value [0...1]
-     * @param red  red component value [0..255]
-     * @param green  green component value [0..255]
-     * @param blue  blue component value [0..255]
+     * <ul>
+     *   <li><code>hsv[0]</code> is Hue \([0..360[\)</li>
+     *   <li><code>hsv[1]</code> is Saturation \([0...1]\)</li>
+     *   <li><code>hsv[2]</code> is Value \([0...1]\)</li>
+     * </ul>
+     * @param red  red component value \([0..255]\)
+     * @param green  green component value \([0..255]\)
+     * @param blue  blue component value \([0..255]\)
      * @param hsv  3 element array which holds the resulting HSV components.
      */
-    public static void RGBToHSV(int red, int green, int blue, @Size(3) float hsv[]) {
+    public static void RGBToHSV(
+            @IntRange(from = 0, to = 255) int red,
+            @IntRange(from = 0, to = 255) int green,
+            @IntRange(from = 0, to = 255) int blue, @Size(3) float hsv[]) {
         if (hsv.length < 3) {
             throw new RuntimeException("3 components required for hsv");
         }
@@ -181,10 +1387,12 @@
     }
 
     /**
-     * Convert the argb color to its HSV components.
-     *     hsv[0] is Hue [0 .. 360)
-     *     hsv[1] is Saturation [0...1]
-     *     hsv[2] is Value [0...1]
+     * Convert the ARGB color to its HSV components.
+     * <ul>
+     *   <li><code>hsv[0]</code> is Hue \([0..360[\)</li>
+     *   <li><code>hsv[1]</code> is Saturation \([0...1]\)</li>
+     *   <li><code>hsv[2]</code> is Value \([0...1]\)</li>
+     * </ul>
      * @param color the argb color to convert. The alpha component is ignored.
      * @param hsv  3 element array which holds the resulting HSV components.
      */
@@ -194,13 +1402,16 @@
 
     /**
      * Convert HSV components to an ARGB color. Alpha set to 0xFF.
-     *     hsv[0] is Hue [0 .. 360)
-     *     hsv[1] is Saturation [0...1]
-     *     hsv[2] is Value [0...1]
+     * <ul>
+     *   <li><code>hsv[0]</code> is Hue \([0..360[\)</li>
+     *   <li><code>hsv[1]</code> is Saturation \([0...1]\)</li>
+     *   <li><code>hsv[2]</code> is Value \([0...1]\)</li>
+     * </ul>
      * If hsv values are out of range, they are pinned.
      * @param hsv  3 element array which holds the input HSV components.
      * @return the resulting argb color
     */
+    @ColorInt
     public static int HSVToColor(@Size(3) float hsv[]) {
         return HSVToColor(0xFF, hsv);
     }
@@ -208,15 +1419,18 @@
     /**
      * Convert HSV components to an ARGB color. The alpha component is passed
      * through unchanged.
-     *     hsv[0] is Hue [0 .. 360)
-     *     hsv[1] is Saturation [0...1]
-     *     hsv[2] is Value [0...1]
+     * <ul>
+     *   <li><code>hsv[0]</code> is Hue \([0..360[\)</li>
+     *   <li><code>hsv[1]</code> is Saturation \([0...1]\)</li>
+     *   <li><code>hsv[2]</code> is Value \([0...1]\)</li>
+     * </ul>
      * If hsv values are out of range, they are pinned.
      * @param alpha the alpha component of the returned argb color.
      * @param hsv  3 element array which holds the input HSV components.
      * @return the resulting argb color
-    */
-    public static int HSVToColor(int alpha, @Size(3) float hsv[]) {
+     */
+    @ColorInt
+    public static int HSVToColor(@IntRange(from = 0, to = 255) int alpha, @Size(3) float hsv[]) {
         if (hsv.length < 3) {
             throw new RuntimeException("3 components required for hsv");
         }
@@ -236,7 +1450,7 @@
      * @hide
      */
     @ColorInt
-    public static int getHtmlColor(String color) {
+    public static int getHtmlColor(@NonNull String color) {
         Integer i = sColorNameMap.get(color.toLowerCase(Locale.ROOT));
         if (i != null) {
             return i;
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index d968516..ec00c45 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.annotation.AnyThread;
 import android.annotation.ColorInt;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -126,7 +127,8 @@
  *
  * <p>To visualize and debug color spaces, you can call {@link #createRenderer()}.
  * The {@link Renderer} created by calling this method can be used to compare
- * color spaces and locate specific colors on a CIE 1931 chromaticity diagram.</p>
+ * color spaces and locate specific colors on a CIE 1931 or CIE 1976 UCS
+ * chromaticity diagram.</p>
  *
  * <p>The following code snippet shows how to render a bitmap that compares
  * the color gamuts and white points of {@link Named#DCI_P3} and
@@ -155,6 +157,7 @@
  * @see Adaptation
  * @see Renderer
  */
+@AnyThread
 @SuppressWarnings("StaticInitializerReferencesSubClass")
 public abstract class ColorSpace {
     /**
@@ -216,7 +219,7 @@
      *
      * @see #getId()
      */
-    public static final int MAX_ID = 64; // Do not change, used to encode in longs
+    public static final int MAX_ID = 63; // Do not change, used to encode in longs
 
     private static final float[] SRGB_PRIMARIES = { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f };
     private static final float[] NTSC_1953_PRIMARIES = { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f };
@@ -341,11 +344,11 @@
          *             \end{equation}\)
          *         </td>
          *     </tr>
-         *     <tr><td>Range</td><td colspan="4">\([-0.5..7.5[\)</td></tr>
+         *     <tr><td>Range</td><td colspan="4">\([-0.799..2.399[\)</td></tr>
          * </table>
          * <p>
          *     <img src="{@docRoot}reference/android/images/graphics/colorspace_scrgb.png" />
-         *     <figcaption style="text-align: center;">Extended RGB (orange) vs sRGB (white)</figcaption>
+         *     <figcaption style="text-align: center;">Extended sRGB (orange) vs sRGB (white)</figcaption>
          * </p>
          */
         EXTENDED_SRGB,
@@ -368,11 +371,11 @@
          *         <td>Electro-optical transfer function</td>
          *         <td colspan="4">\(C_{linear} = C_{scRGB}\)</td>
          *     </tr>
-         *     <tr><td>Range</td><td colspan="4">\([-0.5..7.5[\)</td></tr>
+         *     <tr><td>Range</td><td colspan="4">\([-0.5..7.499[\)</td></tr>
          * </table>
          * <p>
          *     <img src="{@docRoot}reference/android/images/graphics/colorspace_scrgb.png" />
-         *     <figcaption style="text-align: center;">Extended RGB (orange) vs sRGB (white)</figcaption>
+         *     <figcaption style="text-align: center;">Extended sRGB (orange) vs sRGB (white)</figcaption>
          * </p>
          */
         LINEAR_EXTENDED_SRGB,
@@ -1090,7 +1093,7 @@
      * space's color model. The resulting value is passed back in the specified
      * array.</p>
      *
-     * <p class="note>The specified array's length  must be at least equal to
+     * <p class="note">The specified array's length  must be at least equal to
      * to the number of color components as returned by
      * {@link Model#getComponentCount()}, and its first 3 values must
      * be the XYZ components to convert from.</p>
@@ -1125,6 +1128,7 @@
      * @return A string representation of the object
      */
     @Override
+    @NonNull
     public String toString() {
         return mName + " (id=" + mId + ", model=" + mModel + ")";
     }
@@ -1403,7 +1407,7 @@
                 ILLUMINANT_D65,
                 x -> absRcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
                 x -> absResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
-                -0.5f, 7.5f,
+                -0.799f, 2.399f,
                 Named.EXTENDED_SRGB.ordinal()
         );
         sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
@@ -1412,7 +1416,7 @@
                 ILLUMINANT_D65,
                 DoubleUnaryOperator.identity(),
                 DoubleUnaryOperator.identity(),
-                -0.5f, 7.5f,
+                -0.5f, 7.499f,
                 Named.LINEAR_EXTENDED_SRGB.ordinal()
         );
         sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb(
@@ -1437,8 +1441,8 @@
                 "SMPTE RP 431-2-2007 DCI (P3)",
                 new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
                 new float[] { 0.314f, 0.351f },
-                x -> Math.pow(x, 1 / 2.6),
-                x -> Math.pow(x, 2.6),
+                x -> Math.pow(x < 0.0f ? 0.0f : x, 1 / 2.6),
+                x -> Math.pow(x < 0.0f ? 0.0f : x, 2.6),
                 0.0f, 1.0f,
                 Named.DCI_P3.ordinal()
         );
@@ -1473,8 +1477,8 @@
                 "Adobe RGB (1998)",
                 new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
                 ILLUMINANT_D65,
-                x -> Math.pow(x, 1 / 2.2),
-                x -> Math.pow(x, 2.2),
+                x -> Math.pow(x < 0.0f ? 0.0f : x, 1 / 2.2),
+                x -> Math.pow(x < 0.0f ? 0.0f : x, 2.2),
                 0.0f, 1.0f,
                 Named.ADOBE_RGB.ordinal()
         );
@@ -1720,6 +1724,7 @@
     /**
      * Implementation of the CIE XYZ color space. Assumes the white point is D50.
      */
+    @AnyThread
     private static final class Xyz extends ColorSpace {
         private Xyz(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) {
             super(name, Model.XYZ, id);
@@ -1765,6 +1770,7 @@
      * Implementation of the CIE L*a*b* color space. Its PCS is CIE XYZ
      * with a white point of D50.
      */
+    @AnyThread
     private static final class Lab extends ColorSpace {
         private static final float A = 216.0f / 24389.0f;
         private static final float B = 841.0f / 108.0f;
@@ -1949,6 +1955,7 @@
      * <p>To learn more about the white point adaptation process, refer to the
      * documentation of {@link Adaptation}.</p>
      */
+    @AnyThread
     public static class Rgb extends ColorSpace {
         @NonNull private final float[] mWhitePoint;
         @NonNull private final float[] mPrimaries;
@@ -2337,7 +2344,7 @@
          * to "gamma space" (gamma encoded). The terms gamma space and gamma encoded
          * are frequently used because many OETFs can be closely approximated using
          * a simple power function of the form \(x^{\frac{1}{\gamma}}\) (the
-         * approximation of the {@link Named#SRGB sRGB} EOTF uses \(\gamma=2.2\)
+         * approximation of the {@link Named#SRGB sRGB} OETF uses \(\gamma=2.2\)
          * for instance).</p>
          *
          * @return A transfer function that converts from linear space to "gamma space"
@@ -2346,7 +2353,7 @@
          */
         @NonNull
         public DoubleUnaryOperator getOetf() {
-            return mOetf;
+            return mClampedOetf;
         }
 
         /**
@@ -2369,7 +2376,7 @@
          */
         @NonNull
         public DoubleUnaryOperator getEotf() {
-            return mEotf;
+            return mClampedEotf;
         }
 
         @Override
@@ -2924,6 +2931,7 @@
      * @see ColorSpace#connect(ColorSpace, RenderIntent)
      * @see ColorSpace#connect(ColorSpace)
      */
+    @AnyThread
     public static class Connector {
         @NonNull private final ColorSpace mSource;
         @NonNull private final ColorSpace mDestination;
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 08a68f4..36fc596 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -22,8 +22,8 @@
 public class ComposeShader extends Shader {
 
     private int mPorterDuffMode;
-    private final Shader mShaderA;
-    private final Shader mShaderB;
+    final Shader mShaderA;
+    final Shader mShaderB;
 
     /** Create a new compose shader, given shaders A, B, and a combining mode.
         When the mode is applied, it will be given the result from shader A as its
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 2733c43f..2c93c32 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.content.res.AssetManager;
+import android.text.FontConfig;
 import android.util.Log;
 import dalvik.annotation.optimization.CriticalNative;
 
@@ -65,10 +66,23 @@
         mBuilderPtr = 0;
     }
 
+    public void abortCreation() {
+        if (mBuilderPtr == 0) {
+            throw new IllegalStateException("This FontFamily is already frozen or abandoned");
+        }
+        nAbort(mBuilderPtr);
+        mBuilderPtr = 0;
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
-            nUnrefFamily(mNativePtr);
+            if (mNativePtr != 0) {
+                nUnrefFamily(mNativePtr);
+            }
+            if (mBuilderPtr != 0) {
+                nAbort(mBuilderPtr);
+            }
         } finally {
             super.finalize();
         }
@@ -89,7 +103,7 @@
         }
     }
 
-    public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes,
+    public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontConfig.Axis> axes,
             int weight, boolean style) {
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("Unable to call addFontWeightStyle after freezing.");
@@ -97,11 +111,12 @@
         return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, axes, weight, style);
     }
 
-    public boolean addFontFromAsset(AssetManager mgr, String path) {
+    public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
+            boolean isAsset) {
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("Unable to call addFontFromAsset after freezing.");
         }
-        return nAddFontFromAsset(mBuilderPtr, mgr, path);
+        return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset);
     }
 
     private static native long nInitBuilder(String lang, int variant);
@@ -110,10 +125,14 @@
     private static native long nCreateFamily(long mBuilderPtr);
 
     @CriticalNative
+    private static native void nAbort(long mBuilderPtr);
+
+    @CriticalNative
     private static native void nUnrefFamily(long nativePtr);
     private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex);
     private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
-            int ttcIndex, List<FontListParser.Axis> listOfAxis,
+            int ttcIndex, List<FontConfig.Axis> listOfAxis,
             int weight, boolean isItalic);
-    private static native boolean nAddFontFromAsset(long builderPtr, AssetManager mgr, String path);
+    private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
+            String path, int cookie, boolean isAsset);
 }
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 5b53296..4ec564a 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -16,12 +16,12 @@
 
 package android.graphics;
 
+import android.text.FontConfig;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.annotation.Nullable;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
@@ -37,61 +37,8 @@
  */
 public class FontListParser {
 
-    public static class Config {
-        Config() {
-            families = new ArrayList<Family>();
-            aliases = new ArrayList<Alias>();
-        }
-        public List<Family> families;
-        public List<Alias> aliases;
-    }
-
-    public static class Axis {
-        Axis(int tag, float styleValue) {
-            this.tag = tag;
-            this.styleValue = styleValue;
-        }
-        public final int tag;
-        public final float styleValue;
-    }
-
-    public static class Font {
-        Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
-            this.fontName = fontName;
-            this.ttcIndex = ttcIndex;
-            this.axes = axes;
-            this.weight = weight;
-            this.isItalic = isItalic;
-        }
-        public String fontName;
-        public int ttcIndex;
-        public final List<Axis> axes;
-        public int weight;
-        public boolean isItalic;
-    }
-
-    public static class Alias {
-        public String name;
-        public String toName;
-        public int weight;
-    }
-
-    public static class Family {
-        public Family(String name, List<Font> fonts, String lang, String variant) {
-            this.name = name;
-            this.fonts = fonts;
-            this.lang = lang;
-            this.variant = variant;
-        }
-
-        public String name;
-        public List<Font> fonts;
-        public String lang;
-        public String variant;
-    }
-
     /* Parse fallback list (no names) */
-    public static Config parse(InputStream in) throws XmlPullParserException, IOException {
+    public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
         try {
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(in, null);
@@ -105,12 +52,9 @@
     // Note that a well-formed variation contains a four-character tag and a float as styleValue,
     // with spacers in between. The tag is enclosd either by double quotes or single quotes.
     @VisibleForTesting
-    public static ArrayList<Axis> parseFontVariationSettings(@Nullable String settings) {
-        ArrayList<Axis> axisList = new ArrayList<>();
-        if (settings == null) {
-            return axisList;
-        }
+    public static FontConfig.Axis[] parseFontVariationSettings(String settings) {
         String[] settingList = settings.split(",");
+        ArrayList<FontConfig.Axis> axisList = new ArrayList<>();
         settingLoop:
         for (String setting : settingList) {
             int pos = 0;
@@ -152,9 +96,9 @@
             }
             int tag = makeTag(tagString.charAt(0), tagString.charAt(1), tagString.charAt(2),
                     tagString.charAt(3));
-            axisList.add(new Axis(tag, styleValue));
+            axisList.add(new FontConfig.Axis(tag, styleValue));
         }
-        return axisList;
+        return axisList.toArray(new FontConfig.Axis[axisList.size()]);
     }
 
     @VisibleForTesting
@@ -166,17 +110,17 @@
         return c == ' ' || c == '\r' || c == '\t' || c == '\n';
     }
 
-    private static Config readFamilies(XmlPullParser parser)
+    private static FontConfig readFamilies(XmlPullParser parser)
             throws XmlPullParserException, IOException {
-        Config config = new Config();
+        FontConfig config = new FontConfig();
         parser.require(XmlPullParser.START_TAG, null, "familyset");
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
             if (tag.equals("family")) {
-                config.families.add(readFamily(parser));
+                config.getFamilies().add(readFamily(parser));
             } else if (tag.equals("alias")) {
-                config.aliases.add(readAlias(parser));
+                config.getAliases().add(readAlias(parser));
             } else {
                 skip(parser);
             }
@@ -184,12 +128,12 @@
         return config;
     }
 
-    private static Family readFamily(XmlPullParser parser)
+    private static FontConfig.Family readFamily(XmlPullParser parser)
             throws XmlPullParserException, IOException {
         String name = parser.getAttributeValue(null, "name");
         String lang = parser.getAttributeValue(null, "lang");
         String variant = parser.getAttributeValue(null, "variant");
-        List<Font> fonts = new ArrayList<Font>();
+        List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
@@ -199,18 +143,18 @@
                 skip(parser);
             }
         }
-        return new Family(name, fonts, lang, variant);
+        return new FontConfig.Family(name, fonts, lang, variant);
     }
 
     /** Matches leading and trailing XML whitespace. */
     private static final Pattern FILENAME_WHITESPACE_PATTERN =
             Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
 
-    private static Font readFont(XmlPullParser parser)
+    private static FontConfig.Font readFont(XmlPullParser parser)
             throws XmlPullParserException, IOException {
         String indexStr = parser.getAttributeValue(null, "index");
         int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
-        List<Axis> axes = new ArrayList<Axis>();
+        List<FontConfig.Axis> axes = new ArrayList<FontConfig.Axis>();
         String weightStr = parser.getAttributeValue(null, "weight");
         int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
         boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
@@ -229,7 +173,7 @@
         }
         String fullFilename = "/system/fonts/" +
                 FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
-        return new Font(fullFilename, index, axes, weight, isItalic);
+        return new FontConfig.Font(fullFilename, index, axes, weight, isItalic);
     }
 
     /** The 'tag' attribute value is read as four character values between U+0020 and U+007E
@@ -243,7 +187,7 @@
     private static final Pattern STYLE_VALUE_PATTERN =
             Pattern.compile("-?(([0-9]+(\\.[0-9]+)?)|(\\.[0-9]+))");
 
-    private static Axis readAxis(XmlPullParser parser)
+    private static FontConfig.Axis readAxis(XmlPullParser parser)
             throws XmlPullParserException, IOException {
         int tag = 0;
         String tagStr = parser.getAttributeValue(null, "tag");
@@ -262,22 +206,22 @@
         }
 
         skip(parser);  // axis tag is empty, ignore any contents and consume end tag
-        return new Axis(tag, styleValue);
+        return new FontConfig.Axis(tag, styleValue);
     }
 
-    private static Alias readAlias(XmlPullParser parser)
+    private static FontConfig.Alias readAlias(XmlPullParser parser)
             throws XmlPullParserException, IOException {
-        Alias alias = new Alias();
-        alias.name = parser.getAttributeValue(null, "name");
-        alias.toName = parser.getAttributeValue(null, "to");
+        String name = parser.getAttributeValue(null, "name");
+        String toName = parser.getAttributeValue(null, "to");
         String weightStr = parser.getAttributeValue(null, "weight");
+        int weight;
         if (weightStr == null) {
-            alias.weight = 400;
+            weight = 400;
         } else {
-            alias.weight = Integer.parseInt(weightStr);
+            weight = Integer.parseInt(weightStr);
         }
         skip(parser);  // alias tag is empty, ignore any contents and consume end tag
-        return alias;
+        return new FontConfig.Alias(name, toName, weight);
     }
 
     private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 3e59f34..1c85df0 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -119,7 +119,7 @@
      * Currently, only Outlines that can be represented as a rectangle, circle,
      * or round rect support clipping.
      *
-     * @see {@link android.view.View#setClipToOutline(boolean)}
+     * @see android.view.View#setClipToOutline(boolean)
      */
     public boolean canClip() {
         return mMode != MODE_CONVEX_PATH;
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 4ee0c34..7815ae1 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -71,7 +71,6 @@
 
     private LocaleList  mLocales;
     private String      mFontFeatureSettings;
-    private String      mFontVariationSettings;
 
     private static final Object sCacheLock = new Object();
 
@@ -1495,37 +1494,6 @@
     }
 
     /**
-     * Returns the font variation settings.
-     *
-     * @return the paint's currently set font variation settings. Default is null.
-     *
-     * @see #setFontVariationSettings(String)
-     */
-    public String getFontVariationSettings() {
-        return mFontVariationSettings;
-    }
-
-    /**
-     * Set font variation settings.
-     *
-     * @param settings font variation settings, e.g. "'wdth' 300, 'wght' 1.8"
-     *
-     * @see #getFontVariationSettings()
-     *
-     * @param settings the font variation settings. You can pass null or empty string as no
-     *                 variation settings.
-     */
-    public void setFontVariationSettings(String settings) {
-        settings = TextUtils.nullIfEmpty(settings);
-        if (settings == mFontVariationSettings
-                || (settings != null && settings.equals(mFontVariationSettings))) {
-            return;
-        }
-        mFontVariationSettings = settings;
-        setTypeface(Typeface.createFromTypefaceWithVariation(mTypeface, settings));
-    }
-
-    /**
      * Get the current value of hyphen edit.
      *
      * @return the current hyphen edit value
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 166ef1b..38e8061 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -16,12 +16,25 @@
 
 package android.graphics;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.content.res.AssetManager;
+import android.graphics.fonts.FontRequest;
+import android.graphics.fonts.FontResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.ResultReceiver;
+import android.provider.FontsContract;
+import android.text.FontConfig;
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.LruCache;
 import android.util.SparseArray;
-import android.graphics.FontListParser;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -29,6 +42,8 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.util.ArrayList;
@@ -63,7 +78,11 @@
 
     static Typeface[] sDefaults;
     private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache =
-            new LongSparseArray<SparseArray<Typeface>>(3);
+            new LongSparseArray<>(3);
+    @GuardedBy("sLock")
+    private static FontsContract sFontsContract;
+    @GuardedBy("sLock")
+    private static Handler mHandler;
 
     /**
      * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
@@ -73,6 +92,7 @@
     static Typeface sDefaultTypeface;
     static Map<String, Typeface> sSystemFontMap;
     static FontFamily[] sFallbackFonts;
+    private static final Object sLock = new Object();
 
     static final String FONTS_CONFIG = "fonts.xml";
 
@@ -110,6 +130,163 @@
     }
 
     /**
+     * @hide
+     * Used by Resources.
+     */
+    @NonNull
+    public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
+        if (sFallbackFonts != null) {
+            synchronized (sDynamicTypefaceCache) {
+                final String key = createAssetUid(mgr, path);
+                Typeface typeface = sDynamicTypefaceCache.get(key);
+                if (typeface != null) return typeface;
+
+                FontFamily fontFamily = new FontFamily();
+                if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */)) {
+                    FontFamily[] families = {fontFamily};
+                    typeface = createFromFamiliesWithDefault(families);
+                    sDynamicTypefaceCache.put(key, typeface);
+                    return typeface;
+                }
+            }
+        }
+        throw new RuntimeException("Font resource not found " + path);
+    }
+
+    /**
+     * Create a typeface object given a font request. The font will be asynchronously fetched,
+     * therefore the result is delivered to the given callback. See {@link FontRequest}.
+     * Only one of the methods in callback will be invoked, depending on whether the request
+     * succeeds or fails. These calls will happen on the main thread.
+     * @param request A {@link FontRequest} object that identifies the provider and query for the
+     *                request. May not be null.
+     * @param callback A callback that will be triggered when results are obtained. May not be null.
+     */
+    public static void create(@NonNull FontRequest request, @NonNull FontRequestCallback callback) {
+        synchronized (sLock) {
+            if (sFontsContract == null) {
+                sFontsContract = new FontsContract();
+                mHandler = new Handler();
+            }
+            final ResultReceiver receiver = new ResultReceiver(null) {
+                @Override
+                public void onReceiveResult(int resultCode, Bundle resultData) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            receiveResult(request, callback, resultCode, resultData);
+                        }
+                    });
+                }
+            };
+            sFontsContract.getFont(request, receiver);
+        }
+    }
+
+    private static void receiveResult(FontRequest request, FontRequestCallback callback,
+            int resultCode, Bundle resultData) {
+        if (resultCode == FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND) {
+            callback.onTypefaceRequestFailed(
+                    FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND);
+            return;
+        }
+        if (resultCode == FontsContract.RESULT_CODE_FONT_NOT_FOUND
+                || resultData == null) {
+            callback.onTypefaceRequestFailed(
+                    FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND);
+            return;
+        }
+        List<FontResult> resultList =
+                resultData.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS);
+        if (resultList == null || resultList.isEmpty()) {
+            callback.onTypefaceRequestFailed(
+                    FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND);
+            return;
+        }
+        FontFamily fontFamily = new FontFamily();
+        for (int i = 0; i < resultList.size(); ++i) {
+            FontResult result = resultList.get(i);
+            ParcelFileDescriptor fd = result.getFileDescriptor();
+            if (fd == null) {
+                callback.onTypefaceRequestFailed(
+                        FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
+                return;
+            }
+            try (FileInputStream is = new FileInputStream(fd.getFileDescriptor())) {
+                FileChannel fileChannel = is.getChannel();
+                long fontSize = fileChannel.size();
+                ByteBuffer fontBuffer = fileChannel.map(
+                        FileChannel.MapMode.READ_ONLY, 0, fontSize);
+                int style = result.getStyle();
+                int weight = (style & BOLD) != 0 ? 700 : 400;
+                // TODO: this method should be
+                // create(fd, ttcIndex, fontVariationSettings, style).
+                if (!fontFamily.addFontWeightStyle(fontBuffer, result.getTtcIndex(),
+                                null, weight, (style & ITALIC) != 0)) {
+                    Log.e(TAG, "Error creating font " + request.getQuery());
+                    callback.onTypefaceRequestFailed(
+                            FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
+                    return;
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Error reading font " + request.getQuery(), e);
+                callback.onTypefaceRequestFailed(
+                        FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
+                return;
+            } finally {
+                IoUtils.closeQuietly(fd);
+            }
+        }
+        fontFamily.freeze();
+        callback.onTypefaceRetrieved(Typeface.createFromFamiliesWithDefault(
+                new FontFamily[] {fontFamily}));
+    }
+
+    /**
+     * Interface used to receive asynchronously fetched typefaces.
+     */
+    public interface FontRequestCallback {
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
+         * provider was not found on the device.
+         */
+        int FAIL_REASON_PROVIDER_NOT_FOUND = 0;
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font
+         * returned by the provider was not loaded properly.
+         */
+        int FAIL_REASON_FONT_LOAD_ERROR = 1;
+        /**
+         * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given
+         * provider did not return any results for the given query.
+         */
+        int FAIL_REASON_FONT_NOT_FOUND = 2;
+
+        /** @hide */
+        @IntDef({FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR,
+                FAIL_REASON_FONT_NOT_FOUND})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface FontRequestFailReason {}
+
+        /**
+         * Called then a Typeface request done via {@link Typeface#create(FontRequest,
+         * FontRequestCallback)} is complete. Note that this method will not be called if
+         * {@link #onTypefaceRequestFailed(int)} is called instead.
+         * @param typeface  The Typeface object retrieved.
+         */
+        void onTypefaceRetrieved(Typeface typeface);
+
+        /**
+         * Called when a Typeface request done via {@link Typeface#create(FontRequest,
+         * FontRequestCallback)} fails.
+         * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND},
+         *               {@link #FAIL_REASON_FONT_NOT_FOUND} or
+         *               {@link #FAIL_REASON_FONT_LOAD_ERROR}.
+         */
+        void onTypefaceRequestFailed(@FontRequestFailReason int reason);
+    }
+
+    /**
      * Create a typeface object given a family name, and option style information.
      * If null is passed for the name, then the "default" font will be chosen.
      * The resulting typeface object can be queried (getStyle()) to discover what
@@ -172,15 +349,6 @@
         return typeface;
     }
 
-    /** @hide */
-    public static Typeface createFromTypefaceWithVariation(Typeface family,
-            String fontVariationSettings) {
-        final long ni = family == null ? 0 : family.native_instance;
-        ArrayList<FontListParser.Axis> axes =
-                FontListParser.parseFontVariationSettings(fontVariationSettings);
-        return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes));
-    }
-
     /**
      * Returns one of the default typeface objects, based on the specified style
      *
@@ -205,12 +373,14 @@
                 if (typeface != null) return typeface;
 
                 FontFamily fontFamily = new FontFamily();
-                if (fontFamily.addFontFromAsset(mgr, path)) {
+                if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */)) {
                     fontFamily.freeze();
                     FontFamily[] families = { fontFamily };
                     typeface = createFromFamiliesWithDefault(families);
                     sDynamicTypefaceCache.put(key, typeface);
                     return typeface;
+                } else {
+                    fontFamily.abortCreation();
                 }
             }
         }
@@ -259,6 +429,8 @@
                 fontFamily.freeze();
                 FontFamily[] families = { fontFamily };
                 return createFromFamiliesWithDefault(families);
+            } else {
+                fontFamily.abortCreation();
             }
         }
         throw new RuntimeException("Font not found " + path);
@@ -306,25 +478,25 @@
         mStyle = nativeGetStyle(ni);
     }
 
-    private static FontFamily makeFamilyFromParsed(FontListParser.Family family,
+    private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
             Map<String, ByteBuffer> bufferForPath) {
-        FontFamily fontFamily = new FontFamily(family.lang, family.variant);
-        for (FontListParser.Font font : family.fonts) {
-            ByteBuffer fontBuffer = bufferForPath.get(font.fontName);
+        FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
+        for (FontConfig.Font font : family.getFonts()) {
+            ByteBuffer fontBuffer = bufferForPath.get(font.getFontName());
             if (fontBuffer == null) {
-                try (FileInputStream file = new FileInputStream(font.fontName)) {
+                try (FileInputStream file = new FileInputStream(font.getFontName())) {
                     FileChannel fileChannel = file.getChannel();
                     long fontSize = fileChannel.size();
                     fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
-                    bufferForPath.put(font.fontName, fontBuffer);
+                    bufferForPath.put(font.getFontName(), fontBuffer);
                 } catch (IOException e) {
-                    Log.e(TAG, "Error mapping font file " + font.fontName);
+                    Log.e(TAG, "Error mapping font file " + font.getFontName());
                     continue;
                 }
             }
-            if (!fontFamily.addFontWeightStyle(fontBuffer, font.ttcIndex, font.axes,
-                    font.weight, font.isItalic)) {
-                Log.e(TAG, "Error creating font " + font.fontName + "#" + font.ttcIndex);
+            if (!fontFamily.addFontWeightStyle(fontBuffer, font.getTtcIndex(), font.getAxes(),
+                    font.getWeight(), font.isItalic())) {
+                Log.e(TAG, "Error creating font " + font.getFontName() + "#" + font.getTtcIndex());
             }
         }
         fontFamily.freeze();
@@ -342,16 +514,16 @@
         File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
         try {
             FileInputStream fontsIn = new FileInputStream(configFilename);
-            FontListParser.Config fontConfig = FontListParser.parse(fontsIn);
+            FontConfig fontConfig = FontListParser.parse(fontsIn);
 
             Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
 
             List<FontFamily> familyList = new ArrayList<FontFamily>();
             // Note that the default typeface is always present in the fallback list;
             // this is an enhancement from pre-Minikin behavior.
-            for (int i = 0; i < fontConfig.families.size(); i++) {
-                FontListParser.Family f = fontConfig.families.get(i);
-                if (i == 0 || f.name == null) {
+            for (int i = 0; i < fontConfig.getFamilies().size(); i++) {
+                FontConfig.Family f = fontConfig.getFamilies().get(i);
+                if (i == 0 || f.getName() == null) {
                     familyList.add(makeFamilyFromParsed(f, bufferForPath));
                 }
             }
@@ -359,10 +531,10 @@
             setDefault(Typeface.createFromFamilies(sFallbackFonts));
 
             Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
-            for (int i = 0; i < fontConfig.families.size(); i++) {
+            for (int i = 0; i < fontConfig.getFamilies().size(); i++) {
                 Typeface typeface;
-                FontListParser.Family f = fontConfig.families.get(i);
-                if (f.name != null) {
+                FontConfig.Family f = fontConfig.getFamilies().get(i);
+                if (f.getName() != null) {
                     if (i == 0) {
                         // The first entry is the default typeface; no sense in
                         // duplicating the corresponding FontFamily.
@@ -372,17 +544,17 @@
                         FontFamily[] families = { fontFamily };
                         typeface = Typeface.createFromFamiliesWithDefault(families);
                     }
-                    systemFonts.put(f.name, typeface);
+                    systemFonts.put(f.getName(), typeface);
                 }
             }
-            for (FontListParser.Alias alias : fontConfig.aliases) {
-                Typeface base = systemFonts.get(alias.toName);
+            for (FontConfig.Alias alias : fontConfig.getAliases()) {
+                Typeface base = systemFonts.get(alias.getToName());
                 Typeface newFace = base;
-                int weight = alias.weight;
+                int weight = alias.getWeight();
                 if (weight != 400) {
                     newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
                 }
-                systemFonts.put(alias.name, newFace);
+                systemFonts.put(alias.getName(), newFace);
             }
             sSystemFontMap = systemFonts;
 
@@ -453,8 +625,6 @@
     }
 
     private static native long nativeCreateFromTypeface(long native_instance, int style);
-    private static native long nativeCreateFromTypefaceWithVariation(
-            long native_instance, List<FontListParser.Axis> axes);
     private static native long nativeCreateWeightAlias(long native_instance, int weight);
     private static native void nativeUnref(long native_instance);
     private static native int  nativeGetStyle(long native_instance);
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 6ddc2d7..850f40e 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1320,7 +1320,7 @@
          * provide an appropriate Resources object.
          *
          * @return a new drawable object based on this constant state
-         * @see {@link #newDrawable(Resources)}
+         * @see #newDrawable(Resources)
          */
         public abstract @NonNull Drawable newDrawable();
 
diff --git a/graphics/java/android/graphics/drawable/DrawableInflater.java b/graphics/java/android/graphics/drawable/DrawableInflater.java
index 348af70d..6d0bbdf 100644
--- a/graphics/java/android/graphics/drawable/DrawableInflater.java
+++ b/graphics/java/android/graphics/drawable/DrawableInflater.java
@@ -147,6 +147,8 @@
                 return new TransitionDrawable();
             case "ripple":
                 return new RippleDrawable();
+            case "maskable-icon":
+                return new MaskableIconDrawable();
             case "color":
                 return new ColorDrawable();
             case "shape":
diff --git a/graphics/java/android/graphics/drawable/MaskableIconDrawable.java b/graphics/java/android/graphics/drawable/MaskableIconDrawable.java
new file mode 100644
index 0000000..3467b1a
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/MaskableIconDrawable.java
@@ -0,0 +1,1001 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import static android.graphics.drawable.Drawable.obtainAttributes;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.PathParser;
+
+import com.android.internal.R;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * This drawable supports two layers: foreground and background.
+ *
+ * <p>The layers are clipped when rendering using the mask path defined in the device configuration.
+ *
+ * <p>This class can also be created via XML inflation using <code>&lt;maskable-icon></code> tag
+ * in addition to dynamic creation.
+ */
+public class MaskableIconDrawable extends Drawable implements Drawable.Callback {
+
+    /**
+     * Mask path is defined inside device configuration in following dimension: [100 x 100]
+     */
+    public static final float MASK_SIZE = 100f;
+
+    /**
+     * The view port of the layers is smaller than their intrinsic width and height by this factor.
+     *
+     * It is part of the API contract that all four sides of the layers are padded so as to provide
+     * extra content to reveal within the clip path when performing affine transformations on the
+     * layers.
+     */
+    public static final float DEFAULT_VIEW_PORT_SCALE = 2f / 3f;
+
+    /**
+     * Clip path defined in {@link com.android.internal.R.string.config_icon_mask}.
+     */
+    private static Path sMask;
+
+    /**
+     * Scaled mask based on the view bounds.
+     */
+    private final Path mMask;
+    private final Matrix mMaskMatrix;
+    private final Region mTransparentRegion;
+
+    private Bitmap mMaskBitmap;
+
+    /**
+     * Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
+     * background layer.
+     */
+    private static final int BACKGROUND_ID = 0;
+    private static final int FOREGROUND_ID = 1;
+
+    /**
+     * State variable that maintains the {@link ChildDrawable} array.
+     */
+    LayerState mLayerState;
+
+    private Shader mLayersShader;
+    private Bitmap mLayersBitmap;
+
+    private final Rect mTmpOutRect = new Rect();
+    private Rect mHotspotBounds;
+    private boolean mMutated;
+
+    private boolean mSuspendChildInvalidation;
+    private boolean mChildRequestedInvalidation;
+    private final Canvas mCanvas;
+    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
+        Paint.FILTER_BITMAP_FLAG);
+
+    /**
+     * Constructor used for xml inflation.
+     */
+    MaskableIconDrawable() {
+        this((LayerState) null, null);
+    }
+
+    /**
+     * The one constructor to rule them all. This is called by all public
+     * constructors to set the state and initialize local properties.
+     */
+    MaskableIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
+        mLayerState = createConstantState(state, res);
+
+        if (sMask == null) {
+            sMask = PathParser.createPathFromPathData(
+                Resources.getSystem().getString(com.android.internal.R.string.config_icon_mask));
+        }
+        mMask = new Path();
+        mMaskMatrix = new Matrix();
+        mCanvas = new Canvas();
+        mTransparentRegion = new Region();
+    }
+
+    private ChildDrawable createChildDrawable(Drawable drawable) {
+        final ChildDrawable layer = new ChildDrawable(mLayerState.mDensity);
+        layer.mDrawable = drawable;
+        layer.mDrawable.setCallback(this);
+        mLayerState.mChildrenChangingConfigurations |=
+            layer.mDrawable.getChangingConfigurations();
+        return layer;
+    }
+
+    LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) {
+        return new LayerState(state, this, res);
+    }
+
+    /**
+     * Constructor used to dynamically create this drawable.
+     *
+     * @param backgroundDrawable drawable that should be rendered in the background
+     * @param foregroundDrawable drawable that should be rendered in the foreground
+     */
+    public MaskableIconDrawable(Drawable backgroundDrawable,
+            Drawable foregroundDrawable) {
+        this((LayerState)null, null);
+        addLayer(BACKGROUND_ID, createChildDrawable(backgroundDrawable));
+        addLayer(FOREGROUND_ID, createChildDrawable(foregroundDrawable));
+    }
+
+    /**
+     * Sets the layer to the {@param index} and invalidates cache.
+     *
+     * @param index The index of the layer.
+     * @param layer The layer to add.
+     */
+    private void addLayer(int index, @NonNull ChildDrawable layer) {
+        mLayerState.mChildren[index] = layer;
+        mLayerState.invalidateCache();
+    }
+
+    @Override
+    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        super.inflate(r, parser, attrs, theme);
+
+        final LayerState state = mLayerState;
+        if (state == null) {
+            return;
+        }
+
+        // The density may have changed since the last update. This will
+        // apply scaling to any existing constant state properties.
+        final int density = Drawable.resolveDensity(r, 0);
+        state.setDensity(density);
+
+        final ChildDrawable[] array = state.mChildren;
+        for (int i = 0; i < state.mChildren.length; i++) {
+            final ChildDrawable layer = array[i];
+            layer.setDensity(density);
+        }
+        inflateLayers(r, parser, attrs, theme);
+    }
+
+    /**
+     * @return the mask path object used to clip the drawable
+     */
+    public Path getIconMask() {
+        return mMask;
+    }
+
+    /**
+     * @return the foreground drawable managed by this drawable
+     */
+    public Drawable getForeground() {
+        return mLayerState.mChildren[FOREGROUND_ID].mDrawable;
+    }
+
+    /**
+     * @return the background drawable managed by this drawable
+     */
+    public Drawable getBackground() {
+        return mLayerState.mChildren[BACKGROUND_ID].mDrawable;
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (bounds.isEmpty()) {
+            return;
+        }
+        updateLayerBounds(bounds);
+    }
+
+    private void updateLayerBounds(Rect bounds) {
+        try {
+            suspendChildInvalidation();
+            updateLayerBoundsInternal(bounds);
+            updateMaskBoundsInternal(bounds);
+        } finally {
+            resumeChildInvalidation();
+        }
+    }
+
+    private void updateLayerBoundsInternal(Rect bounds) {
+        int cX = bounds.centerX();
+        int cY = bounds.centerY();
+
+        for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
+            int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
+            int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
+            final Rect outRect = mTmpOutRect;
+            outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
+
+            final ChildDrawable r = mLayerState.mChildren[i];
+            final Drawable d = r.mDrawable;
+            d.setBounds(outRect);
+        }
+    }
+
+    private void updateMaskBoundsInternal(Rect b) {
+        mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+        sMask.transform(mMaskMatrix, mMask);
+
+        mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
+        mCanvas.setBitmap(mMaskBitmap);
+        mPaint.setShader(null);
+        mCanvas.drawPath(mMask, mPaint);
+
+        // reset everything that depends on the view bounds
+        mTransparentRegion.setEmpty();
+        mLayersShader = null;
+        mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mLayersShader == null) {
+            mCanvas.setBitmap(mLayersBitmap);
+            for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+                final Drawable dr = mLayerState.mChildren[i].mDrawable;
+                if (dr != null) {
+                    dr.draw(mCanvas);
+                }
+            }
+            mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
+            mPaint.setShader(mLayersShader);
+        }
+        canvas.drawBitmap(mMaskBitmap, 0.0f, 0.0f, mPaint);
+    }
+
+    @Override
+    public void invalidateSelf() {
+        mLayersShader = null;
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void getOutline(@NonNull Outline outline) {
+        outline.setConvexPath(mMask);
+    }
+
+    @Override
+    public @Nullable Region getTransparentRegion() {
+        if (mTransparentRegion.isEmpty()) {
+            mMask.toggleInverseFillType();
+            mTransparentRegion.set(getBounds());
+            mTransparentRegion.setPath(mMask, mTransparentRegion);
+            mMask.toggleInverseFillType();
+        }
+        return mTransparentRegion;
+    }
+
+    @Override
+    public void applyTheme(@NonNull Theme t) {
+        super.applyTheme(t);
+
+        final LayerState state = mLayerState;
+        if (state == null) {
+            return;
+        }
+
+        final int density = Drawable.resolveDensity(t.getResources(), 0);
+        state.setDensity(density);
+
+        final ChildDrawable[] array = state.mChildren;
+        for (int i = 0; i < state.N_CHILDREN; i++) {
+            final ChildDrawable layer = array[i];
+            layer.setDensity(density);
+
+            if (layer.mThemeAttrs != null) {
+                final TypedArray a = t.resolveAttributes(
+                    layer.mThemeAttrs, R.styleable.MaskableIconDrawableLayer);
+                updateLayerFromTypedArray(layer, a);
+                a.recycle();
+            }
+
+            final Drawable d = layer.mDrawable;
+            if (d != null && d.canApplyTheme()) {
+                d.applyTheme(t);
+
+                // Update cached mask of child changing configurations.
+                state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
+            }
+        }
+    }
+
+    /**
+     * Inflates child layers using the specified parser.
+     */
+    void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
+        final LayerState state = mLayerState;
+
+        final int innerDepth = parser.getDepth() + 1;
+        int type;
+        int depth;
+        int childIndex = 0;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            if (depth > innerDepth) {
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals("background")) {
+                childIndex = BACKGROUND_ID;
+            } else if (tagName.equals("foreground")) {
+                childIndex = FOREGROUND_ID;
+            } else {
+                continue;
+            }
+
+            final ChildDrawable layer = new ChildDrawable(state.mDensity);
+            final TypedArray a = obtainAttributes(r, theme, attrs,
+                R.styleable.MaskableIconDrawableLayer);
+            updateLayerFromTypedArray(layer, a);
+            a.recycle();
+
+            // If the layer doesn't have a drawable or unresolved theme
+            // attribute for a drawable, attempt to parse one from the child
+            // element. If multiple child elements exist, we'll only use the
+            // first one.
+            if (layer.mDrawable == null && (layer.mThemeAttrs == null)) {
+                while ((type = parser.next()) == XmlPullParser.TEXT) {
+                }
+                if (type != XmlPullParser.START_TAG) {
+                    throw new XmlPullParserException(parser.getPositionDescription()
+                            + ": <foreground> or <background> tag requires a 'color' or 'drawable'"
+                            + "attribute or child tag defining a drawable");
+                }
+
+                // We found a child drawable. Take ownership.
+                layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
+                layer.mDrawable.setCallback(this);
+                state.mChildrenChangingConfigurations |=
+                        layer.mDrawable.getChangingConfigurations();
+            }
+            addLayer(childIndex, layer);
+        }
+    }
+
+    private void updateLayerFromTypedArray(@NonNull ChildDrawable layer, @NonNull TypedArray a) {
+        final LayerState state = mLayerState;
+
+        // Account for any configuration changes.
+        state.mChildrenChangingConfigurations |= a.getChangingConfigurations();
+
+        // Extract the theme attributes, if any.
+        layer.mThemeAttrs = a.extractThemeAttrs();
+
+        Drawable dr = a.getDrawable(R.styleable.MaskableIconDrawableLayer_drawable);
+        if (dr == null) {
+             int color = a.getColor(R.styleable.MaskableIconDrawableLayer_color, Color.TRANSPARENT);
+             if (color != Color.TRANSPARENT) {
+                 dr = new ColorDrawable(color);
+             }
+        }
+        if (dr != null) {
+            if (layer.mDrawable != null) {
+                // It's possible that a drawable was already set, in which case
+                // we should clear the callback. We may have also integrated the
+                // drawable's changing configurations, but we don't have enough
+                // information to revert that change.
+                layer.mDrawable.setCallback(null);
+            }
+
+            // Take ownership of the new drawable.
+            layer.mDrawable = dr;
+            layer.mDrawable.setCallback(this);
+            state.mChildrenChangingConfigurations |=
+                layer.mDrawable.getChangingConfigurations();
+        }
+    }
+
+    @Override
+    public boolean canApplyTheme() {
+        return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isProjected() {
+        if (super.isProjected()) {
+            return true;
+        }
+
+        final ChildDrawable[] layers = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            if (layers[i].mDrawable.isProjected()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Temporarily suspends child invalidation.
+     *
+     * @see #resumeChildInvalidation()
+     */
+    private void suspendChildInvalidation() {
+        mSuspendChildInvalidation = true;
+    }
+
+    /**
+     * Resumes child invalidation after suspension, immediately performing an
+     * invalidation if one was requested by a child during suspension.
+     *
+     * @see #suspendChildInvalidation()
+     */
+    private void resumeChildInvalidation() {
+        mSuspendChildInvalidation = false;
+
+        if (mChildRequestedInvalidation) {
+            mChildRequestedInvalidation = false;
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void invalidateDrawable(@NonNull Drawable who) {
+        if (mSuspendChildInvalidation) {
+            mChildRequestedInvalidation = true;
+        } else {
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+        scheduleSelf(what, when);
+    }
+
+    @Override
+    public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+        unscheduleSelf(what);
+    }
+
+    @Override
+    public @Config int getChangingConfigurations() {
+        return super.getChangingConfigurations() | mLayerState.getChangingConfigurations();
+    }
+
+    @Override
+    public void setHotspot(float x, float y) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setHotspot(x, y);
+            }
+        }
+    }
+
+    @Override
+    public void setHotspotBounds(int left, int top, int right, int bottom) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setHotspotBounds(left, top, right, bottom);
+            }
+        }
+
+        if (mHotspotBounds == null) {
+            mHotspotBounds = new Rect(left, top, right, bottom);
+        } else {
+            mHotspotBounds.set(left, top, right, bottom);
+        }
+    }
+
+    @Override
+    public void getHotspotBounds(Rect outRect) {
+        if (mHotspotBounds != null) {
+            outRect.set(mHotspotBounds);
+        } else {
+            super.getHotspotBounds(outRect);
+        }
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        final boolean changed = super.setVisible(visible, restart);
+        final ChildDrawable[] array = mLayerState.mChildren;
+
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setVisible(visible, restart);
+            }
+        }
+
+        return changed;
+    }
+
+    @Override
+    public void setDither(boolean dither) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setDither(dither);
+            }
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setAlpha(alpha);
+            }
+        }
+    }
+
+    @Override
+    public int getAlpha() {
+        final Drawable dr = getFirstNonNullDrawable();
+        if (dr != null) {
+            return dr.getAlpha();
+        } else {
+            return super.getAlpha();
+        }
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setColorFilter(colorFilter);
+            }
+        }
+    }
+
+    @Override
+    public void setTintList(ColorStateList tint) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        final int N = mLayerState.N_CHILDREN;
+        for (int i = 0; i < N; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setTintList(tint);
+            }
+        }
+    }
+
+    @Override
+    public void setTintMode(Mode tintMode) {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        final int N = mLayerState.N_CHILDREN;
+        for (int i = 0; i < N; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setTintMode(tintMode);
+            }
+        }
+    }
+
+    private Drawable getFirstNonNullDrawable() {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                return dr;
+            }
+        }
+        return null;
+    }
+
+    public void setOpacity(int opacity) {
+        mLayerState.mOpacityOverride = opacity;
+    }
+
+    @Override
+    public int getOpacity() {
+        if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
+            return mLayerState.mOpacityOverride;
+        }
+        return mLayerState.getOpacity();
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        mLayerState.mAutoMirrored = mirrored;
+
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.setAutoMirrored(mirrored);
+            }
+        }
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        return mLayerState.mAutoMirrored;
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.jumpToCurrentState();
+            }
+        }
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mLayerState.isStateful();
+    }
+
+    @Override
+    protected boolean onStateChange(int[] state) {
+        boolean changed = false;
+
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null && dr.isStateful() && dr.setState(state)) {
+                changed = true;
+            }
+        }
+
+        if (changed) {
+            updateLayerBounds(getBounds());
+        }
+
+        return changed;
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        boolean changed = false;
+
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null && dr.setLevel(level)) {
+                changed = true;
+            }
+        }
+
+        if (changed) {
+            updateLayerBounds(getBounds());
+        }
+
+        return changed;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return (int)(getMaxIntrinsicWidth() * DEFAULT_VIEW_PORT_SCALE);
+    }
+
+    private int getMaxIntrinsicWidth() {
+        int width = -1;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final ChildDrawable r = mLayerState.mChildren[i];
+            final int w = r.mDrawable.getIntrinsicWidth();
+            if (w > width) {
+                width = w;
+            }
+        }
+        return width;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return (int)(getMaxIntrinsicHeight() * DEFAULT_VIEW_PORT_SCALE);
+    }
+
+    private int getMaxIntrinsicHeight() {
+        int height = -1;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final ChildDrawable r = mLayerState.mChildren[i];
+            final int h = r.mDrawable.getIntrinsicHeight();
+            if (h > height) {
+                height = h;
+            }
+        }
+        return height;
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        if (mLayerState.canConstantState()) {
+            mLayerState.mChangingConfigurations = getChangingConfigurations();
+            return mLayerState;
+        }
+        return null;
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            mLayerState = createConstantState(mLayerState, null);
+            for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+                final Drawable dr = mLayerState.mChildren[i].mDrawable;
+                if (dr != null) {
+                    dr.mutate();
+                }
+            }
+            mMutated = true;
+        }
+        return this;
+    }
+
+    /**
+     * @hide
+     */
+    public void clearMutated() {
+        super.clearMutated();
+        final ChildDrawable[] array = mLayerState.mChildren;
+        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
+            final Drawable dr = array[i].mDrawable;
+            if (dr != null) {
+                dr.clearMutated();
+            }
+        }
+        mMutated = false;
+    }
+
+    static class ChildDrawable {
+        public Drawable mDrawable;
+        public int[] mThemeAttrs;
+        public int mDensity = DisplayMetrics.DENSITY_DEFAULT;
+
+        ChildDrawable(int density) {
+            mDensity = density;
+        }
+
+        ChildDrawable(@NonNull ChildDrawable orig, @NonNull MaskableIconDrawable owner,
+                @Nullable Resources res) {
+
+            final Drawable dr = orig.mDrawable;
+            final Drawable clone;
+            if (dr != null) {
+                final ConstantState cs = dr.getConstantState();
+                if (cs == null) {
+                    clone = dr;
+                } else if (res != null) {
+                    clone = cs.newDrawable(res);
+                } else {
+                    clone = cs.newDrawable();
+                }
+                clone.setCallback(owner);
+                clone.setBounds(dr.getBounds());
+                clone.setLevel(dr.getLevel());
+            } else {
+                clone = null;
+            }
+
+            mDrawable = clone;
+            mThemeAttrs = orig.mThemeAttrs;
+
+            mDensity = Drawable.resolveDensity(res, orig.mDensity);
+        }
+
+        public boolean canApplyTheme() {
+            return mThemeAttrs != null
+                    || (mDrawable != null && mDrawable.canApplyTheme());
+        }
+
+        public final void setDensity(int targetDensity) {
+            if (mDensity != targetDensity) {
+                mDensity = targetDensity;
+            }
+        }
+    }
+
+    static class LayerState extends ConstantState {
+        private int[] mThemeAttrs;
+
+        final static int N_CHILDREN = 2;
+        ChildDrawable[] mChildren;
+
+        int mDensity;
+        int mOpacityOverride = PixelFormat.UNKNOWN;
+
+        @Config int mChangingConfigurations;
+        @Config int mChildrenChangingConfigurations;
+
+        private boolean mCheckedOpacity;
+        private int mOpacity;
+
+        private boolean mCheckedStateful;
+        private boolean mIsStateful;
+        private boolean mAutoMirrored = false;
+
+        LayerState(@Nullable LayerState orig, @NonNull MaskableIconDrawable owner,
+                @Nullable Resources res) {
+            mDensity = Drawable.resolveDensity(res, orig != null ? orig.mDensity : 0);
+            mChildren = new ChildDrawable[N_CHILDREN];
+            if (orig != null) {
+                final ChildDrawable[] origChildDrawable = orig.mChildren;
+
+                mChangingConfigurations = orig.mChangingConfigurations;
+                mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
+
+                for (int i = 0; i < N_CHILDREN; i++) {
+                    final ChildDrawable or = origChildDrawable[i];
+                    mChildren[i] = new ChildDrawable(or, owner, res);
+                }
+
+                mCheckedOpacity = orig.mCheckedOpacity;
+                mOpacity = orig.mOpacity;
+                mCheckedStateful = orig.mCheckedStateful;
+                mIsStateful = orig.mIsStateful;
+                mAutoMirrored = orig.mAutoMirrored;
+                mThemeAttrs = orig.mThemeAttrs;
+                mOpacityOverride = orig.mOpacityOverride;
+            } else {
+                for (int i = 0; i < N_CHILDREN; i++) {
+                    mChildren[i] = new ChildDrawable(mDensity);
+                }
+            }
+        }
+
+        public final void setDensity(int targetDensity) {
+            if (mDensity != targetDensity) {
+                mDensity = targetDensity;
+            }
+        }
+
+        @Override
+        public boolean canApplyTheme() {
+            if (mThemeAttrs != null || super.canApplyTheme()) {
+                return true;
+            }
+
+            final ChildDrawable[] array = mChildren;
+            for (int i = 0; i < N_CHILDREN; i++) {
+                final ChildDrawable layer = array[i];
+                if (layer.canApplyTheme()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new MaskableIconDrawable(this, null);
+        }
+
+        @Override
+        public Drawable newDrawable(@Nullable Resources res) {
+            return new MaskableIconDrawable(this, res);
+        }
+
+        @Override
+        public @Config int getChangingConfigurations() {
+            return mChangingConfigurations
+                    | mChildrenChangingConfigurations;
+        }
+
+        public final int getOpacity() {
+            if (mCheckedOpacity) {
+                return mOpacity;
+            }
+
+            final ChildDrawable[] array = mChildren;
+
+            // Seek to the first non-null drawable.
+            int firstIndex = -1;
+            for (int i = 0; i < N_CHILDREN; i++) {
+                if (array[i].mDrawable != null) {
+                    firstIndex = i;
+                    break;
+                }
+            }
+
+            int op;
+            if (firstIndex >= 0) {
+                op = array[firstIndex].mDrawable.getOpacity();
+            } else {
+                op = PixelFormat.TRANSPARENT;
+            }
+
+            // Merge all remaining non-null drawables.
+            for (int i = firstIndex + 1; i < N_CHILDREN; i++) {
+                final Drawable dr = array[i].mDrawable;
+                if (dr != null) {
+                    op = Drawable.resolveOpacity(op, dr.getOpacity());
+                }
+            }
+
+            mOpacity = op;
+            mCheckedOpacity = true;
+            return op;
+        }
+
+        public final boolean isStateful() {
+            if (mCheckedStateful) {
+                return mIsStateful;
+            }
+
+            final ChildDrawable[] array = mChildren;
+            boolean isStateful = false;
+            for (int i = 0; i < N_CHILDREN; i++) {
+                final Drawable dr = array[i].mDrawable;
+                if (dr != null && dr.isStateful()) {
+                    isStateful = true;
+                    break;
+                }
+            }
+
+            mIsStateful = isStateful;
+            mCheckedStateful = true;
+            return isStateful;
+        }
+
+        public final boolean canConstantState() {
+            final ChildDrawable[] array = mChildren;
+            for (int i = 0; i < N_CHILDREN; i++) {
+                final Drawable dr = array[i].mDrawable;
+                if (dr != null && dr.getConstantState() == null) {
+                    return false;
+                }
+            }
+
+            // Don't cache the result, this method is not called very often.
+            return true;
+        }
+
+        public void invalidateCache() {
+            mCheckedOpacity = false;
+            mCheckedStateful = false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/graphics/java/android/graphics/fonts/FontRequest.java b/graphics/java/android/graphics/fonts/FontRequest.java
new file mode 100644
index 0000000..e50df6f
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontRequest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Information about a font request that may be sent to a Font Provider.
+ */
+public final class FontRequest implements Parcelable {
+    private final String mProviderAuthority;
+    private final String mQuery;
+
+    /**
+     * @param providerAuthority The authority of the Font Provider to be used for the request.
+     * @param query The query to be sent over to the provider. Refer to your font provider's
+     *              documentation on the format of this string.
+     */
+    public FontRequest(@NonNull String providerAuthority, @NonNull String query) {
+        mProviderAuthority = Preconditions.checkNotNull(providerAuthority);
+        mQuery = Preconditions.checkNotNull(query);
+    }
+
+    /**
+     * Returns the selected font provider's authority. This tells the system what font provider
+     * it should request the font from.
+     */
+    public String getProviderAuthority() {
+        return mProviderAuthority;
+    }
+
+    /**
+     * Returns the query string. Refer to your font provider's documentation on the format of this
+     * string.
+     */
+    public String getQuery() {
+        return mQuery;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mProviderAuthority);
+        dest.writeString(mQuery);
+    }
+
+    private FontRequest(Parcel in) {
+        mProviderAuthority = in.readString();
+        mQuery = in.readString();
+    }
+
+    public static final Parcelable.Creator<FontRequest> CREATOR =
+            new Parcelable.Creator<FontRequest>() {
+                @Override
+                public FontRequest createFromParcel(Parcel in) {
+                    return new FontRequest(in);
+                }
+
+                @Override
+                public FontRequest[] newArray(int size) {
+                    return new FontRequest[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return "FontRequest {"
+                + "mProviderAuthority: " + mProviderAuthority
+                + ", mQuery: " + mQuery
+                + "}";
+    }
+}
diff --git a/graphics/java/android/graphics/fonts/FontResult.java b/graphics/java/android/graphics/fonts/FontResult.java
new file mode 100644
index 0000000..3ef99fd
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontResult.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Paint;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Results returned from a Font Provider to the system.
+ * @hide
+ */
+public final class FontResult implements Parcelable {
+    private final ParcelFileDescriptor mFileDescriptor;
+    private final int mTtcIndex;
+    private final String mFontVariationSettings;
+    private final int mStyle;
+
+    /**
+     * Creates a FontResult with all the information needed about a provided font.
+     * @param fileDescriptor A ParcelFileDescriptor pointing to the font file. This shoult point to
+     *                       a real file or shared memory, as the client will mmap the given file
+     *                       descriptor. Pipes, sockets and other non-mmap-able file descriptors
+     *                       will fail to load in the client application.
+     * @param ttcIndex If providing a TTC_INDEX file, the index to point to. Otherwise, 0.
+     * @param fontVariationSettings If providing a variation font, the settings for it. May be null.
+     * @param style One of {@link android.graphics.Typeface#NORMAL},
+     *              {@link android.graphics.Typeface#BOLD}, {@link android.graphics.Typeface#ITALIC}
+     *              or {@link android.graphics.Typeface#BOLD_ITALIC}
+     */
+    public FontResult(@NonNull ParcelFileDescriptor fileDescriptor, int ttcIndex,
+            @Nullable String fontVariationSettings, int style) {
+        mFileDescriptor = Preconditions.checkNotNull(fileDescriptor);
+        mTtcIndex = ttcIndex;
+        mFontVariationSettings = fontVariationSettings;
+        mStyle = style;
+    }
+
+    public ParcelFileDescriptor getFileDescriptor() {
+        return mFileDescriptor;
+    }
+
+    public int getTtcIndex() {
+        return mTtcIndex;
+    }
+
+    public String getFontVariationSettings() {
+        return mFontVariationSettings;
+    }
+
+    public int getStyle() {
+        return mStyle;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mFileDescriptor, flags);
+        dest.writeInt(mTtcIndex);
+        dest.writeString(mFontVariationSettings);
+        dest.writeInt(mStyle);
+    }
+
+    private FontResult(Parcel in) {
+        mFileDescriptor = in.readParcelable(null);
+        mTtcIndex = in.readInt();
+        mFontVariationSettings = in.readString();
+        mStyle = in.readInt();
+    }
+
+    public static final Parcelable.Creator<FontResult> CREATOR =
+            new Parcelable.Creator<FontResult>() {
+                @Override
+                public FontResult createFromParcel(Parcel in) {
+                    return new FontResult(in);
+                }
+
+                @Override
+                public FontResult[] newArray(int size) {
+                    return new FontResult[size];
+                }
+            };
+}
diff --git a/graphics/java/android/graphics/fonts/FontSpec.aidl b/graphics/java/android/graphics/fonts/FontSpec.aidl
new file mode 100644
index 0000000..dddea25
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontSpec.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.graphics.fonts;
+
+parcelable FontSpec;
\ No newline at end of file
diff --git a/graphics/tests/graphicstests/Android.mk b/graphics/tests/graphicstests/Android.mk
index 1845395..8ea44bd 100644
--- a/graphics/tests/graphicstests/Android.mk
+++ b/graphics/tests/graphicstests/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 LOCAL_PACKAGE_NAME := FrameworksGraphicsTests
 
 include $(BUILD_PACKAGE)
diff --git a/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java b/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java
index 23de416..2b4e6c2 100644
--- a/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java
+++ b/graphics/tests/graphicstests/src/android/graphics/VariationParserTest.java
@@ -17,101 +17,103 @@
 package android.graphics;
 
 import android.test.suitebuilder.annotation.SmallTest;
-import java.util.List;
+import android.text.FontConfig;
 import junit.framework.TestCase;
 
+import java.util.List;
+
 
 public class VariationParserTest extends TestCase {
 
     @SmallTest
     public void testParseFontVariationSetting() {
         int tag = FontListParser.makeTag('w', 'd', 't', 'h');
-        List<FontListParser.Axis> axes = FontListParser.parseFontVariationSettings("'wdth' 1");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(1.0f, axes.get(0).styleValue);
+        FontConfig.Axis[] axis = FontListParser.parseFontVariationSettings("'wdth' 1");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(1.0f, axis[0].getStyleValue());
 
-        axes = FontListParser.parseFontVariationSettings("\"wdth\" 100");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(100.0f, axes.get(0).styleValue);
+        axis = FontListParser.parseFontVariationSettings("\"wdth\" 100");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(100.0f, axis[0].getStyleValue());
 
-        axes = FontListParser.parseFontVariationSettings("   'wdth' 100");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(100.0f, axes.get(0).styleValue);
+        axis = FontListParser.parseFontVariationSettings("   'wdth' 100");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(100.0f, axis[0].getStyleValue());
 
-        axes = FontListParser.parseFontVariationSettings("\t'wdth' 0.5");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(0.5f, axes.get(0).styleValue);
+        axis = FontListParser.parseFontVariationSettings("\t'wdth' 0.5");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(0.5f, axis[0].getStyleValue());
 
         tag = FontListParser.makeTag('A', 'X', ' ', ' ');
-        axes = FontListParser.parseFontVariationSettings("'AX  ' 1");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(1.0f, axes.get(0).styleValue);
+        axis = FontListParser.parseFontVariationSettings("'AX  ' 1");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(1.0f, axis[0].getStyleValue());
 
-        axes = FontListParser.parseFontVariationSettings("'AX  '\t1");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(1.0f, axes.get(0).styleValue);
+        axis = FontListParser.parseFontVariationSettings("'AX  '\t1");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(1.0f, axis[0].getStyleValue());
 
-        axes = FontListParser.parseFontVariationSettings("'AX  '\n1");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(1.0f, axes.get(0).styleValue);
+        axis = FontListParser.parseFontVariationSettings("'AX  '\n1");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(1.0f, axis[0].getStyleValue());
 
-        axes = FontListParser.parseFontVariationSettings("'AX  '\r1");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(1.0f, axes.get(0).styleValue);
+        axis = FontListParser.parseFontVariationSettings("'AX  '\r1");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(1.0f, axis[0].getStyleValue());
 
-        axes = FontListParser.parseFontVariationSettings("'AX  '\r\t\n 1");
-        assertEquals(tag, axes.get(0).tag);
-        assertEquals(1.0f, axes.get(0).styleValue);
+        axis = FontListParser.parseFontVariationSettings("'AX  '\r\t\n 1");
+        assertEquals(tag, axis[0].getTag());
+        assertEquals(1.0f, axis[0].getStyleValue());
 
         // Test for invalid input
-        axes = FontListParser.parseFontVariationSettings("");
-        assertEquals(0, axes.size());
-        axes = FontListParser.parseFontVariationSettings("invalid_form");
-        assertEquals(0, axes.size());
+        axis = FontListParser.parseFontVariationSettings("");
+        assertEquals(0, axis.length);
+        axis = FontListParser.parseFontVariationSettings("invalid_form");
+        assertEquals(0, axis.length);
 
         // Test with invalid tag
-        axes = FontListParser.parseFontVariationSettings("'' 1");
-        assertEquals(0, axes.size());
-        axes = FontListParser.parseFontVariationSettings("'invalid' 1");
-        assertEquals(0, axes.size());
+        axis = FontListParser.parseFontVariationSettings("'' 1");
+        assertEquals(0, axis.length);
+        axis = FontListParser.parseFontVariationSettings("'invalid' 1");
+        assertEquals(0, axis.length);
 
         // Test with invalid styleValue
-        axes = FontListParser.parseFontVariationSettings("'wdth' ");
-        assertEquals(0, axes.size());
-        axes = FontListParser.parseFontVariationSettings("'wdth' x");
-        assertEquals(0, axes.size());
-        axes = FontListParser.parseFontVariationSettings("'wdth' \t");
-        assertEquals(0, axes.size());
-        axes = FontListParser.parseFontVariationSettings("'wdth' \n\r");
-        assertEquals(0, axes.size());
+        axis = FontListParser.parseFontVariationSettings("'wdth' ");
+        assertEquals(0, axis.length);
+        axis = FontListParser.parseFontVariationSettings("'wdth' x");
+        assertEquals(0, axis.length);
+        axis = FontListParser.parseFontVariationSettings("'wdth' \t");
+        assertEquals(0, axis.length);
+        axis = FontListParser.parseFontVariationSettings("'wdth' \n\r");
+        assertEquals(0, axis.length);
     }
 
     @SmallTest
     public void testParseFontVariationStyleSettings() {
-        List<FontListParser.Axis> axes =
+        FontConfig.Axis[] axis =
                 FontListParser.parseFontVariationSettings("'wdth' 10,'AX  '\r1");
         int tag1 = FontListParser.makeTag('w', 'd', 't', 'h');
         int tag2 = FontListParser.makeTag('A', 'X', ' ', ' ');
-        assertEquals(tag1, axes.get(0).tag);
-        assertEquals(10.0f, axes.get(0).styleValue);
-        assertEquals(tag2, axes.get(1).tag);
-        assertEquals(1.0f, axes.get(1).styleValue);
+        assertEquals(tag1, axis[0].getTag());
+        assertEquals(10.0f, axis[0].getStyleValue());
+        assertEquals(tag2, axis[1].getTag());
+        assertEquals(1.0f, axis[1].getStyleValue());
 
         // Test only spacers are allowed before tag
-        axes = FontListParser.parseFontVariationSettings("     'wdth' 10,ab'wdth' 1");
+        axis = FontListParser.parseFontVariationSettings("     'wdth' 10,ab'wdth' 1");
         tag1 = FontListParser.makeTag('w', 'd', 't', 'h');
-        assertEquals(tag1, axes.get(0).tag);
-        assertEquals(10.0f, axes.get(0).styleValue);
-        assertEquals(1, axes.size());
+        assertEquals(tag1, axis[0].getTag());
+        assertEquals(10.0f, axis[0].getStyleValue());
+        assertEquals(1, axis.length);
     }
 
     @SmallTest
     public void testInvalidTagCharacters() {
-        List<FontListParser.Axis> axes =
+        FontConfig.Axis[] axis =
                 FontListParser.parseFontVariationSettings("'\u0000\u0000\u0000\u0000' 10");
-        assertEquals(0, axes.size());
-        axes = FontListParser.parseFontVariationSettings("'\u3042\u3044\u3046\u3048' 10");
-        assertEquals(0, axes.size());
+        assertEquals(0, axis.length);
+        axis = FontListParser.parseFontVariationSettings("'\u3042\u3044\u3046\u3048' 10");
+        assertEquals(0, axis.length);
     }
 
     @SmallTest
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
new file mode 100644
index 0000000..e36632a
--- /dev/null
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.Build;
+import android.os.Process;
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterCertificateChain;
+import android.security.keymaster.KeymasterDefs;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Utilities for attesting the device's hardware identifiers.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public abstract class AttestationUtils {
+    private static AtomicInteger sSequenceNumber = new AtomicInteger(0);
+
+    private AttestationUtils() {
+    }
+
+    /**
+     * Specifies that the device should attest its serial number. For use with
+     * {@link #attestDeviceIds}.
+     *
+     * @see #attestDeviceIds
+     */
+    public static final int ID_TYPE_SERIAL = 1;
+
+    /**
+     * Specifies that the device should attest its IMEIs. For use with {@link #attestDeviceIds}.
+     *
+     * @see #attestDeviceIds
+     */
+    public static final int ID_TYPE_IMEI = 2;
+
+    /**
+     * Specifies that the device should attest its MEIDs. For use with {@link #attestDeviceIds}.
+     *
+     * @see #attestDeviceIds
+     */
+    public static final int ID_TYPE_MEID = 3;
+
+    /**
+     * Performs attestation of the device's identifiers. This method returns a certificate chain
+     * whose first element contains the requested device identifiers in an extension. The device's
+     * brand, device and product are always also included in the attestation. If the device supports
+     * attestation in secure hardware, the chain will be rooted at a trustworthy CA key. Otherwise,
+     * the chain will be rooted at an untrusted certificate. See
+     * <a href="https://developer.android.com/training/articles/security-key-attestation.html">
+     * Key Attestation</a> for the format of the certificate extension.
+     * <p>
+     * Attestation will only be successful when all of the following are true:
+     * 1) The device has been set up to support device identifier attestation at the factory.
+     * 2) The user has not permanently disabled device identifier attestation.
+     * 3) You have permission to access the device identifiers you are requesting attestation for.
+     * <p>
+     * For privacy reasons, you cannot distinguish between (1) and (2). If attestation is
+     * unsuccessful, the device may not support it in general or the user may have permanently
+     * disabled it.
+     * <p>
+     * The caller must hold {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     * permission.
+     *
+     * @param context the context to use for retrieving device identifiers.
+     * @param idTypes the types of device identifiers to attest.
+     * @param attestationChallenge a blob to include in the certificate alongside the device
+     * identifiers.
+     *
+     * @return a certificate chain containing the requested device identifiers in the first element
+     *
+     * @exception SecurityException if you are not permitted to obtain an attestation of the
+     * device's identifiers.
+     * @exception DeviceIdAttestationException if the attestation operation fails.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @NonNull public static X509Certificate[] attestDeviceIds(Context context,
+            @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws
+            DeviceIdAttestationException {
+        // Check method arguments, retrieve requested device IDs and prepare attestation arguments.
+        if (idTypes == null) {
+            throw new NullPointerException("Missing id types");
+        }
+        if (attestationChallenge == null) {
+            throw new NullPointerException("Missing attestation challenge");
+        }
+        final KeymasterArguments attestArgs = new KeymasterArguments();
+        attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, attestationChallenge);
+        final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
+        for (int idType : idTypes) {
+            idTypesSet.add(idType);
+        }
+        TelephonyManager telephonyService = null;
+        if (idTypesSet.contains(ID_TYPE_IMEI) || idTypesSet.contains(ID_TYPE_MEID)) {
+            telephonyService = (TelephonyManager) context.getSystemService(
+                    Context.TELEPHONY_SERVICE);
+            if (telephonyService == null) {
+                throw new DeviceIdAttestationException("Unable to access telephony service");
+            }
+        }
+        for (final Integer idType : idTypesSet) {
+            switch (idType) {
+                case ID_TYPE_SERIAL:
+                    attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL,
+                            Build.getSerial().getBytes(StandardCharsets.UTF_8));
+                    break;
+                case ID_TYPE_IMEI: {
+                    final String imei = telephonyService.getImei(0);
+                    if (imei == null) {
+                        throw new DeviceIdAttestationException("Unable to retrieve IMEI");
+                    }
+                    attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_IMEI,
+                            imei.getBytes(StandardCharsets.UTF_8));
+                    break;
+                }
+                case ID_TYPE_MEID: {
+                    final String meid = telephonyService.getDeviceId();
+                    if (meid == null) {
+                        throw new DeviceIdAttestationException("Unable to retrieve MEID");
+                    }
+                    attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID,
+                            meid.getBytes(StandardCharsets.UTF_8));
+                    break;
+                }
+                default:
+                    throw new IllegalArgumentException("Unknown device ID type " + idType);
+            }
+        }
+        attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND,
+                Build.BRAND.getBytes(StandardCharsets.UTF_8));
+        attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE,
+                Build.DEVICE.getBytes(StandardCharsets.UTF_8));
+        attestArgs.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT,
+                Build.PRODUCT.getBytes(StandardCharsets.UTF_8));
+
+        final KeyStore keyStore = KeyStore.getInstance();
+        final String keyAlias = "android_internal_device_id_attestation-"
+                + Process.myPid() + "-" + sSequenceNumber.incrementAndGet();
+        // Clear any leftover temporary key.
+        if (!keyStore.delete(keyAlias)) {
+            throw new DeviceIdAttestationException("Unable to remove temporary key");
+        }
+        try {
+            // Generate a temporary key.
+            final KeymasterArguments generateArgs = new KeymasterArguments();
+            generateArgs.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_VERIFY);
+            generateArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+            generateArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+            generateArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
+            generateArgs.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+            generateArgs.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+            generateArgs.addUnsignedLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+                    RSAKeyGenParameterSpec.F4);
+            int errorCode = keyStore.generateKey(keyAlias, generateArgs, null, 0,
+                    new KeyCharacteristics());
+            if (errorCode != KeyStore.NO_ERROR) {
+                throw new DeviceIdAttestationException("Unable to create temporary key",
+                        KeyStore.getKeyStoreException(errorCode));
+            }
+
+            // Perform attestation.
+            final KeymasterCertificateChain outChain = new KeymasterCertificateChain();
+            errorCode = keyStore.attestKey(keyAlias, attestArgs, outChain);
+            if (errorCode != KeyStore.NO_ERROR) {
+                throw new DeviceIdAttestationException("Unable to perform attestation",
+                        KeyStore.getKeyStoreException(errorCode));
+            }
+
+            // Extract certificate chain.
+            final Collection<byte[]> rawChain = outChain.getCertificates();
+            if (rawChain.size() < 2) {
+                throw new DeviceIdAttestationException("Attestation certificate chain contained "
+                        + rawChain.size() + " entries. At least two are required.");
+            }
+            final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream();
+            try {
+                for (final byte[] cert : rawChain) {
+                    concatenatedRawChain.write(cert);
+                }
+                return CertificateFactory.getInstance("X.509").generateCertificates(
+                        new ByteArrayInputStream(concatenatedRawChain.toByteArray()))
+                                .toArray(new X509Certificate[0]);
+            } catch (Exception e) {
+                throw new DeviceIdAttestationException("Unable to construct certificate chain", e);
+            }
+        } finally {
+            // Remove temporary key.
+            keyStore.delete(keyAlias);
+        }
+    }
+}
diff --git a/keystore/java/android/security/keystore/DeviceIdAttestationException.java b/keystore/java/android/security/keystore/DeviceIdAttestationException.java
new file mode 100644
index 0000000..e18d193
--- /dev/null
+++ b/keystore/java/android/security/keystore/DeviceIdAttestationException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * Thrown when {@link AttestationUtils} is unable to attest the given device ids.
+ *
+ * @hide
+ */
+public class DeviceIdAttestationException extends Exception {
+    /**
+     * Constructs a new {@code DeviceIdAttestationException} with the current stack trace and the
+     * specified detail message.
+     *
+     * @param detailMessage the detail message for this exception.
+     */
+    public DeviceIdAttestationException(String detailMessage) {
+        super(detailMessage);
+    }
+
+    /**
+     * Constructs a new {@code DeviceIdAttestationException} with the current stack trace, the
+     * specified detail message and the specified cause.
+     *
+     * @param message the detail message for this exception.
+     * @param cause the cause of this exception, may be {@code null}.
+     */
+    public DeviceIdAttestationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index d6b1cf1..bf7a779a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -45,6 +45,7 @@
                 PURPOSE_DECRYPT,
                 PURPOSE_SIGN,
                 PURPOSE_VERIFY,
+                PURPOSE_WRAP_KEY,
                 })
     public @interface PurposeEnum {}
 
@@ -69,6 +70,12 @@
     public static final int PURPOSE_VERIFY = 1 << 3;
 
     /**
+     * Purpose of key: wrapping key for secure key import.
+     */
+    public static final int PURPOSE_WRAP_KEY = 1 << 4;
+
+
+    /**
      * @hide
      */
     public static abstract class Purpose {
@@ -84,6 +91,8 @@
                     return KeymasterDefs.KM_PURPOSE_SIGN;
                 case PURPOSE_VERIFY:
                     return KeymasterDefs.KM_PURPOSE_VERIFY;
+                case PURPOSE_WRAP_KEY:
+                    return KeymasterDefs.KM_PURPOSE_WRAP_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
@@ -99,6 +108,8 @@
                     return PURPOSE_SIGN;
                 case KeymasterDefs.KM_PURPOSE_VERIFY:
                     return PURPOSE_VERIFY;
+                case KeymasterDefs.KM_PURPOSE_WRAP_KEY:
+                    return PURPOSE_WRAP_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
index 35388d7..a740b13 100644
--- a/keystore/tests/Android.mk
+++ b/keystore/tests/Android.mk
@@ -6,6 +6,7 @@
 LOCAL_CERTIFICATE := platform
 
 LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index 5e72a0d..0a814f3 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -18,7 +18,8 @@
 
 # Build the legacy-test library
 # =============================
-# This contains the junit.framework classes that were in Android API level 25.
+# This contains the junit.framework and android.test classes that were in
+# Android API level 25.
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -28,6 +29,18 @@
 
 include $(BUILD_JAVA_LIBRARY)
 
+# Build the legacy-android-test library
+# =============================
+# This contains the android.test classes that were in Android API level 25.
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src/android)
+LOCAL_MODULE := legacy-android-test
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework junit
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
 ifeq ($(HOST_OS),linux)
 # Build the legacy-performance-test-hostdex library
 # =================================================
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index d501d25..fb89835 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -24,10 +24,14 @@
         "-Wunreachable-code",
     ],
     srcs: [
+        "ApkAssets.cpp",
         "Asset.cpp",
         "AssetDir.cpp",
         "AssetManager.cpp",
+        "AssetManager2.cpp",
         "AttributeResolution.cpp",
+        "ChunkIterator.cpp",
+        "LoadedArsc.cpp",
         "LocaleData.cpp",
         "misc.cpp",
         "ObbFile.cpp",
@@ -65,7 +69,16 @@
             shared: {
                 enabled: false,
             },
-            shared_libs: ["libz-host"],
+            static_libs: [
+                "libziparchive",
+                "libbase",
+                "liblog",
+                "libcutils",
+                "libutils",
+            ],
+            shared_libs: [
+                "libz-host",
+            ],
         },
         windows: {
             enabled: true,
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
new file mode 100644
index 0000000..55f4c3c
--- /dev/null
+++ b/libs/androidfw/ApkAssets.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/ApkAssets.h"
+
+#include "android-base/logging.h"
+#include "utils/Trace.h"
+#include "ziparchive/zip_archive.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/Util.h"
+
+namespace android {
+
+std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path) {
+  ATRACE_NAME("ApkAssets::Load");
+  ::ZipArchiveHandle unmanaged_handle;
+  int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
+  if (result != 0) {
+    LOG(ERROR) << ::ErrorCodeString(result);
+    return {};
+  }
+
+  // Wrap the handle in a unique_ptr so it gets automatically closed.
+  std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets());
+  loaded_apk->zip_handle_.reset(unmanaged_handle);
+
+  ::ZipString entry_name("resources.arsc");
+  ::ZipEntry entry;
+  result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry);
+  if (result != 0) {
+    LOG(ERROR) << ::ErrorCodeString(result);
+    return {};
+  }
+
+  if (entry.method == kCompressDeflated) {
+    LOG(WARNING) << "resources.arsc is compressed.";
+  }
+
+  loaded_apk->resources_asset_ =
+      loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
+  if (loaded_apk->resources_asset_ == nullptr) {
+    return {};
+  }
+
+  loaded_apk->path_ = path;
+  loaded_apk->loaded_arsc_ =
+      LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
+                       loaded_apk->resources_asset_->getLength());
+  if (loaded_apk->loaded_arsc_ == nullptr) {
+    return {};
+  }
+  return loaded_apk;
+}
+
+std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const {
+  ATRACE_NAME("ApkAssets::Open");
+  CHECK(zip_handle_ != nullptr);
+
+  ::ZipString name(path.c_str());
+  ::ZipEntry entry;
+  int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
+  if (result != 0) {
+    LOG(ERROR) << "No entry '" << path << "' found in APK.";
+    return {};
+  }
+
+  if (entry.method == kCompressDeflated) {
+    auto compressed_asset = util::make_unique<_CompressedAsset>();
+    if (compressed_asset->openChunk(::GetFileDescriptor(zip_handle_.get()), entry.offset,
+                                    entry.method, entry.uncompressed_length,
+                                    entry.compressed_length) != NO_ERROR) {
+      LOG(ERROR) << "Failed to decompress '" << path << "'.";
+      return {};
+    }
+    return std::move(compressed_asset);
+  } else {
+    auto uncompressed_asset = util::make_unique<_FileAsset>();
+    if (uncompressed_asset->openChunk(path.c_str(), ::GetFileDescriptor(zip_handle_.get()),
+                                      entry.offset, entry.uncompressed_length) != NO_ERROR) {
+      LOG(ERROR) << "Failed to mmap '" << path << "'.";
+      return {};
+    }
+    return std::move(uncompressed_asset);
+  }
+  return {};
+}
+
+}  // namespace android
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index e068900..acacd76 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -288,22 +288,34 @@
 {
     AutoMutex _l(mLock);
     const String8 paths[2] = { String8(targetApkPath), String8(overlayApkPath) };
-    ResTable tables[2];
+    Asset* assets[2] = {NULL, NULL};
+    bool ret = false;
+    {
+        ResTable tables[2];
 
-    for (int i = 0; i < 2; ++i) {
-        asset_path ap;
-        ap.type = kFileTypeRegular;
-        ap.path = paths[i];
-        Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap);
-        if (ass == NULL) {
-            ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
-            return false;
+        for (int i = 0; i < 2; ++i) {
+            asset_path ap;
+            ap.type = kFileTypeRegular;
+            ap.path = paths[i];
+            assets[i] = openNonAssetInPathLocked("resources.arsc",
+                    Asset::ACCESS_BUFFER, ap);
+            if (assets[i] == NULL) {
+                ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
+                goto exit;
+            }
+            if (tables[i].add(assets[i]) != NO_ERROR) {
+                ALOGW("failed to add %s to resource table", paths[i].string());
+                goto exit;
+            }
         }
-        tables[i].add(ass);
+        ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
+                targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
     }
 
-    return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
-            targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
+exit:
+    delete assets[0];
+    delete assets[1];
+    return ret;
 }
 
 bool AssetManager::addDefaultAssets()
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
new file mode 100644
index 0000000..8d65925
--- /dev/null
+++ b/libs/androidfw/AssetManager2.cpp
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/AssetManager2.h"
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "utils/ByteOrder.h"
+#include "utils/Trace.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+namespace android {
+
+AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); }
+
+bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
+                                 bool invalidate_caches) {
+  apk_assets_ = apk_assets;
+  if (invalidate_caches) {
+    InvalidateCaches(static_cast<uint32_t>(-1));
+  }
+  return true;
+}
+
+const std::vector<const ApkAssets*> AssetManager2::GetApkAssets() const { return apk_assets_; }
+
+const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const {
+  if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
+    return nullptr;
+  }
+  return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool();
+}
+
+void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
+  const int diff = configuration_.diff(configuration);
+  configuration_ = configuration;
+
+  if (diff) {
+    InvalidateCaches(static_cast<uint32_t>(diff));
+  }
+}
+
+const ResTable_config& AssetManager2::GetConfiguration() const { return configuration_; }
+
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
+  const std::string new_path = "assets/" + filename;
+  return OpenNonAsset(new_path, mode);
+}
+
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
+                                           Asset::AccessMode mode) {
+  const std::string new_path = "assets/" + filename;
+  return OpenNonAsset(new_path, cookie, mode);
+}
+
+// Search in reverse because that's how we used to do it and we need to preserve behaviour.
+// This is unfortunate, because ClassLoaders delegate to the parent first, so the order
+// is inconsistent for split APKs.
+std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
+                                                   Asset::AccessMode mode,
+                                                   ApkAssetsCookie* out_cookie) {
+  ATRACE_CALL();
+  for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
+    std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
+    if (asset) {
+      if (out_cookie != nullptr) {
+        *out_cookie = i;
+      }
+      return asset;
+    }
+  }
+
+  if (out_cookie != nullptr) {
+    *out_cookie = kInvalidCookie;
+  }
+  return {};
+}
+
+std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
+                                                   ApkAssetsCookie cookie, Asset::AccessMode mode) {
+  ATRACE_CALL();
+  if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
+    return {};
+  }
+  return apk_assets_[cookie]->Open(filename, mode);
+}
+
+ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
+                                         bool stop_at_first_match, LoadedArsc::Entry* out_entry,
+                                         ResTable_config* out_selected_config,
+                                         uint32_t* out_flags) {
+  ATRACE_CALL();
+
+  // Might use this if density_override != 0.
+  ResTable_config density_override_config;
+
+  // Select our configuration or generate a density override configuration.
+  ResTable_config* desired_config = &configuration_;
+  if (density_override != 0 && density_override != configuration_.density) {
+    density_override_config = configuration_;
+    density_override_config.density = density_override;
+    desired_config = &density_override_config;
+  }
+
+  LoadedArsc::Entry best_entry;
+  ResTable_config best_config;
+  int32_t best_index = -1;
+  uint32_t cumulated_flags = 0;
+
+  const size_t apk_asset_count = apk_assets_.size();
+  for (size_t i = 0; i < apk_asset_count; i++) {
+    const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc();
+
+    LoadedArsc::Entry current_entry;
+    ResTable_config current_config;
+    uint32_t flags = 0;
+    if (!loaded_arsc->FindEntry(resid, *desired_config, &current_entry, &current_config, &flags)) {
+      continue;
+    }
+
+    cumulated_flags |= flags;
+
+    if (best_index == -1 || current_config.isBetterThan(best_config, desired_config)) {
+      best_entry = current_entry;
+      best_config = current_config;
+      best_index = static_cast<int32_t>(i);
+      if (stop_at_first_match) {
+        break;
+      }
+    }
+  }
+
+  if (best_index == -1) {
+    return kInvalidCookie;
+  }
+
+  *out_entry = best_entry;
+  *out_selected_config = best_config;
+  *out_flags = cumulated_flags;
+  return best_index;
+}
+
+bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
+  ATRACE_CALL();
+
+  LoadedArsc::Entry entry;
+  ResTable_config config;
+  uint32_t flags = 0u;
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     true /* stop_at_first_match */, &entry, &config, &flags);
+  if (cookie == kInvalidCookie) {
+    return false;
+  }
+
+  const std::string* package_name =
+      apk_assets_[cookie]->GetLoadedArsc()->GetPackageNameForId(resid);
+  if (package_name == nullptr) {
+    return false;
+  }
+
+  out_name->package = package_name->data();
+  out_name->package_len = package_name->size();
+
+  out_name->type = entry.type_string_ref.string8(&out_name->type_len);
+  out_name->type16 = nullptr;
+  if (out_name->type == nullptr) {
+    out_name->type16 = entry.type_string_ref.string16(&out_name->type_len);
+    if (out_name->type16 == nullptr) {
+      return false;
+    }
+  }
+
+  out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len);
+  out_name->entry16 = nullptr;
+  if (out_name->entry == nullptr) {
+    out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len);
+    if (out_name->entry16 == nullptr) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
+  LoadedArsc::Entry entry;
+  ResTable_config config;
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     false /* stop_at_first_match */, &entry, &config, out_flags);
+  return cookie != kInvalidCookie;
+}
+
+ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
+                                           uint16_t density_override, Res_value* out_value,
+                                           ResTable_config* out_selected_config,
+                                           uint32_t* out_flags) {
+  ATRACE_CALL();
+
+  LoadedArsc::Entry entry;
+  ResTable_config config;
+  uint32_t flags = 0u;
+  ApkAssetsCookie cookie =
+      FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags);
+  if (cookie == kInvalidCookie) {
+    return kInvalidCookie;
+  }
+
+  if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+    if (!may_be_bag) {
+      LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
+    }
+    return kInvalidCookie;
+  }
+
+  const Res_value* device_value = reinterpret_cast<const Res_value*>(
+      reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
+  out_value->copyFrom_dtoh(*device_value);
+  *out_selected_config = config;
+  *out_flags = flags;
+  return cookie;
+}
+
+const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
+  ATRACE_CALL();
+
+  auto cached_iter = cached_bags_.find(resid);
+  if (cached_iter != cached_bags_.end()) {
+    return cached_iter->second.get();
+  }
+
+  LoadedArsc::Entry entry;
+  ResTable_config config;
+  uint32_t flags = 0u;
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     false /* stop_at_first_match */, &entry, &config, &flags);
+  if (cookie == kInvalidCookie) {
+    return nullptr;
+  }
+
+  // Check that the size of the entry header is at least as big as
+  // the desired ResTable_map_entry. Also verify that the entry
+  // was intended to be a map.
+  if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
+      (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
+    // Not a bag, nothing to do.
+    return nullptr;
+  }
+
+  const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
+  const ResTable_map* map_entry =
+      reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
+  const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
+
+  const uint32_t parent = dtohl(map->parent.ident);
+  if (parent == 0) {
+    // There is no parent, meaning there is nothing to inherit and we can do a simple
+    // copy of the entries in the map.
+    const size_t entry_count = map_entry_end - map_entry;
+    util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
+        malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
+    ResolvedBag::Entry* new_entry = new_bag->entries;
+    for (; map_entry != map_entry_end; ++map_entry) {
+      new_entry->cookie = cookie;
+      new_entry->value.copyFrom_dtoh(map_entry->value);
+      new_entry->key = dtohl(map_entry->name.ident);
+      new_entry->key_pool = nullptr;
+      new_entry->type_pool = nullptr;
+      ++new_entry;
+    }
+    new_bag->type_spec_flags = flags;
+    new_bag->entry_count = static_cast<uint32_t>(entry_count);
+    ResolvedBag* result = new_bag.get();
+    cached_bags_[resid] = std::move(new_bag);
+    return result;
+  }
+
+  // Get the parent and do a merge of the keys.
+  const ResolvedBag* parent_bag = GetBag(parent);
+  if (parent_bag == nullptr) {
+    // Failed to get the parent that should exist.
+    return nullptr;
+  }
+
+  // Combine flags from the parent and our own bag.
+  flags |= parent_bag->type_spec_flags;
+
+  // Create the max possible entries we can make. Once we construct the bag,
+  // we will realloc to fit to size.
+  const size_t max_count = parent_bag->entry_count + dtohl(map->count);
+  ResolvedBag* new_bag = reinterpret_cast<ResolvedBag*>(
+      malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))));
+  ResolvedBag::Entry* new_entry = new_bag->entries;
+
+  const ResolvedBag::Entry* parent_entry = parent_bag->entries;
+  const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
+
+  // The keys are expected to be in sorted order. Merge the two bags.
+  while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
+    const uint32_t child_key = dtohl(map_entry->name.ident);
+    if (child_key <= parent_entry->key) {
+      // Use the child key if it comes before the parent
+      // or is equal to the parent (overrides).
+      new_entry->cookie = cookie;
+      new_entry->value.copyFrom_dtoh(map_entry->value);
+      new_entry->key = child_key;
+      new_entry->key_pool = nullptr;
+      new_entry->type_pool = nullptr;
+      ++map_entry;
+    } else {
+      // Take the parent entry as-is.
+      memcpy(new_entry, parent_entry, sizeof(*new_entry));
+    }
+
+    if (child_key >= parent_entry->key) {
+      // Move to the next parent entry if we used it or it was overridden.
+      ++parent_entry;
+    }
+    // Increment to the next entry to fill.
+    ++new_entry;
+  }
+
+  // Finish the child entries if they exist.
+  while (map_entry != map_entry_end) {
+    new_entry->cookie = cookie;
+    new_entry->value.copyFrom_dtoh(map_entry->value);
+    new_entry->key = dtohl(map_entry->name.ident);
+    new_entry->key_pool = nullptr;
+    new_entry->type_pool = nullptr;
+    ++map_entry;
+    ++new_entry;
+  }
+
+  // Finish the parent entries if they exist.
+  if (parent_entry != parent_entry_end) {
+    // Take the rest of the parent entries as-is.
+    const size_t num_entries_to_copy = parent_entry_end - parent_entry;
+    memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
+    new_entry += num_entries_to_copy;
+  }
+
+  // Resize the resulting array to fit.
+  const size_t actual_count = new_entry - new_bag->entries;
+  if (actual_count != max_count) {
+    new_bag = reinterpret_cast<ResolvedBag*>(
+        realloc(new_bag, sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))));
+  }
+
+  util::unique_cptr<ResolvedBag> final_bag{new_bag};
+  final_bag->type_spec_flags = flags;
+  final_bag->entry_count = static_cast<uint32_t>(actual_count);
+  ResolvedBag* result = final_bag.get();
+  cached_bags_[resid] = std::move(final_bag);
+  return result;
+}
+
+void AssetManager2::InvalidateCaches(uint32_t diff) {
+  if (diff == 0xffffffffu) {
+    // Everything must go.
+    cached_bags_.clear();
+    return;
+  }
+
+  // Be more conservative with what gets purged. Only if the bag has other possible
+  // variations with respect to what changed (diff) should we remove it.
+  for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) {
+    if (diff & iter->second->type_spec_flags) {
+      iter = cached_bags_.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+}
+
+std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); }
+
+bool Theme::ApplyStyle(uint32_t resid, bool force) {
+  ATRACE_CALL();
+
+  const ResolvedBag* bag = asset_manager_->GetBag(resid);
+  if (bag == nullptr) {
+    return false;
+  }
+
+  // Merge the flags from this style.
+  type_spec_flags_ |= bag->type_spec_flags;
+
+  // On the first iteration, verify the attribute IDs and
+  // update the entry count in each type.
+  const auto bag_iter_end = end(bag);
+  for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
+    const uint32_t attr_resid = bag_iter->key;
+
+    // If the resource ID passed in is not a style, the key can be
+    // some other identifier that is not a resource ID.
+    if (!util::is_valid_resid(attr_resid)) {
+      return false;
+    }
+
+    const uint32_t package_idx = util::get_package_id(attr_resid);
+
+    // The type ID is 1-based, so subtract 1 to get an index.
+    const uint32_t type_idx = util::get_type_id(attr_resid) - 1;
+    const uint32_t entry_idx = util::get_entry_id(attr_resid);
+
+    std::unique_ptr<Package>& package = packages_[package_idx];
+    if (package == nullptr) {
+      package.reset(new Package());
+    }
+
+    util::unique_cptr<Type>& type = package->types[type_idx];
+    if (type == nullptr) {
+      // Set the initial capacity to take up a total amount of 1024 bytes.
+      constexpr uint32_t kInitialCapacity = (1024u - sizeof(Type)) / sizeof(Entry);
+      const uint32_t initial_capacity = std::max(entry_idx, kInitialCapacity);
+      type.reset(
+          reinterpret_cast<Type*>(calloc(sizeof(Type) + (initial_capacity * sizeof(Entry)), 1)));
+      type->entry_capacity = initial_capacity;
+    }
+
+    // Set the entry_count to include this entry. We will populate
+    // and resize the array as necessary in the next pass.
+    if (entry_idx + 1 > type->entry_count) {
+      // Increase the entry count to include this.
+      type->entry_count = entry_idx + 1;
+    }
+  }
+
+  // On the second pass, we will realloc to fit the entry counts
+  // and populate the structures.
+  for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
+    const uint32_t attr_resid = bag_iter->key;
+    const uint32_t package_idx = util::get_package_id(attr_resid);
+    const uint32_t type_idx = util::get_type_id(attr_resid) - 1;
+    const uint32_t entry_idx = util::get_entry_id(attr_resid);
+    Package* package = packages_[package_idx].get();
+    util::unique_cptr<Type>& type = package->types[type_idx];
+    if (type->entry_count != type->entry_capacity) {
+      // Resize to fit the actual entries that will be included.
+      Type* type_ptr = type.release();
+      type.reset(reinterpret_cast<Type*>(
+          realloc(type_ptr, sizeof(Type) + (type_ptr->entry_count * sizeof(Entry)))));
+      if (type->entry_capacity < type->entry_count) {
+        // Clear the newly allocated memory (which does not get zero initialized).
+        // We need to do this because we |= type_spec_flags.
+        memset(type->entries + type->entry_capacity, 0,
+               sizeof(Entry) * (type->entry_count - type->entry_capacity));
+      }
+      type->entry_capacity = type->entry_count;
+    }
+    Entry& entry = type->entries[entry_idx];
+    if (force || entry.value.dataType == Res_value::TYPE_NULL) {
+      entry.cookie = bag_iter->cookie;
+      entry.type_spec_flags |= bag->type_spec_flags;
+      entry.value = bag_iter->value;
+    }
+  }
+  return true;
+}
+
+ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
+                                    uint32_t* out_flags) const {
+  constexpr const int kMaxIterations = 20;
+
+  uint32_t type_spec_flags = 0u;
+
+  for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) {
+    if (!util::is_valid_resid(resid)) {
+      return kInvalidCookie;
+    }
+
+    const uint32_t package_idx = util::get_package_id(resid);
+
+    // Type ID is 1-based, subtract 1 to get the index.
+    const uint32_t type_idx = util::get_type_id(resid) - 1;
+    const uint32_t entry_idx = util::get_entry_id(resid);
+
+    const Package* package = packages_[package_idx].get();
+    if (package == nullptr) {
+      return kInvalidCookie;
+    }
+
+    const Type* type = package->types[type_idx].get();
+    if (type == nullptr) {
+      return kInvalidCookie;
+    }
+
+    if (entry_idx >= type->entry_count) {
+      return kInvalidCookie;
+    }
+
+    const Entry& entry = type->entries[entry_idx];
+    type_spec_flags |= entry.type_spec_flags;
+
+    switch (entry.value.dataType) {
+      case Res_value::TYPE_ATTRIBUTE:
+        resid = entry.value.data;
+        break;
+
+      case Res_value::TYPE_NULL:
+        return kInvalidCookie;
+
+      default:
+        *out_value = entry.value;
+        if (out_flags != nullptr) {
+          *out_flags = type_spec_flags;
+        }
+        return entry.cookie;
+    }
+  }
+
+  LOG(WARNING) << base::StringPrintf("Too many (%d) attribute references, stopped at: 0x%08x",
+                                     kMaxIterations, resid);
+  return kInvalidCookie;
+}
+
+void Theme::Clear() {
+  type_spec_flags_ = 0u;
+  for (std::unique_ptr<Package>& package : packages_) {
+    package.reset();
+  }
+}
+
+bool Theme::SetTo(const Theme& o) {
+  if (this == &o) {
+    return true;
+  }
+
+  if (asset_manager_ != o.asset_manager_) {
+    return false;
+  }
+
+  type_spec_flags_ = o.type_spec_flags_;
+
+  for (size_t p = 0; p < arraysize(packages_); p++) {
+    const Package* package = o.packages_[p].get();
+    if (package == nullptr) {
+      packages_[p].reset();
+      continue;
+    }
+
+    for (size_t t = 0; t < arraysize(package->types); t++) {
+      const Type* type = package->types[t].get();
+      if (type == nullptr) {
+        packages_[p]->types[t].reset();
+        continue;
+      }
+
+      const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry));
+      void* copied_data = malloc(type_alloc_size);
+      memcpy(copied_data, type, type_alloc_size);
+      packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data));
+    }
+  }
+  return true;
+}
+
+}  // namespace android
diff --git a/libs/androidfw/Chunk.h b/libs/androidfw/Chunk.h
new file mode 100644
index 0000000..e87b940
--- /dev/null
+++ b/libs/androidfw/Chunk.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHUNK_H_
+#define CHUNK_H_
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "utils/ByteOrder.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+// Helpful wrapper around a ResChunk_header that provides getter methods
+// that handle endianness conversions and provide access to the data portion
+// of the chunk.
+class Chunk {
+ public:
+  explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {}
+
+  // Returns the type of the chunk. Caller need not worry about endianness.
+  inline int type() const { return dtohs(device_chunk_->type); }
+
+  // Returns the size of the entire chunk. This can be useful for skipping
+  // over the entire chunk. Caller need not worry about endianness.
+  inline size_t size() const { return dtohl(device_chunk_->size); }
+
+  // Returns the size of the header. Caller need not worry about endianness.
+  inline size_t header_size() const { return dtohs(device_chunk_->headerSize); }
+
+  template <typename T>
+  inline const T* header() const {
+    if (header_size() >= sizeof(T)) {
+      return reinterpret_cast<const T*>(device_chunk_);
+    }
+    return nullptr;
+  }
+
+  inline const void* data_ptr() const {
+    return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size();
+  }
+
+  inline size_t data_size() const { return size() - header_size(); }
+
+ private:
+  const ResChunk_header* device_chunk_;
+};
+
+// Provides a Java style iterator over an array of ResChunk_header's.
+// Validation is performed while iterating.
+// The caller should check if there was an error during chunk validation
+// by calling HadError() and GetLastError() to get the reason for failure.
+// Example:
+//
+//   ChunkIterator iter(data_ptr, data_len);
+//   while (iter.HasNext()) {
+//     const Chunk chunk = iter.Next();
+//     ...
+//   }
+//
+//   if (iter.HadError()) {
+//     LOG(ERROR) << iter.GetLastError();
+//   }
+//
+class ChunkIterator {
+ public:
+  ChunkIterator(const void* data, size_t len)
+      : next_chunk_(reinterpret_cast<const ResChunk_header*>(data)),
+        len_(len),
+        last_error_(nullptr) {
+    CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
+    VerifyNextChunk();
+  }
+
+  Chunk Next();
+  inline bool HasNext() const { return !HadError() && len_ != 0; };
+  inline bool HadError() const { return last_error_ != nullptr; }
+  inline std::string GetLastError() const { return last_error_; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChunkIterator);
+
+  // Returns false if there was an error.
+  bool VerifyNextChunk();
+
+  const ResChunk_header* next_chunk_;
+  size_t len_;
+  const char* last_error_;
+};
+
+}  // namespace android
+
+#endif /* CHUNK_H_ */
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
new file mode 100644
index 0000000..747aa4a
--- /dev/null
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Chunk.h"
+
+#include "android-base/logging.h"
+
+namespace android {
+
+Chunk ChunkIterator::Next() {
+  CHECK(len_ != 0) << "called Next() after last chunk";
+
+  const ResChunk_header* this_chunk = next_chunk_;
+
+  // We've already checked the values of this_chunk, so safely increment.
+  next_chunk_ = reinterpret_cast<const ResChunk_header*>(
+      reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size));
+  len_ -= dtohl(this_chunk->size);
+
+  if (len_ != 0) {
+    // Prepare the next chunk.
+    VerifyNextChunk();
+  }
+  return Chunk(this_chunk);
+}
+
+// Returns false if there was an error.
+bool ChunkIterator::VerifyNextChunk() {
+  const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
+
+  // This data must be 4-byte aligned, since we directly
+  // access 32-bit words, which must be aligned on
+  // certain architectures.
+  if (header_start & 0x03) {
+    last_error_ = "header not aligned on 4-byte boundary";
+    return false;
+  }
+
+  if (len_ < sizeof(ResChunk_header)) {
+    last_error_ = "not enough space for header";
+    return false;
+  }
+
+  const size_t header_size = dtohs(next_chunk_->headerSize);
+  const size_t size = dtohl(next_chunk_->size);
+  if (header_size < sizeof(ResChunk_header)) {
+    last_error_ = "header size too small";
+    return false;
+  }
+
+  if (header_size > size) {
+    last_error_ = "header size is larger than entire chunk";
+    return false;
+  }
+
+  if (size > len_) {
+    last_error_ = "chunk size is bigger than given data";
+    return false;
+  }
+
+  if ((size | header_size) & 0x03) {
+    last_error_ = "header sizes are not aligned on 4-byte boundary";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace android
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
new file mode 100644
index 0000000..94d0d46
--- /dev/null
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/LoadedArsc.h"
+
+#include <cstddef>
+#include <limits>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "utils/ByteOrder.h"
+#include "utils/Trace.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include "Chunk.h"
+#include "androidfw/ByteBucketArray.h"
+#include "androidfw/Util.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+
+namespace {
+
+// Element of a TypeSpec array. See TypeSpec.
+struct Type {
+  // The configuration for which this type defines entries.
+  // This is already converted to host endianness.
+  ResTable_config configuration;
+
+  // Pointer to the mmapped data where entry definitions are kept.
+  const ResTable_type* type;
+};
+
+// TypeSpec is going to be immediately proceeded by
+// an array of Type structs, all in the same block of memory.
+struct TypeSpec {
+  // Pointer to the mmapped data where flags are kept.
+  // Flags denote whether the resource entry is public
+  // and under which configurations it varies.
+  const ResTable_typeSpec* type_spec;
+
+  // The number of types that follow this struct.
+  // There is a type for each configuration
+  // that entries are defined for.
+  size_t type_count;
+
+  // Trick to easily access a variable number of Type structs
+  // proceeding this struct, and to ensure their alignment.
+  const Type types[0];
+};
+
+// TypeSpecPtr points to the block of memory that holds
+// a TypeSpec struct, followed by an array of Type structs.
+// TypeSpecPtr is a managed pointer that knows how to delete
+// itself.
+using TypeSpecPtr = util::unique_cptr<TypeSpec>;
+
+// Builder that helps accumulate Type structs and then create a single
+// contiguous block of memory to store both the TypeSpec struct and
+// the Type structs.
+class TypeSpecPtrBuilder {
+ public:
+  TypeSpecPtrBuilder(const ResTable_typeSpec* header) : header_(header) {}
+
+  void AddType(const ResTable_type* type) {
+    ResTable_config config;
+    config.copyFromDtoH(type->config);
+    types_.push_back(Type{config, type});
+  }
+
+  TypeSpecPtr Build() {
+    // Check for overflow.
+    if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) {
+      return {};
+    }
+    TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type)));
+    type_spec->type_spec = header_;
+    type_spec->type_count = types_.size();
+    memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type));
+    return TypeSpecPtr(type_spec);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
+
+  const ResTable_typeSpec* header_;
+  std::vector<Type> types_;
+};
+
+}  // namespace
+
+class LoadedPackage {
+ public:
+  LoadedPackage() = default;
+
+  bool FindEntry(uint8_t type_id, uint16_t entry_id, const ResTable_config& config,
+                 LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+                 uint32_t* out_flags) const;
+
+  ResStringPool type_string_pool_;
+  ResStringPool key_string_pool_;
+  std::string package_name_;
+  int package_id_ = -1;
+
+  ByteBucketArray<TypeSpecPtr> type_specs_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
+};
+
+bool LoadedPackage::FindEntry(uint8_t type_id, uint16_t entry_id, const ResTable_config& config,
+                              LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+                              uint32_t* out_flags) const {
+  ATRACE_NAME("LoadedPackage::FindEntry");
+  const TypeSpecPtr& ptr = type_specs_[type_id];
+  if (ptr == nullptr) {
+    return false;
+  }
+
+  // Don't bother checking if the entry ID is larger than
+  // the number of entries.
+  if (entry_id >= dtohl(ptr->type_spec->entryCount)) {
+    return false;
+  }
+
+  const ResTable_config* best_config = nullptr;
+  const ResTable_type* best_type = nullptr;
+  uint32_t best_offset = 0;
+
+  for (uint32_t i = 0; i < ptr->type_count; i++) {
+    const Type* type = &ptr->types[i];
+
+    if (type->configuration.match(config) &&
+        (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) {
+      // The configuration matches and is better than the previous selection.
+      // Find the entry value if it exists for this configuration.
+      size_t entry_count = dtohl(type->type->entryCount);
+      if (entry_id < entry_count) {
+        const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
+            reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize));
+        const uint32_t offset = dtohl(entry_offsets[entry_id]);
+        if (offset != ResTable_type::NO_ENTRY) {
+          // There is an entry for this resource, record it.
+          best_config = &type->configuration;
+          best_type = type->type;
+          best_offset = offset + dtohl(type->type->entriesStart);
+        }
+      }
+    }
+  }
+
+  if (best_type == nullptr) {
+    return false;
+  }
+
+  const uint32_t* flags = reinterpret_cast<const uint32_t*>(ptr->type_spec + 1);
+  *out_flags = dtohl(flags[entry_id]);
+  *out_selected_config = *best_config;
+
+  const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
+      reinterpret_cast<const uint8_t*>(best_type) + best_offset);
+  out_entry->entry = best_entry;
+  out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1);
+  out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index));
+  return true;
+}
+
+// The destructor gets generated into arbitrary translation units
+// if left implicit, which causes the compiler to complain about
+// forward declarations and incomplete types.
+LoadedArsc::~LoadedArsc() {}
+
+bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, Entry* out_entry,
+                           ResTable_config* out_selected_config, uint32_t* out_flags) const {
+  ATRACE_NAME("LoadedArsc::FindEntry");
+  const uint8_t package_id = util::get_package_id(resid);
+  const uint8_t type_id = util::get_type_id(resid);
+  const uint16_t entry_id = util::get_entry_id(resid);
+
+  if (type_id == 0) {
+    LOG(ERROR) << "Invalid ID 0x" << std::hex << resid << std::dec << ".";
+    return false;
+  }
+
+  for (const auto& loaded_package : packages_) {
+    if (loaded_package->package_id_ == package_id) {
+      return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry,
+                                       out_selected_config, out_flags);
+    }
+  }
+  return false;
+}
+
+const std::string* LoadedArsc::GetPackageNameForId(uint32_t resid) const {
+  const uint8_t package_id = util::get_package_id(resid);
+  for (const auto& loaded_package : packages_) {
+    if (loaded_package->package_id_ == package_id) {
+      return &loaded_package->package_name_;
+    }
+  }
+  return nullptr;
+}
+
+static bool VerifyType(const Chunk& chunk) {
+  ATRACE_CALL();
+  const ResTable_type* header = chunk.header<ResTable_type>();
+
+  const size_t entry_count = dtohl(header->entryCount);
+  if (entry_count > std::numeric_limits<uint16_t>::max()) {
+    LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE.";
+    return false;
+  }
+
+  // Make sure that there is enough room for the entry offsets.
+  const size_t offsets_offset = chunk.header_size();
+  const size_t entries_offset = dtohl(header->entriesStart);
+  const size_t offsets_length = sizeof(uint32_t) * entry_count;
+
+  if (offsets_offset + offsets_length > entries_offset) {
+    LOG(ERROR) << "Entry offsets overlap actual entry data.";
+    return false;
+  }
+
+  if (entries_offset > chunk.size()) {
+    LOG(ERROR) << "Entry offsets extend beyond chunk.";
+    return false;
+  }
+
+  if (entries_offset & 0x03) {
+    LOG(ERROR) << "Entries start at unaligned address.";
+    return false;
+  }
+
+  // Check each entry offset.
+  const uint32_t* offsets =
+      reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(header) + offsets_offset);
+  for (size_t i = 0; i < entry_count; i++) {
+    uint32_t offset = dtohl(offsets[i]);
+    if (offset != ResTable_type::NO_ENTRY) {
+      // Check that the offset is aligned.
+      if (offset & 0x03) {
+        LOG(ERROR) << "Entry offset at index " << i << " is not 4-byte aligned.";
+        return false;
+      }
+
+      // Check that the offset doesn't overflow.
+      if (offset > std::numeric_limits<uint32_t>::max() - entries_offset) {
+        // Overflow in offset.
+        LOG(ERROR) << "Entry offset at index " << i << " is too large.";
+        return false;
+      }
+
+      offset += entries_offset;
+      if (offset > chunk.size() - sizeof(ResTable_entry)) {
+        LOG(ERROR) << "Entry offset at index " << i << " is too large. No room for ResTable_entry.";
+        return false;
+      }
+
+      const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+          reinterpret_cast<const uint8_t*>(header) + offset);
+      const size_t entry_size = dtohs(entry->size);
+      if (entry_size < sizeof(*entry)) {
+        LOG(ERROR) << "ResTable_entry size " << entry_size << " is too small.";
+        return false;
+      }
+
+      // Check the declared entrySize.
+      if (entry_size > chunk.size() || offset > chunk.size() - entry_size) {
+        LOG(ERROR) << "ResTable_entry size " << entry_size << " is too large.";
+        return false;
+      }
+
+      // If this is a map entry, then keep validating.
+      if (entry_size >= sizeof(ResTable_map_entry)) {
+        const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry);
+        const size_t map_entry_count = dtohl(map->count);
+
+        size_t map_entries_start = offset + entry_size;
+        if (map_entries_start & 0x03) {
+          LOG(ERROR) << "Map entries start at unaligned offset.";
+          return false;
+        }
+
+        // Each entry is sizeof(ResTable_map) big.
+        if (map_entry_count > ((chunk.size() - map_entries_start) / sizeof(ResTable_map))) {
+          LOG(ERROR) << "Too many map entries in ResTable_map_entry.";
+          return false;
+        }
+
+        // Great, all the map entries fit!.
+      } else {
+        // There needs to be room for one Res_value struct.
+        if (offset + entry_size > chunk.size() - sizeof(Res_value)) {
+          LOG(ERROR) << "No room for Res_value after ResTable_entry.";
+          return false;
+        }
+
+        const Res_value* value = reinterpret_cast<const Res_value*>(
+            reinterpret_cast<const uint8_t*>(entry) + entry_size);
+        const size_t value_size = dtohs(value->size);
+        if (value_size < sizeof(Res_value)) {
+          LOG(ERROR) << "Res_value is too small.";
+          return false;
+        }
+
+        if (value_size > chunk.size() || offset + entry_size > chunk.size() - value_size) {
+          LOG(ERROR) << "Res_value size is too large.";
+          return false;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+static bool LoadPackage(const Chunk& chunk, LoadedPackage* loaded_package) {
+  ATRACE_CALL();
+  const ResTable_package* header = chunk.header<ResTable_package>();
+  if (header == nullptr) {
+    LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small.";
+    return false;
+  }
+
+  loaded_package->package_id_ = dtohl(header->id);
+
+  // A TypeSpec builder. We use this to accumulate the set of Types
+  // available for a TypeSpec, and later build a single, contiguous block
+  // of memory that holds all the Types together with the TypeSpec.
+  std::unique_ptr<TypeSpecPtrBuilder> types_builder;
+
+  // Keep track of the last seen type index. Since type IDs are 1-based,
+  // this records their index, which is 0-based (type ID - 1).
+  uint8_t last_type_idx = 0;
+
+  ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
+  while (iter.HasNext()) {
+    const Chunk child_chunk = iter.Next();
+    switch (child_chunk.type()) {
+      case RES_STRING_POOL_TYPE: {
+        const uintptr_t pool_address =
+            reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>());
+        const uintptr_t header_address = reinterpret_cast<uintptr_t>(header);
+        if (pool_address == header_address + dtohl(header->typeStrings)) {
+          // This string pool is the type string pool.
+          status_t err = loaded_package->type_string_pool_.setTo(
+              child_chunk.header<ResStringPool_header>(), child_chunk.size());
+          if (err != NO_ERROR) {
+            LOG(ERROR) << "Corrupt package type string pool.";
+            return false;
+          }
+        } else if (pool_address == header_address + dtohl(header->keyStrings)) {
+          // This string pool is the key string pool.
+          status_t err = loaded_package->key_string_pool_.setTo(
+              child_chunk.header<ResStringPool_header>(), child_chunk.size());
+          if (err != NO_ERROR) {
+            LOG(ERROR) << "Corrupt package key string pool.";
+            return false;
+          }
+        } else {
+          LOG(WARNING) << "Too many string pool chunks found in package.";
+        }
+      } break;
+
+      case RES_TABLE_TYPE_SPEC_TYPE: {
+        ATRACE_NAME("LoadTableTypeSpec");
+
+        // Starting a new TypeSpec, so finish the old one if there was one.
+        if (types_builder) {
+          TypeSpecPtr type_spec_ptr = types_builder->Build();
+          if (type_spec_ptr == nullptr) {
+            LOG(ERROR) << "Too many type configurations, overflow detected.";
+            return false;
+          }
+
+          loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
+
+          types_builder = {};
+          last_type_idx = 0;
+        }
+
+        const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>();
+        if (type_spec == nullptr) {
+          LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE is too small.";
+          return false;
+        }
+
+        if (type_spec->id == 0) {
+          LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.";
+          return false;
+        }
+
+        // The data portion of this chunk contains entry_count 32bit entries,
+        // each one representing a set of flags.
+        // Here we only validate that the chunk is well formed.
+        const size_t entry_count = dtohl(type_spec->entryCount);
+
+        // There can only be 2^16 entries in a type, because that is the ID
+        // space for entries (EEEE) in the resource ID 0xPPTTEEEE.
+        if (entry_count > std::numeric_limits<uint16_t>::max()) {
+          LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_SPEC_TYPE: " << entry_count << ".";
+          return false;
+        }
+
+        if (entry_count * sizeof(uint32_t) > chunk.data_size()) {
+          LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_TYPE_SPEC_TYPE.";
+          return false;
+        }
+
+        last_type_idx = type_spec->id - 1;
+        types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec);
+      } break;
+
+      case RES_TABLE_TYPE_TYPE: {
+        const ResTable_type* type = child_chunk.header<ResTable_type>();
+        if (type == nullptr) {
+          LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE is too small.";
+          return false;
+        }
+
+        if (type->id == 0) {
+          LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE has invalid ID 0.";
+          return false;
+        }
+
+        // Type chunks must be preceded by their TypeSpec chunks.
+        if (!types_builder || type->id - 1 != last_type_idx) {
+          LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without "
+                        "RES_TABLE_TYPE_SPEC_TYPE.";
+          return false;
+        }
+
+        if (!VerifyType(child_chunk)) {
+          return false;
+        }
+
+        types_builder->AddType(type);
+      } break;
+
+      default:
+        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        break;
+    }
+  }
+
+  // Finish the last TypeSpec.
+  if (types_builder) {
+    TypeSpecPtr type_spec_ptr = types_builder->Build();
+    if (type_spec_ptr == nullptr) {
+      LOG(ERROR) << "Too many type configurations, overflow detected.";
+      return false;
+    }
+    loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
+  }
+
+  if (iter.HadError()) {
+    LOG(ERROR) << iter.GetLastError();
+    return false;
+  }
+  return true;
+}
+
+bool LoadedArsc::LoadTable(const Chunk& chunk) {
+  ATRACE_CALL();
+  const ResTable_header* header = chunk.header<ResTable_header>();
+  if (header == nullptr) {
+    LOG(ERROR) << "Chunk RES_TABLE_TYPE is too small.";
+    return false;
+  }
+
+  const size_t package_count = dtohl(header->packageCount);
+  size_t packages_seen = 0;
+
+  packages_.reserve(package_count);
+
+  ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
+  while (iter.HasNext()) {
+    const Chunk child_chunk = iter.Next();
+    switch (child_chunk.type()) {
+      case RES_STRING_POOL_TYPE:
+        // Only use the first string pool. Ignore others.
+        if (global_string_pool_.getError() == NO_INIT) {
+          status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(),
+                                                   child_chunk.size());
+          if (err != NO_ERROR) {
+            LOG(ERROR) << "Corrupt string pool.";
+            return false;
+          }
+        } else {
+          LOG(WARNING) << "Multiple string pool chunks found in resource table.";
+        }
+        break;
+
+      case RES_TABLE_PACKAGE_TYPE: {
+        if (packages_seen + 1 > package_count) {
+          LOG(ERROR) << "More package chunks were found than the " << package_count
+                     << " declared in the "
+                        "header.";
+          return false;
+        }
+        packages_seen++;
+
+        std::unique_ptr<LoadedPackage> loaded_package = util::make_unique<LoadedPackage>();
+        if (!LoadPackage(child_chunk, loaded_package.get())) {
+          return false;
+        }
+        packages_.push_back(std::move(loaded_package));
+      } break;
+
+      default:
+        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        break;
+    }
+  }
+
+  if (iter.HadError()) {
+    LOG(ERROR) << iter.GetLastError();
+    return false;
+  }
+  return true;
+}
+
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len) {
+  ATRACE_CALL();
+
+  // Not using make_unique because the constructor is private.
+  std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
+
+  ChunkIterator iter(data, len);
+  while (iter.HasNext()) {
+    const Chunk chunk = iter.Next();
+    switch (chunk.type()) {
+      case RES_TABLE_TYPE:
+        if (!loaded_arsc->LoadTable(chunk)) {
+          return {};
+        }
+        break;
+
+      default:
+        LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+        break;
+    }
+  }
+
+  if (iter.HadError()) {
+    LOG(ERROR) << iter.GetLastError();
+    return {};
+  }
+  return loaded_arsc;
+}
+
+}  // namespace android
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 1ac5085..7c381ef 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -1,4 +1,4 @@
-// Auto-generated by frameworks/base/tools/localedata/extract_icu_data.py
+// Auto-generated by ./tools/localedata/extract_icu_data.py
 
 const char SCRIPT_CODES[][4] = {
     /* 0  */ {'A', 'h', 'o', 'm'},
@@ -39,27 +39,27 @@
     /* 35 */ {'K', 'h', 'm', 'r'},
     /* 36 */ {'K', 'n', 'd', 'a'},
     /* 37 */ {'K', 'o', 'r', 'e'},
-    /* 38 */ {'K', 't', 'h', 'i'},
-    /* 39 */ {'L', 'a', 'n', 'a'},
-    /* 40 */ {'L', 'a', 'o', 'o'},
-    /* 41 */ {'L', 'a', 't', 'n'},
-    /* 42 */ {'L', 'e', 'p', 'c'},
-    /* 43 */ {'L', 'i', 'n', 'a'},
-    /* 44 */ {'L', 'i', 's', 'u'},
-    /* 45 */ {'L', 'y', 'c', 'i'},
-    /* 46 */ {'L', 'y', 'd', 'i'},
-    /* 47 */ {'M', 'a', 'n', 'd'},
-    /* 48 */ {'M', 'a', 'n', 'i'},
-    /* 49 */ {'M', 'e', 'r', 'c'},
-    /* 50 */ {'M', 'l', 'y', 'm'},
-    /* 51 */ {'M', 'o', 'n', 'g'},
-    /* 52 */ {'M', 'r', 'o', 'o'},
-    /* 53 */ {'M', 'y', 'm', 'r'},
-    /* 54 */ {'N', 'a', 'r', 'b'},
-    /* 55 */ {'N', 'k', 'o', 'o'},
-    /* 56 */ {'O', 'g', 'a', 'm'},
-    /* 57 */ {'O', 'r', 'k', 'h'},
-    /* 58 */ {'O', 'r', 'y', 'a'},
+    /* 38 */ {'L', 'a', 'n', 'a'},
+    /* 39 */ {'L', 'a', 'o', 'o'},
+    /* 40 */ {'L', 'a', 't', 'n'},
+    /* 41 */ {'L', 'e', 'p', 'c'},
+    /* 42 */ {'L', 'i', 'n', 'a'},
+    /* 43 */ {'L', 'i', 's', 'u'},
+    /* 44 */ {'L', 'y', 'c', 'i'},
+    /* 45 */ {'L', 'y', 'd', 'i'},
+    /* 46 */ {'M', 'a', 'n', 'd'},
+    /* 47 */ {'M', 'a', 'n', 'i'},
+    /* 48 */ {'M', 'e', 'r', 'c'},
+    /* 49 */ {'M', 'l', 'y', 'm'},
+    /* 50 */ {'M', 'o', 'n', 'g'},
+    /* 51 */ {'M', 'r', 'o', 'o'},
+    /* 52 */ {'M', 'y', 'm', 'r'},
+    /* 53 */ {'N', 'a', 'r', 'b'},
+    /* 54 */ {'N', 'k', 'o', 'o'},
+    /* 55 */ {'O', 'g', 'a', 'm'},
+    /* 56 */ {'O', 'r', 'k', 'h'},
+    /* 57 */ {'O', 'r', 'y', 'a'},
+    /* 58 */ {'O', 's', 'g', 'e'},
     /* 59 */ {'P', 'a', 'u', 'c'},
     /* 60 */ {'P', 'h', 'l', 'i'},
     /* 61 */ {'P', 'h', 'n', 'x'},
@@ -76,78 +76,147 @@
     /* 72 */ {'T', 'a', 'l', 'e'},
     /* 73 */ {'T', 'a', 'l', 'u'},
     /* 74 */ {'T', 'a', 'm', 'l'},
-    /* 75 */ {'T', 'a', 'v', 't'},
-    /* 76 */ {'T', 'e', 'l', 'u'},
-    /* 77 */ {'T', 'f', 'n', 'g'},
-    /* 78 */ {'T', 'h', 'a', 'a'},
-    /* 79 */ {'T', 'h', 'a', 'i'},
-    /* 80 */ {'T', 'i', 'b', 't'},
-    /* 81 */ {'U', 'g', 'a', 'r'},
-    /* 82 */ {'V', 'a', 'i', 'i'},
-    /* 83 */ {'X', 'p', 'e', 'o'},
-    /* 84 */ {'X', 's', 'u', 'x'},
-    /* 85 */ {'Y', 'i', 'i', 'i'},
-    /* 86 */ {'~', '~', '~', 'A'},
-    /* 87 */ {'~', '~', '~', 'B'},
+    /* 75 */ {'T', 'a', 'n', 'g'},
+    /* 76 */ {'T', 'a', 'v', 't'},
+    /* 77 */ {'T', 'e', 'l', 'u'},
+    /* 78 */ {'T', 'f', 'n', 'g'},
+    /* 79 */ {'T', 'h', 'a', 'a'},
+    /* 80 */ {'T', 'h', 'a', 'i'},
+    /* 81 */ {'T', 'i', 'b', 't'},
+    /* 82 */ {'U', 'g', 'a', 'r'},
+    /* 83 */ {'V', 'a', 'i', 'i'},
+    /* 84 */ {'X', 'p', 'e', 'o'},
+    /* 85 */ {'X', 's', 'u', 'x'},
+    /* 86 */ {'Y', 'i', 'i', 'i'},
+    /* 87 */ {'~', '~', '~', 'A'},
+    /* 88 */ {'~', '~', '~', 'B'},
 };
 
 
 const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
-    {0x61610000u, 41u}, // aa -> Latn
+    {0x61610000u, 40u}, // aa -> Latn
+    {0xA0000000u, 40u}, // aai -> Latn
+    {0xA8000000u, 40u}, // aak -> Latn
+    {0xD0000000u, 40u}, // aau -> Latn
     {0x61620000u, 15u}, // ab -> Cyrl
-    {0xC4200000u, 41u}, // abr -> Latn
-    {0x90400000u, 41u}, // ace -> Latn
-    {0x9C400000u, 41u}, // ach -> Latn
-    {0x80600000u, 41u}, // ada -> Latn
+    {0xA0200000u, 40u}, // abi -> Latn
+    {0xC4200000u, 40u}, // abr -> Latn
+    {0xCC200000u, 40u}, // abt -> Latn
+    {0xE0200000u, 40u}, // aby -> Latn
+    {0x8C400000u, 40u}, // acd -> Latn
+    {0x90400000u, 40u}, // ace -> Latn
+    {0x9C400000u, 40u}, // ach -> Latn
+    {0x80600000u, 40u}, // ada -> Latn
+    {0x90600000u, 40u}, // ade -> Latn
+    {0xA4600000u, 40u}, // adj -> Latn
     {0xE0600000u, 15u}, // ady -> Cyrl
+    {0xE4600000u, 40u}, // adz -> Latn
     {0x61650000u,  4u}, // ae -> Avst
     {0x84800000u,  1u}, // aeb -> Arab
-    {0x61660000u, 41u}, // af -> Latn
-    {0xC0C00000u, 41u}, // agq -> Latn
+    {0xE0800000u, 40u}, // aey -> Latn
+    {0x61660000u, 40u}, // af -> Latn
+    {0x88C00000u, 40u}, // agc -> Latn
+    {0x8CC00000u, 40u}, // agd -> Latn
+    {0x98C00000u, 40u}, // agg -> Latn
+    {0xB0C00000u, 40u}, // agm -> Latn
+    {0xB8C00000u, 40u}, // ago -> Latn
+    {0xC0C00000u, 40u}, // agq -> Latn
+    {0x80E00000u, 40u}, // aha -> Latn
+    {0xACE00000u, 40u}, // ahl -> Latn
     {0xB8E00000u,  0u}, // aho -> Ahom
-    {0x616B0000u, 41u}, // ak -> Latn
-    {0xA9400000u, 84u}, // akk -> Xsux
-    {0xB5600000u, 41u}, // aln -> Latn
+    {0x99200000u, 40u}, // ajg -> Latn
+    {0x616B0000u, 40u}, // ak -> Latn
+    {0xA9400000u, 85u}, // akk -> Xsux
+    {0x81600000u, 40u}, // ala -> Latn
+    {0xA1600000u, 40u}, // ali -> Latn
+    {0xB5600000u, 40u}, // aln -> Latn
     {0xCD600000u, 15u}, // alt -> Cyrl
     {0x616D0000u, 18u}, // am -> Ethi
-    {0xB9800000u, 41u}, // amo -> Latn
-    {0xE5C00000u, 41u}, // aoz -> Latn
+    {0xB1800000u, 40u}, // amm -> Latn
+    {0xB5800000u, 40u}, // amn -> Latn
+    {0xB9800000u, 40u}, // amo -> Latn
+    {0xBD800000u, 40u}, // amp -> Latn
+    {0x89A00000u, 40u}, // anc -> Latn
+    {0xA9A00000u, 40u}, // ank -> Latn
+    {0xB5A00000u, 40u}, // ann -> Latn
+    {0xE1A00000u, 40u}, // any -> Latn
+    {0xA5C00000u, 40u}, // aoj -> Latn
+    {0xB1C00000u, 40u}, // aom -> Latn
+    {0xE5C00000u, 40u}, // aoz -> Latn
+    {0x89E00000u,  1u}, // apc -> Arab
+    {0x8DE00000u,  1u}, // apd -> Arab
+    {0x91E00000u, 40u}, // ape -> Latn
+    {0xC5E00000u, 40u}, // apr -> Latn
+    {0xC9E00000u, 40u}, // aps -> Latn
+    {0xE5E00000u, 40u}, // apz -> Latn
     {0x61720000u,  1u}, // ar -> Arab
-    {0x61725842u, 87u}, // ar-XB -> ~~~B
+    {0x61725842u, 88u}, // ar-XB -> ~~~B
     {0x8A200000u,  2u}, // arc -> Armi
-    {0xB6200000u, 41u}, // arn -> Latn
-    {0xBA200000u, 41u}, // aro -> Latn
+    {0x9E200000u, 40u}, // arh -> Latn
+    {0xB6200000u, 40u}, // arn -> Latn
+    {0xBA200000u, 40u}, // aro -> Latn
     {0xC2200000u,  1u}, // arq -> Arab
     {0xE2200000u,  1u}, // ary -> Arab
     {0xE6200000u,  1u}, // arz -> Arab
     {0x61730000u,  7u}, // as -> Beng
-    {0x82400000u, 41u}, // asa -> Latn
+    {0x82400000u, 40u}, // asa -> Latn
     {0x92400000u, 68u}, // ase -> Sgnw
-    {0xCE400000u, 41u}, // ast -> Latn
-    {0xA6600000u, 41u}, // atj -> Latn
+    {0x9A400000u, 40u}, // asg -> Latn
+    {0xBA400000u, 40u}, // aso -> Latn
+    {0xCE400000u, 40u}, // ast -> Latn
+    {0x82600000u, 40u}, // ata -> Latn
+    {0x9A600000u, 40u}, // atg -> Latn
+    {0xA6600000u, 40u}, // atj -> Latn
+    {0xE2800000u, 40u}, // auy -> Latn
     {0x61760000u, 15u}, // av -> Cyrl
+    {0xAEA00000u,  1u}, // avl -> Arab
+    {0xB6A00000u, 40u}, // avn -> Latn
+    {0xCEA00000u, 40u}, // avt -> Latn
+    {0xD2A00000u, 40u}, // avu -> Latn
     {0x82C00000u, 16u}, // awa -> Deva
-    {0x61790000u, 41u}, // ay -> Latn
-    {0x617A0000u, 41u}, // az -> Latn
+    {0x86C00000u, 40u}, // awb -> Latn
+    {0xBAC00000u, 40u}, // awo -> Latn
+    {0xDEC00000u, 40u}, // awx -> Latn
+    {0x61790000u, 40u}, // ay -> Latn
+    {0x87000000u, 40u}, // ayb -> Latn
+    {0x617A0000u, 40u}, // az -> Latn
     {0x617A4951u,  1u}, // az-IQ -> Arab
     {0x617A4952u,  1u}, // az-IR -> Arab
     {0x617A5255u, 15u}, // az-RU -> Cyrl
     {0x62610000u, 15u}, // ba -> Cyrl
     {0xAC010000u,  1u}, // bal -> Arab
-    {0xB4010000u, 41u}, // ban -> Latn
+    {0xB4010000u, 40u}, // ban -> Latn
     {0xBC010000u, 16u}, // bap -> Deva
-    {0xC4010000u, 41u}, // bar -> Latn
-    {0xC8010000u, 41u}, // bas -> Latn
+    {0xC4010000u, 40u}, // bar -> Latn
+    {0xC8010000u, 40u}, // bas -> Latn
+    {0xD4010000u, 40u}, // bav -> Latn
     {0xDC010000u,  5u}, // bax -> Bamu
-    {0x88210000u, 41u}, // bbc -> Latn
-    {0xA4210000u, 41u}, // bbj -> Latn
-    {0xA0410000u, 41u}, // bci -> Latn
+    {0x80210000u, 40u}, // bba -> Latn
+    {0x84210000u, 40u}, // bbb -> Latn
+    {0x88210000u, 40u}, // bbc -> Latn
+    {0x8C210000u, 40u}, // bbd -> Latn
+    {0xA4210000u, 40u}, // bbj -> Latn
+    {0xBC210000u, 40u}, // bbp -> Latn
+    {0xC4210000u, 40u}, // bbr -> Latn
+    {0x94410000u, 40u}, // bcf -> Latn
+    {0x9C410000u, 40u}, // bch -> Latn
+    {0xA0410000u, 40u}, // bci -> Latn
+    {0xB0410000u, 40u}, // bcm -> Latn
+    {0xB4410000u, 40u}, // bcn -> Latn
+    {0xB8410000u, 40u}, // bco -> Latn
+    {0xC0410000u, 18u}, // bcq -> Ethi
+    {0xD0410000u, 40u}, // bcu -> Latn
+    {0x8C610000u, 40u}, // bdd -> Latn
     {0x62650000u, 15u}, // be -> Cyrl
+    {0x94810000u, 40u}, // bef -> Latn
+    {0x9C810000u, 40u}, // beh -> Latn
     {0xA4810000u,  1u}, // bej -> Arab
-    {0xB0810000u, 41u}, // bem -> Latn
-    {0xD8810000u, 41u}, // bew -> Latn
-    {0xE4810000u, 41u}, // bez -> Latn
-    {0x8CA10000u, 41u}, // bfd -> Latn
+    {0xB0810000u, 40u}, // bem -> Latn
+    {0xCC810000u, 40u}, // bet -> Latn
+    {0xD8810000u, 40u}, // bew -> Latn
+    {0xDC810000u, 40u}, // bex -> Latn
+    {0xE4810000u, 40u}, // bez -> Latn
+    {0x8CA10000u, 40u}, // bfd -> Latn
     {0xC0A10000u, 74u}, // bfq -> Taml
     {0xCCA10000u,  1u}, // bft -> Arab
     {0xE0A10000u, 16u}, // bfy -> Deva
@@ -155,663 +224,1202 @@
     {0x88C10000u, 16u}, // bgc -> Deva
     {0xB4C10000u,  1u}, // bgn -> Arab
     {0xDCC10000u, 21u}, // bgx -> Grek
-    {0x62680000u, 38u}, // bh -> Kthi
     {0x84E10000u, 16u}, // bhb -> Deva
+    {0x98E10000u, 40u}, // bhg -> Latn
     {0xA0E10000u, 16u}, // bhi -> Deva
-    {0xA8E10000u, 41u}, // bhk -> Latn
+    {0xA8E10000u, 40u}, // bhk -> Latn
+    {0xACE10000u, 40u}, // bhl -> Latn
     {0xB8E10000u, 16u}, // bho -> Deva
-    {0x62690000u, 41u}, // bi -> Latn
-    {0xA9010000u, 41u}, // bik -> Latn
-    {0xB5010000u, 41u}, // bin -> Latn
+    {0xE0E10000u, 40u}, // bhy -> Latn
+    {0x62690000u, 40u}, // bi -> Latn
+    {0x85010000u, 40u}, // bib -> Latn
+    {0x99010000u, 40u}, // big -> Latn
+    {0xA9010000u, 40u}, // bik -> Latn
+    {0xB1010000u, 40u}, // bim -> Latn
+    {0xB5010000u, 40u}, // bin -> Latn
+    {0xB9010000u, 40u}, // bio -> Latn
+    {0xC1010000u, 40u}, // biq -> Latn
+    {0x9D210000u, 40u}, // bjh -> Latn
+    {0xA1210000u, 18u}, // bji -> Ethi
     {0xA5210000u, 16u}, // bjj -> Deva
-    {0xB5210000u, 41u}, // bjn -> Latn
-    {0xB1410000u, 41u}, // bkm -> Latn
-    {0xD1410000u, 41u}, // bku -> Latn
-    {0xCD610000u, 75u}, // blt -> Tavt
-    {0x626D0000u, 41u}, // bm -> Latn
-    {0xC1810000u, 41u}, // bmq -> Latn
+    {0xB5210000u, 40u}, // bjn -> Latn
+    {0xB9210000u, 40u}, // bjo -> Latn
+    {0xC5210000u, 40u}, // bjr -> Latn
+    {0xE5210000u, 40u}, // bjz -> Latn
+    {0x89410000u, 40u}, // bkc -> Latn
+    {0xB1410000u, 40u}, // bkm -> Latn
+    {0xC1410000u, 40u}, // bkq -> Latn
+    {0xD1410000u, 40u}, // bku -> Latn
+    {0xD5410000u, 40u}, // bkv -> Latn
+    {0xCD610000u, 76u}, // blt -> Tavt
+    {0x626D0000u, 40u}, // bm -> Latn
+    {0x9D810000u, 40u}, // bmh -> Latn
+    {0xA9810000u, 40u}, // bmk -> Latn
+    {0xC1810000u, 40u}, // bmq -> Latn
+    {0xD1810000u, 40u}, // bmu -> Latn
     {0x626E0000u,  7u}, // bn -> Beng
-    {0x626F0000u, 80u}, // bo -> Tibt
+    {0x99A10000u, 40u}, // bng -> Latn
+    {0xB1A10000u, 40u}, // bnm -> Latn
+    {0xBDA10000u, 40u}, // bnp -> Latn
+    {0x626F0000u, 81u}, // bo -> Tibt
+    {0xA5C10000u, 40u}, // boj -> Latn
+    {0xB1C10000u, 40u}, // bom -> Latn
+    {0xB5C10000u, 40u}, // bon -> Latn
     {0xE1E10000u,  7u}, // bpy -> Beng
+    {0x8A010000u, 40u}, // bqc -> Latn
     {0xA2010000u,  1u}, // bqi -> Arab
-    {0xD6010000u, 41u}, // bqv -> Latn
-    {0x62720000u, 41u}, // br -> Latn
+    {0xBE010000u, 40u}, // bqp -> Latn
+    {0xD6010000u, 40u}, // bqv -> Latn
+    {0x62720000u, 40u}, // br -> Latn
     {0x82210000u, 16u}, // bra -> Deva
     {0x9E210000u,  1u}, // brh -> Arab
     {0xDE210000u, 16u}, // brx -> Deva
-    {0x62730000u, 41u}, // bs -> Latn
+    {0xE6210000u, 40u}, // brz -> Latn
+    {0x62730000u, 40u}, // bs -> Latn
+    {0xA6410000u, 40u}, // bsj -> Latn
     {0xC2410000u,  6u}, // bsq -> Bass
-    {0xCA410000u, 41u}, // bss -> Latn
-    {0xBA610000u, 41u}, // bto -> Latn
+    {0xCA410000u, 40u}, // bss -> Latn
+    {0xCE410000u, 18u}, // bst -> Ethi
+    {0xBA610000u, 40u}, // bto -> Latn
+    {0xCE610000u, 40u}, // btt -> Latn
     {0xD6610000u, 16u}, // btv -> Deva
     {0x82810000u, 15u}, // bua -> Cyrl
-    {0x8A810000u, 41u}, // buc -> Latn
-    {0x9A810000u, 41u}, // bug -> Latn
-    {0xB2810000u, 41u}, // bum -> Latn
-    {0x86A10000u, 41u}, // bvb -> Latn
+    {0x8A810000u, 40u}, // buc -> Latn
+    {0x8E810000u, 40u}, // bud -> Latn
+    {0x9A810000u, 40u}, // bug -> Latn
+    {0xAA810000u, 40u}, // buk -> Latn
+    {0xB2810000u, 40u}, // bum -> Latn
+    {0xBA810000u, 40u}, // buo -> Latn
+    {0xCA810000u, 40u}, // bus -> Latn
+    {0xD2810000u, 40u}, // buu -> Latn
+    {0x86A10000u, 40u}, // bvb -> Latn
+    {0x8EC10000u, 40u}, // bwd -> Latn
+    {0xC6C10000u, 40u}, // bwr -> Latn
+    {0x9EE10000u, 40u}, // bxh -> Latn
+    {0x93010000u, 40u}, // bye -> Latn
     {0xB7010000u, 18u}, // byn -> Ethi
-    {0xD7010000u, 41u}, // byv -> Latn
-    {0x93210000u, 41u}, // bze -> Latn
-    {0x63610000u, 41u}, // ca -> Latn
-    {0x9C420000u, 41u}, // cch -> Latn
+    {0xC7010000u, 40u}, // byr -> Latn
+    {0xCB010000u, 40u}, // bys -> Latn
+    {0xD7010000u, 40u}, // byv -> Latn
+    {0xDF010000u, 40u}, // byx -> Latn
+    {0x83210000u, 40u}, // bza -> Latn
+    {0x93210000u, 40u}, // bze -> Latn
+    {0x97210000u, 40u}, // bzf -> Latn
+    {0x9F210000u, 40u}, // bzh -> Latn
+    {0xDB210000u, 40u}, // bzw -> Latn
+    {0x63610000u, 40u}, // ca -> Latn
+    {0xB4020000u, 40u}, // can -> Latn
+    {0xA4220000u, 40u}, // cbj -> Latn
+    {0x9C420000u, 40u}, // cch -> Latn
     {0xBC420000u,  7u}, // ccp -> Beng
     {0x63650000u, 15u}, // ce -> Cyrl
-    {0x84820000u, 41u}, // ceb -> Latn
-    {0x98C20000u, 41u}, // cgg -> Latn
-    {0x63680000u, 41u}, // ch -> Latn
-    {0xA8E20000u, 41u}, // chk -> Latn
+    {0x84820000u, 40u}, // ceb -> Latn
+    {0x80A20000u, 40u}, // cfa -> Latn
+    {0x98C20000u, 40u}, // cgg -> Latn
+    {0x63680000u, 40u}, // ch -> Latn
+    {0xA8E20000u, 40u}, // chk -> Latn
     {0xB0E20000u, 15u}, // chm -> Cyrl
-    {0xB8E20000u, 41u}, // cho -> Latn
-    {0xBCE20000u, 41u}, // chp -> Latn
+    {0xB8E20000u, 40u}, // cho -> Latn
+    {0xBCE20000u, 40u}, // chp -> Latn
     {0xC4E20000u, 12u}, // chr -> Cher
     {0x81220000u,  1u}, // cja -> Arab
     {0xB1220000u, 11u}, // cjm -> Cham
+    {0xD5220000u, 40u}, // cjv -> Latn
     {0x85420000u,  1u}, // ckb -> Arab
-    {0x636F0000u, 41u}, // co -> Latn
+    {0xAD420000u, 40u}, // ckl -> Latn
+    {0xB9420000u, 40u}, // cko -> Latn
+    {0xE1420000u, 40u}, // cky -> Latn
+    {0x81620000u, 40u}, // cla -> Latn
+    {0x91820000u, 40u}, // cme -> Latn
+    {0x636F0000u, 40u}, // co -> Latn
     {0xBDC20000u, 13u}, // cop -> Copt
-    {0xC9E20000u, 41u}, // cps -> Latn
+    {0xC9E20000u, 40u}, // cps -> Latn
     {0x63720000u,  9u}, // cr -> Cans
     {0xA6220000u,  9u}, // crj -> Cans
     {0xAA220000u,  9u}, // crk -> Cans
     {0xAE220000u,  9u}, // crl -> Cans
     {0xB2220000u,  9u}, // crm -> Cans
-    {0xCA220000u, 41u}, // crs -> Latn
-    {0x63730000u, 41u}, // cs -> Latn
-    {0x86420000u, 41u}, // csb -> Latn
+    {0xCA220000u, 40u}, // crs -> Latn
+    {0x63730000u, 40u}, // cs -> Latn
+    {0x86420000u, 40u}, // csb -> Latn
     {0xDA420000u,  9u}, // csw -> Cans
     {0x8E620000u, 59u}, // ctd -> Pauc
     {0x63750000u, 15u}, // cu -> Cyrl
     {0x63760000u, 15u}, // cv -> Cyrl
-    {0x63790000u, 41u}, // cy -> Latn
-    {0x64610000u, 41u}, // da -> Latn
-    {0xA8030000u, 41u}, // dak -> Latn
+    {0x63790000u, 40u}, // cy -> Latn
+    {0x64610000u, 40u}, // da -> Latn
+    {0x8C030000u, 40u}, // dad -> Latn
+    {0x94030000u, 40u}, // daf -> Latn
+    {0x98030000u, 40u}, // dag -> Latn
+    {0x9C030000u, 40u}, // dah -> Latn
+    {0xA8030000u, 40u}, // dak -> Latn
     {0xC4030000u, 15u}, // dar -> Cyrl
-    {0xD4030000u, 41u}, // dav -> Latn
+    {0xD4030000u, 40u}, // dav -> Latn
+    {0x8C230000u, 40u}, // dbd -> Latn
+    {0xC0230000u, 40u}, // dbq -> Latn
     {0x88430000u,  1u}, // dcc -> Arab
-    {0x64650000u, 41u}, // de -> Latn
-    {0xB4830000u, 41u}, // den -> Latn
-    {0xC4C30000u, 41u}, // dgr -> Latn
-    {0x91230000u, 41u}, // dje -> Latn
-    {0xA5A30000u, 41u}, // dnj -> Latn
+    {0xB4630000u, 40u}, // ddn -> Latn
+    {0x64650000u, 40u}, // de -> Latn
+    {0x8C830000u, 40u}, // ded -> Latn
+    {0xB4830000u, 40u}, // den -> Latn
+    {0x80C30000u, 40u}, // dga -> Latn
+    {0x9CC30000u, 40u}, // dgh -> Latn
+    {0xA0C30000u, 40u}, // dgi -> Latn
+    {0xACC30000u,  1u}, // dgl -> Arab
+    {0xC4C30000u, 40u}, // dgr -> Latn
+    {0xE4C30000u, 40u}, // dgz -> Latn
+    {0x81030000u, 40u}, // dia -> Latn
+    {0x91230000u, 40u}, // dje -> Latn
+    {0xA5A30000u, 40u}, // dnj -> Latn
+    {0x85C30000u, 40u}, // dob -> Latn
     {0xA1C30000u,  1u}, // doi -> Arab
-    {0x86430000u, 41u}, // dsb -> Latn
-    {0xB2630000u, 41u}, // dtm -> Latn
-    {0xBE630000u, 41u}, // dtp -> Latn
-    {0x82830000u, 41u}, // dua -> Latn
-    {0x64760000u, 78u}, // dv -> Thaa
-    {0xBB030000u, 41u}, // dyo -> Latn
-    {0xD3030000u, 41u}, // dyu -> Latn
-    {0x647A0000u, 80u}, // dz -> Tibt
-    {0xD0240000u, 41u}, // ebu -> Latn
-    {0x65650000u, 41u}, // ee -> Latn
-    {0xA0A40000u, 41u}, // efi -> Latn
-    {0xACC40000u, 41u}, // egl -> Latn
+    {0xBDC30000u, 40u}, // dop -> Latn
+    {0xD9C30000u, 40u}, // dow -> Latn
+    {0xA2230000u, 40u}, // dri -> Latn
+    {0xCA230000u, 18u}, // drs -> Ethi
+    {0x86430000u, 40u}, // dsb -> Latn
+    {0xB2630000u, 40u}, // dtm -> Latn
+    {0xBE630000u, 40u}, // dtp -> Latn
+    {0xCA630000u, 40u}, // dts -> Latn
+    {0xE2630000u, 16u}, // dty -> Deva
+    {0x82830000u, 40u}, // dua -> Latn
+    {0x8A830000u, 40u}, // duc -> Latn
+    {0x8E830000u, 40u}, // dud -> Latn
+    {0x9A830000u, 40u}, // dug -> Latn
+    {0x64760000u, 79u}, // dv -> Thaa
+    {0x82A30000u, 40u}, // dva -> Latn
+    {0xDAC30000u, 40u}, // dww -> Latn
+    {0xBB030000u, 40u}, // dyo -> Latn
+    {0xD3030000u, 40u}, // dyu -> Latn
+    {0x647A0000u, 81u}, // dz -> Tibt
+    {0x9B230000u, 40u}, // dzg -> Latn
+    {0xD0240000u, 40u}, // ebu -> Latn
+    {0x65650000u, 40u}, // ee -> Latn
+    {0xA0A40000u, 40u}, // efi -> Latn
+    {0xACC40000u, 40u}, // egl -> Latn
     {0xE0C40000u, 17u}, // egy -> Egyp
     {0xE1440000u, 32u}, // eky -> Kali
     {0x656C0000u, 21u}, // el -> Grek
-    {0x656E0000u, 41u}, // en -> Latn
-    {0x656E5841u, 86u}, // en-XA -> ~~~A
-    {0x656F0000u, 41u}, // eo -> Latn
-    {0x65730000u, 41u}, // es -> Latn
-    {0xD2440000u, 41u}, // esu -> Latn
-    {0x65740000u, 41u}, // et -> Latn
+    {0x81840000u, 40u}, // ema -> Latn
+    {0xA1840000u, 40u}, // emi -> Latn
+    {0x656E0000u, 40u}, // en -> Latn
+    {0x656E5841u, 87u}, // en-XA -> ~~~A
+    {0xB5A40000u, 40u}, // enn -> Latn
+    {0xC1A40000u, 40u}, // enq -> Latn
+    {0x656F0000u, 40u}, // eo -> Latn
+    {0xA2240000u, 40u}, // eri -> Latn
+    {0x65730000u, 40u}, // es -> Latn
+    {0xD2440000u, 40u}, // esu -> Latn
+    {0x65740000u, 40u}, // et -> Latn
+    {0xC6640000u, 40u}, // etr -> Latn
     {0xCE640000u, 30u}, // ett -> Ital
-    {0x65750000u, 41u}, // eu -> Latn
-    {0xBAC40000u, 41u}, // ewo -> Latn
-    {0xCEE40000u, 41u}, // ext -> Latn
+    {0xD2640000u, 40u}, // etu -> Latn
+    {0xDE640000u, 40u}, // etx -> Latn
+    {0x65750000u, 40u}, // eu -> Latn
+    {0xBAC40000u, 40u}, // ewo -> Latn
+    {0xCEE40000u, 40u}, // ext -> Latn
     {0x66610000u,  1u}, // fa -> Arab
-    {0xB4050000u, 41u}, // fan -> Latn
-    {0x66660000u, 41u}, // ff -> Latn
-    {0xB0A50000u, 41u}, // ffm -> Latn
-    {0x66690000u, 41u}, // fi -> Latn
+    {0x80050000u, 40u}, // faa -> Latn
+    {0x84050000u, 40u}, // fab -> Latn
+    {0x98050000u, 40u}, // fag -> Latn
+    {0xA0050000u, 40u}, // fai -> Latn
+    {0xB4050000u, 40u}, // fan -> Latn
+    {0x66660000u, 40u}, // ff -> Latn
+    {0xA0A50000u, 40u}, // ffi -> Latn
+    {0xB0A50000u, 40u}, // ffm -> Latn
+    {0x66690000u, 40u}, // fi -> Latn
     {0x81050000u,  1u}, // fia -> Arab
-    {0xAD050000u, 41u}, // fil -> Latn
-    {0xCD050000u, 41u}, // fit -> Latn
-    {0x666A0000u, 41u}, // fj -> Latn
-    {0x666F0000u, 41u}, // fo -> Latn
-    {0xB5C50000u, 41u}, // fon -> Latn
-    {0x66720000u, 41u}, // fr -> Latn
-    {0x8A250000u, 41u}, // frc -> Latn
-    {0xBE250000u, 41u}, // frp -> Latn
-    {0xC6250000u, 41u}, // frr -> Latn
-    {0xCA250000u, 41u}, // frs -> Latn
-    {0x8E850000u, 41u}, // fud -> Latn
-    {0xC2850000u, 41u}, // fuq -> Latn
-    {0xC6850000u, 41u}, // fur -> Latn
-    {0xD6850000u, 41u}, // fuv -> Latn
-    {0xC6A50000u, 41u}, // fvr -> Latn
-    {0x66790000u, 41u}, // fy -> Latn
-    {0x67610000u, 41u}, // ga -> Latn
-    {0x80060000u, 41u}, // gaa -> Latn
-    {0x98060000u, 41u}, // gag -> Latn
+    {0xAD050000u, 40u}, // fil -> Latn
+    {0xCD050000u, 40u}, // fit -> Latn
+    {0x666A0000u, 40u}, // fj -> Latn
+    {0xC5650000u, 40u}, // flr -> Latn
+    {0xBD850000u, 40u}, // fmp -> Latn
+    {0x666F0000u, 40u}, // fo -> Latn
+    {0x8DC50000u, 40u}, // fod -> Latn
+    {0xB5C50000u, 40u}, // fon -> Latn
+    {0xC5C50000u, 40u}, // for -> Latn
+    {0x91E50000u, 40u}, // fpe -> Latn
+    {0xCA050000u, 40u}, // fqs -> Latn
+    {0x66720000u, 40u}, // fr -> Latn
+    {0x8A250000u, 40u}, // frc -> Latn
+    {0xBE250000u, 40u}, // frp -> Latn
+    {0xC6250000u, 40u}, // frr -> Latn
+    {0xCA250000u, 40u}, // frs -> Latn
+    {0x86850000u,  1u}, // fub -> Arab
+    {0x8E850000u, 40u}, // fud -> Latn
+    {0x92850000u, 40u}, // fue -> Latn
+    {0x96850000u, 40u}, // fuf -> Latn
+    {0x9E850000u, 40u}, // fuh -> Latn
+    {0xC2850000u, 40u}, // fuq -> Latn
+    {0xC6850000u, 40u}, // fur -> Latn
+    {0xD6850000u, 40u}, // fuv -> Latn
+    {0xE2850000u, 40u}, // fuy -> Latn
+    {0xC6A50000u, 40u}, // fvr -> Latn
+    {0x66790000u, 40u}, // fy -> Latn
+    {0x67610000u, 40u}, // ga -> Latn
+    {0x80060000u, 40u}, // gaa -> Latn
+    {0x94060000u, 40u}, // gaf -> Latn
+    {0x98060000u, 40u}, // gag -> Latn
+    {0x9C060000u, 40u}, // gah -> Latn
+    {0xA4060000u, 40u}, // gaj -> Latn
+    {0xB0060000u, 40u}, // gam -> Latn
     {0xB4060000u, 24u}, // gan -> Hans
-    {0xE0060000u, 41u}, // gay -> Latn
+    {0xD8060000u, 40u}, // gaw -> Latn
+    {0xE0060000u, 40u}, // gay -> Latn
+    {0x94260000u, 40u}, // gbf -> Latn
     {0xB0260000u, 16u}, // gbm -> Deva
+    {0xE0260000u, 40u}, // gby -> Latn
     {0xE4260000u,  1u}, // gbz -> Arab
-    {0xC4460000u, 41u}, // gcr -> Latn
-    {0x67640000u, 41u}, // gd -> Latn
+    {0xC4460000u, 40u}, // gcr -> Latn
+    {0x67640000u, 40u}, // gd -> Latn
+    {0x90660000u, 40u}, // gde -> Latn
+    {0xB4660000u, 40u}, // gdn -> Latn
+    {0xC4660000u, 40u}, // gdr -> Latn
+    {0x84860000u, 40u}, // geb -> Latn
+    {0xA4860000u, 40u}, // gej -> Latn
+    {0xAC860000u, 40u}, // gel -> Latn
     {0xE4860000u, 18u}, // gez -> Ethi
+    {0xA8A60000u, 40u}, // gfk -> Latn
     {0xB4C60000u, 16u}, // ggn -> Deva
-    {0xAD060000u, 41u}, // gil -> Latn
+    {0xC8E60000u, 40u}, // ghs -> Latn
+    {0xAD060000u, 40u}, // gil -> Latn
+    {0xB1060000u, 40u}, // gim -> Latn
     {0xA9260000u,  1u}, // gjk -> Arab
+    {0xB5260000u, 40u}, // gjn -> Latn
     {0xD1260000u,  1u}, // gju -> Arab
-    {0x676C0000u, 41u}, // gl -> Latn
+    {0xB5460000u, 40u}, // gkn -> Latn
+    {0xBD460000u, 40u}, // gkp -> Latn
+    {0x676C0000u, 40u}, // gl -> Latn
     {0xA9660000u,  1u}, // glk -> Arab
-    {0x676E0000u, 41u}, // gn -> Latn
+    {0xB1860000u, 40u}, // gmm -> Latn
+    {0xD5860000u, 18u}, // gmv -> Ethi
+    {0x676E0000u, 40u}, // gn -> Latn
+    {0x8DA60000u, 40u}, // gnd -> Latn
+    {0x99A60000u, 40u}, // gng -> Latn
+    {0x8DC60000u, 40u}, // god -> Latn
+    {0x95C60000u, 18u}, // gof -> Ethi
+    {0xA1C60000u, 40u}, // goi -> Latn
     {0xB1C60000u, 16u}, // gom -> Deva
-    {0xB5C60000u, 76u}, // gon -> Telu
-    {0xC5C60000u, 41u}, // gor -> Latn
-    {0xC9C60000u, 41u}, // gos -> Latn
+    {0xB5C60000u, 77u}, // gon -> Telu
+    {0xC5C60000u, 40u}, // gor -> Latn
+    {0xC9C60000u, 40u}, // gos -> Latn
     {0xCDC60000u, 20u}, // got -> Goth
     {0x8A260000u, 14u}, // grc -> Cprt
     {0xCE260000u,  7u}, // grt -> Beng
-    {0xDA460000u, 41u}, // gsw -> Latn
+    {0xDA260000u, 40u}, // grw -> Latn
+    {0xDA460000u, 40u}, // gsw -> Latn
     {0x67750000u, 22u}, // gu -> Gujr
-    {0x86860000u, 41u}, // gub -> Latn
-    {0x8A860000u, 41u}, // guc -> Latn
-    {0xC6860000u, 41u}, // gur -> Latn
-    {0xE6860000u, 41u}, // guz -> Latn
-    {0x67760000u, 41u}, // gv -> Latn
+    {0x86860000u, 40u}, // gub -> Latn
+    {0x8A860000u, 40u}, // guc -> Latn
+    {0x8E860000u, 40u}, // gud -> Latn
+    {0xC6860000u, 40u}, // gur -> Latn
+    {0xDA860000u, 40u}, // guw -> Latn
+    {0xDE860000u, 40u}, // gux -> Latn
+    {0xE6860000u, 40u}, // guz -> Latn
+    {0x67760000u, 40u}, // gv -> Latn
+    {0x96A60000u, 40u}, // gvf -> Latn
     {0xC6A60000u, 16u}, // gvr -> Deva
-    {0xA2C60000u, 41u}, // gwi -> Latn
-    {0x68610000u, 41u}, // ha -> Latn
+    {0xCAA60000u, 40u}, // gvs -> Latn
+    {0x8AC60000u,  1u}, // gwc -> Arab
+    {0xA2C60000u, 40u}, // gwi -> Latn
+    {0xCEC60000u,  1u}, // gwt -> Arab
+    {0xA3060000u, 40u}, // gyi -> Latn
+    {0x68610000u, 40u}, // ha -> Latn
     {0x6861434Du,  1u}, // ha-CM -> Arab
     {0x68615344u,  1u}, // ha-SD -> Arab
+    {0x98070000u, 40u}, // hag -> Latn
     {0xA8070000u, 24u}, // hak -> Hans
-    {0xD8070000u, 41u}, // haw -> Latn
+    {0xB0070000u, 40u}, // ham -> Latn
+    {0xD8070000u, 40u}, // haw -> Latn
     {0xE4070000u,  1u}, // haz -> Arab
+    {0x84270000u, 40u}, // hbb -> Latn
+    {0xE0670000u, 18u}, // hdy -> Ethi
     {0x68650000u, 27u}, // he -> Hebr
+    {0xE0E70000u, 40u}, // hhy -> Latn
     {0x68690000u, 16u}, // hi -> Deva
-    {0x95070000u, 41u}, // hif -> Latn
-    {0xAD070000u, 41u}, // hil -> Latn
+    {0x81070000u, 40u}, // hia -> Latn
+    {0x95070000u, 40u}, // hif -> Latn
+    {0x99070000u, 40u}, // hig -> Latn
+    {0x9D070000u, 40u}, // hih -> Latn
+    {0xAD070000u, 40u}, // hil -> Latn
+    {0x81670000u, 40u}, // hla -> Latn
     {0xD1670000u, 28u}, // hlu -> Hluw
     {0x8D870000u, 62u}, // hmd -> Plrd
+    {0xCD870000u, 40u}, // hmt -> Latn
     {0x8DA70000u,  1u}, // hnd -> Arab
     {0x91A70000u, 16u}, // hne -> Deva
     {0xA5A70000u, 29u}, // hnj -> Hmng
-    {0xB5A70000u, 41u}, // hnn -> Latn
+    {0xB5A70000u, 40u}, // hnn -> Latn
     {0xB9A70000u,  1u}, // hno -> Arab
-    {0x686F0000u, 41u}, // ho -> Latn
+    {0x686F0000u, 40u}, // ho -> Latn
     {0x89C70000u, 16u}, // hoc -> Deva
     {0xA5C70000u, 16u}, // hoj -> Deva
-    {0x68720000u, 41u}, // hr -> Latn
-    {0x86470000u, 41u}, // hsb -> Latn
+    {0xCDC70000u, 40u}, // hot -> Latn
+    {0x68720000u, 40u}, // hr -> Latn
+    {0x86470000u, 40u}, // hsb -> Latn
     {0xB6470000u, 24u}, // hsn -> Hans
-    {0x68740000u, 41u}, // ht -> Latn
-    {0x68750000u, 41u}, // hu -> Latn
+    {0x68740000u, 40u}, // ht -> Latn
+    {0x68750000u, 40u}, // hu -> Latn
+    {0xA2870000u, 40u}, // hui -> Latn
     {0x68790000u,  3u}, // hy -> Armn
-    {0x687A0000u, 41u}, // hz -> Latn
-    {0x69610000u, 41u}, // ia -> Latn
-    {0x80280000u, 41u}, // iba -> Latn
-    {0x84280000u, 41u}, // ibb -> Latn
-    {0x69640000u, 41u}, // id -> Latn
-    {0x69670000u, 41u}, // ig -> Latn
-    {0x69690000u, 85u}, // ii -> Yiii
-    {0x696B0000u, 41u}, // ik -> Latn
-    {0xCD480000u, 41u}, // ikt -> Latn
-    {0xB9680000u, 41u}, // ilo -> Latn
-    {0x696E0000u, 41u}, // in -> Latn
+    {0x687A0000u, 40u}, // hz -> Latn
+    {0x69610000u, 40u}, // ia -> Latn
+    {0xB4080000u, 40u}, // ian -> Latn
+    {0xC4080000u, 40u}, // iar -> Latn
+    {0x80280000u, 40u}, // iba -> Latn
+    {0x84280000u, 40u}, // ibb -> Latn
+    {0xE0280000u, 40u}, // iby -> Latn
+    {0x80480000u, 40u}, // ica -> Latn
+    {0x9C480000u, 40u}, // ich -> Latn
+    {0x69640000u, 40u}, // id -> Latn
+    {0x8C680000u, 40u}, // idd -> Latn
+    {0xA0680000u, 40u}, // idi -> Latn
+    {0xD0680000u, 40u}, // idu -> Latn
+    {0x69670000u, 40u}, // ig -> Latn
+    {0x84C80000u, 40u}, // igb -> Latn
+    {0x90C80000u, 40u}, // ige -> Latn
+    {0x69690000u, 86u}, // ii -> Yiii
+    {0xA5280000u, 40u}, // ijj -> Latn
+    {0x696B0000u, 40u}, // ik -> Latn
+    {0xA9480000u, 40u}, // ikk -> Latn
+    {0xCD480000u, 40u}, // ikt -> Latn
+    {0xD9480000u, 40u}, // ikw -> Latn
+    {0xDD480000u, 40u}, // ikx -> Latn
+    {0xB9680000u, 40u}, // ilo -> Latn
+    {0xB9880000u, 40u}, // imo -> Latn
+    {0x696E0000u, 40u}, // in -> Latn
     {0x9DA80000u, 15u}, // inh -> Cyrl
-    {0x69730000u, 41u}, // is -> Latn
-    {0x69740000u, 41u}, // it -> Latn
+    {0xD1C80000u, 40u}, // iou -> Latn
+    {0xA2280000u, 40u}, // iri -> Latn
+    {0x69730000u, 40u}, // is -> Latn
+    {0x69740000u, 40u}, // it -> Latn
     {0x69750000u,  9u}, // iu -> Cans
     {0x69770000u, 27u}, // iw -> Hebr
-    {0x9F280000u, 41u}, // izh -> Latn
+    {0xB2C80000u, 40u}, // iwm -> Latn
+    {0xCAC80000u, 40u}, // iws -> Latn
+    {0x9F280000u, 40u}, // izh -> Latn
+    {0xA3280000u, 40u}, // izi -> Latn
     {0x6A610000u, 31u}, // ja -> Jpan
-    {0xB0090000u, 41u}, // jam -> Latn
-    {0xB8C90000u, 41u}, // jgo -> Latn
+    {0x84090000u, 40u}, // jab -> Latn
+    {0xB0090000u, 40u}, // jam -> Latn
+    {0xD0290000u, 40u}, // jbu -> Latn
+    {0xB4890000u, 40u}, // jen -> Latn
+    {0xA8C90000u, 40u}, // jgk -> Latn
+    {0xB8C90000u, 40u}, // jgo -> Latn
     {0x6A690000u, 27u}, // ji -> Hebr
-    {0x89890000u, 41u}, // jmc -> Latn
+    {0x85090000u, 40u}, // jib -> Latn
+    {0x89890000u, 40u}, // jmc -> Latn
     {0xAD890000u, 16u}, // jml -> Deva
-    {0xCE890000u, 41u}, // jut -> Latn
-    {0x6A760000u, 41u}, // jv -> Latn
-    {0x6A770000u, 41u}, // jw -> Latn
+    {0x82290000u, 40u}, // jra -> Latn
+    {0xCE890000u, 40u}, // jut -> Latn
+    {0x6A760000u, 40u}, // jv -> Latn
+    {0x6A770000u, 40u}, // jw -> Latn
     {0x6B610000u, 19u}, // ka -> Geor
     {0x800A0000u, 15u}, // kaa -> Cyrl
-    {0x840A0000u, 41u}, // kab -> Latn
-    {0x880A0000u, 41u}, // kac -> Latn
-    {0xA40A0000u, 41u}, // kaj -> Latn
-    {0xB00A0000u, 41u}, // kam -> Latn
-    {0xB80A0000u, 41u}, // kao -> Latn
+    {0x840A0000u, 40u}, // kab -> Latn
+    {0x880A0000u, 40u}, // kac -> Latn
+    {0x8C0A0000u, 40u}, // kad -> Latn
+    {0xA00A0000u, 40u}, // kai -> Latn
+    {0xA40A0000u, 40u}, // kaj -> Latn
+    {0xB00A0000u, 40u}, // kam -> Latn
+    {0xB80A0000u, 40u}, // kao -> Latn
     {0x8C2A0000u, 15u}, // kbd -> Cyrl
-    {0x984A0000u, 41u}, // kcg -> Latn
-    {0xA84A0000u, 41u}, // kck -> Latn
-    {0x906A0000u, 41u}, // kde -> Latn
-    {0xCC6A0000u, 79u}, // kdt -> Thai
-    {0x808A0000u, 41u}, // kea -> Latn
-    {0xB48A0000u, 41u}, // ken -> Latn
-    {0xB8AA0000u, 41u}, // kfo -> Latn
+    {0xB02A0000u, 40u}, // kbm -> Latn
+    {0xBC2A0000u, 40u}, // kbp -> Latn
+    {0xC02A0000u, 40u}, // kbq -> Latn
+    {0xDC2A0000u, 40u}, // kbx -> Latn
+    {0xE02A0000u,  1u}, // kby -> Arab
+    {0x984A0000u, 40u}, // kcg -> Latn
+    {0xA84A0000u, 40u}, // kck -> Latn
+    {0xAC4A0000u, 40u}, // kcl -> Latn
+    {0xCC4A0000u, 40u}, // kct -> Latn
+    {0x906A0000u, 40u}, // kde -> Latn
+    {0x9C6A0000u,  1u}, // kdh -> Arab
+    {0xAC6A0000u, 40u}, // kdl -> Latn
+    {0xCC6A0000u, 80u}, // kdt -> Thai
+    {0x808A0000u, 40u}, // kea -> Latn
+    {0xB48A0000u, 40u}, // ken -> Latn
+    {0xE48A0000u, 40u}, // kez -> Latn
+    {0xB8AA0000u, 40u}, // kfo -> Latn
     {0xC4AA0000u, 16u}, // kfr -> Deva
     {0xE0AA0000u, 16u}, // kfy -> Deva
-    {0x6B670000u, 41u}, // kg -> Latn
-    {0x90CA0000u, 41u}, // kge -> Latn
-    {0xBCCA0000u, 41u}, // kgp -> Latn
-    {0x80EA0000u, 41u}, // kha -> Latn
+    {0x6B670000u, 40u}, // kg -> Latn
+    {0x90CA0000u, 40u}, // kge -> Latn
+    {0x94CA0000u, 40u}, // kgf -> Latn
+    {0xBCCA0000u, 40u}, // kgp -> Latn
+    {0x80EA0000u, 40u}, // kha -> Latn
     {0x84EA0000u, 73u}, // khb -> Talu
     {0xB4EA0000u, 16u}, // khn -> Deva
-    {0xC0EA0000u, 41u}, // khq -> Latn
-    {0xCCEA0000u, 53u}, // kht -> Mymr
+    {0xC0EA0000u, 40u}, // khq -> Latn
+    {0xC8EA0000u, 40u}, // khs -> Latn
+    {0xCCEA0000u, 52u}, // kht -> Mymr
     {0xD8EA0000u,  1u}, // khw -> Arab
-    {0x6B690000u, 41u}, // ki -> Latn
-    {0xD10A0000u, 41u}, // kiu -> Latn
-    {0x6B6A0000u, 41u}, // kj -> Latn
-    {0x992A0000u, 40u}, // kjg -> Laoo
+    {0xE4EA0000u, 40u}, // khz -> Latn
+    {0x6B690000u, 40u}, // ki -> Latn
+    {0xA50A0000u, 40u}, // kij -> Latn
+    {0xD10A0000u, 40u}, // kiu -> Latn
+    {0xD90A0000u, 40u}, // kiw -> Latn
+    {0x6B6A0000u, 40u}, // kj -> Latn
+    {0x8D2A0000u, 40u}, // kjd -> Latn
+    {0x992A0000u, 39u}, // kjg -> Laoo
+    {0xC92A0000u, 40u}, // kjs -> Latn
+    {0xE12A0000u, 40u}, // kjy -> Latn
     {0x6B6B0000u, 15u}, // kk -> Cyrl
     {0x6B6B4146u,  1u}, // kk-AF -> Arab
     {0x6B6B434Eu,  1u}, // kk-CN -> Arab
     {0x6B6B4952u,  1u}, // kk-IR -> Arab
     {0x6B6B4D4Eu,  1u}, // kk-MN -> Arab
-    {0xA54A0000u, 41u}, // kkj -> Latn
-    {0x6B6C0000u, 41u}, // kl -> Latn
-    {0xB56A0000u, 41u}, // kln -> Latn
+    {0x894A0000u, 40u}, // kkc -> Latn
+    {0xA54A0000u, 40u}, // kkj -> Latn
+    {0x6B6C0000u, 40u}, // kl -> Latn
+    {0xB56A0000u, 40u}, // kln -> Latn
+    {0xC16A0000u, 40u}, // klq -> Latn
+    {0xCD6A0000u, 40u}, // klt -> Latn
+    {0xDD6A0000u, 40u}, // klx -> Latn
     {0x6B6D0000u, 35u}, // km -> Khmr
-    {0x858A0000u, 41u}, // kmb -> Latn
+    {0x858A0000u, 40u}, // kmb -> Latn
+    {0x9D8A0000u, 40u}, // kmh -> Latn
+    {0xB98A0000u, 40u}, // kmo -> Latn
+    {0xC98A0000u, 40u}, // kms -> Latn
+    {0xD18A0000u, 40u}, // kmu -> Latn
+    {0xD98A0000u, 40u}, // kmw -> Latn
     {0x6B6E0000u, 36u}, // kn -> Knda
+    {0xBDAA0000u, 40u}, // knp -> Latn
     {0x6B6F0000u, 37u}, // ko -> Kore
     {0xA1CA0000u, 15u}, // koi -> Cyrl
     {0xA9CA0000u, 16u}, // kok -> Deva
-    {0xC9CA0000u, 41u}, // kos -> Latn
-    {0x91EA0000u, 41u}, // kpe -> Latn
+    {0xADCA0000u, 40u}, // kol -> Latn
+    {0xC9CA0000u, 40u}, // kos -> Latn
+    {0xE5CA0000u, 40u}, // koz -> Latn
+    {0x91EA0000u, 40u}, // kpe -> Latn
+    {0x95EA0000u, 40u}, // kpf -> Latn
+    {0xB9EA0000u, 40u}, // kpo -> Latn
+    {0xC5EA0000u, 40u}, // kpr -> Latn
+    {0xDDEA0000u, 40u}, // kpx -> Latn
+    {0x860A0000u, 40u}, // kqb -> Latn
+    {0x960A0000u, 40u}, // kqf -> Latn
+    {0xCA0A0000u, 40u}, // kqs -> Latn
+    {0xE20A0000u, 18u}, // kqy -> Ethi
     {0x8A2A0000u, 15u}, // krc -> Cyrl
-    {0xA22A0000u, 41u}, // kri -> Latn
-    {0xA62A0000u, 41u}, // krj -> Latn
-    {0xAE2A0000u, 41u}, // krl -> Latn
+    {0xA22A0000u, 40u}, // kri -> Latn
+    {0xA62A0000u, 40u}, // krj -> Latn
+    {0xAE2A0000u, 40u}, // krl -> Latn
+    {0xCA2A0000u, 40u}, // krs -> Latn
     {0xD22A0000u, 16u}, // kru -> Deva
     {0x6B730000u,  1u}, // ks -> Arab
-    {0x864A0000u, 41u}, // ksb -> Latn
-    {0x964A0000u, 41u}, // ksf -> Latn
-    {0x9E4A0000u, 41u}, // ksh -> Latn
-    {0x6B750000u, 41u}, // ku -> Latn
+    {0x864A0000u, 40u}, // ksb -> Latn
+    {0x8E4A0000u, 40u}, // ksd -> Latn
+    {0x964A0000u, 40u}, // ksf -> Latn
+    {0x9E4A0000u, 40u}, // ksh -> Latn
+    {0xA64A0000u, 40u}, // ksj -> Latn
+    {0xC64A0000u, 40u}, // ksr -> Latn
+    {0x866A0000u, 18u}, // ktb -> Ethi
+    {0xB26A0000u, 40u}, // ktm -> Latn
+    {0xBA6A0000u, 40u}, // kto -> Latn
+    {0x6B750000u, 40u}, // ku -> Latn
     {0x6B754952u,  1u}, // ku-IR -> Arab
     {0x6B754C42u,  1u}, // ku-LB -> Arab
+    {0x868A0000u, 40u}, // kub -> Latn
+    {0x8E8A0000u, 40u}, // kud -> Latn
+    {0x928A0000u, 40u}, // kue -> Latn
+    {0xA68A0000u, 40u}, // kuj -> Latn
     {0xB28A0000u, 15u}, // kum -> Cyrl
+    {0xB68A0000u, 40u}, // kun -> Latn
+    {0xBE8A0000u, 40u}, // kup -> Latn
+    {0xCA8A0000u, 40u}, // kus -> Latn
     {0x6B760000u, 15u}, // kv -> Cyrl
-    {0xC6AA0000u, 41u}, // kvr -> Latn
+    {0x9AAA0000u, 40u}, // kvg -> Latn
+    {0xC6AA0000u, 40u}, // kvr -> Latn
     {0xDEAA0000u,  1u}, // kvx -> Arab
-    {0x6B770000u, 41u}, // kw -> Latn
-    {0xB2EA0000u, 79u}, // kxm -> Thai
+    {0x6B770000u, 40u}, // kw -> Latn
+    {0xA6CA0000u, 40u}, // kwj -> Latn
+    {0xBACA0000u, 40u}, // kwo -> Latn
+    {0x82EA0000u, 40u}, // kxa -> Latn
+    {0x8AEA0000u, 18u}, // kxc -> Ethi
+    {0xB2EA0000u, 80u}, // kxm -> Thai
     {0xBEEA0000u,  1u}, // kxp -> Arab
+    {0xDAEA0000u, 40u}, // kxw -> Latn
+    {0xE6EA0000u, 40u}, // kxz -> Latn
     {0x6B790000u, 15u}, // ky -> Cyrl
     {0x6B79434Eu,  1u}, // ky-CN -> Arab
-    {0x6B795452u, 41u}, // ky-TR -> Latn
-    {0x6C610000u, 41u}, // la -> Latn
-    {0x840B0000u, 43u}, // lab -> Lina
+    {0x6B795452u, 40u}, // ky-TR -> Latn
+    {0x930A0000u, 40u}, // kye -> Latn
+    {0xDF0A0000u, 40u}, // kyx -> Latn
+    {0xC72A0000u, 40u}, // kzr -> Latn
+    {0x6C610000u, 40u}, // la -> Latn
+    {0x840B0000u, 42u}, // lab -> Lina
     {0x8C0B0000u, 27u}, // lad -> Hebr
-    {0x980B0000u, 41u}, // lag -> Latn
+    {0x980B0000u, 40u}, // lag -> Latn
     {0x9C0B0000u,  1u}, // lah -> Arab
-    {0xA40B0000u, 41u}, // laj -> Latn
-    {0x6C620000u, 41u}, // lb -> Latn
+    {0xA40B0000u, 40u}, // laj -> Latn
+    {0xC80B0000u, 40u}, // las -> Latn
+    {0x6C620000u, 40u}, // lb -> Latn
     {0x902B0000u, 15u}, // lbe -> Cyrl
-    {0xD82B0000u, 41u}, // lbw -> Latn
-    {0xBC4B0000u, 79u}, // lcp -> Thai
-    {0xBC8B0000u, 42u}, // lep -> Lepc
+    {0xD02B0000u, 40u}, // lbu -> Latn
+    {0xD82B0000u, 40u}, // lbw -> Latn
+    {0xB04B0000u, 40u}, // lcm -> Latn
+    {0xBC4B0000u, 80u}, // lcp -> Thai
+    {0x846B0000u, 40u}, // ldb -> Latn
+    {0x8C8B0000u, 40u}, // led -> Latn
+    {0x908B0000u, 40u}, // lee -> Latn
+    {0xB08B0000u, 40u}, // lem -> Latn
+    {0xBC8B0000u, 41u}, // lep -> Lepc
+    {0xC08B0000u, 40u}, // leq -> Latn
+    {0xD08B0000u, 40u}, // leu -> Latn
     {0xE48B0000u, 15u}, // lez -> Cyrl
-    {0x6C670000u, 41u}, // lg -> Latn
-    {0x6C690000u, 41u}, // li -> Latn
+    {0x6C670000u, 40u}, // lg -> Latn
+    {0x98CB0000u, 40u}, // lgg -> Latn
+    {0x6C690000u, 40u}, // li -> Latn
+    {0x810B0000u, 40u}, // lia -> Latn
+    {0x8D0B0000u, 40u}, // lid -> Latn
     {0x950B0000u, 16u}, // lif -> Deva
-    {0xA50B0000u, 41u}, // lij -> Latn
-    {0xC90B0000u, 44u}, // lis -> Lisu
-    {0xBD2B0000u, 41u}, // ljp -> Latn
+    {0x990B0000u, 40u}, // lig -> Latn
+    {0x9D0B0000u, 40u}, // lih -> Latn
+    {0xA50B0000u, 40u}, // lij -> Latn
+    {0xC90B0000u, 43u}, // lis -> Lisu
+    {0xBD2B0000u, 40u}, // ljp -> Latn
     {0xA14B0000u,  1u}, // lki -> Arab
-    {0xCD4B0000u, 41u}, // lkt -> Latn
-    {0xB58B0000u, 76u}, // lmn -> Telu
-    {0xB98B0000u, 41u}, // lmo -> Latn
-    {0x6C6E0000u, 41u}, // ln -> Latn
-    {0x6C6F0000u, 40u}, // lo -> Laoo
-    {0xADCB0000u, 41u}, // lol -> Latn
-    {0xE5CB0000u, 41u}, // loz -> Latn
+    {0xCD4B0000u, 40u}, // lkt -> Latn
+    {0x916B0000u, 40u}, // lle -> Latn
+    {0xB56B0000u, 40u}, // lln -> Latn
+    {0xB58B0000u, 77u}, // lmn -> Telu
+    {0xB98B0000u, 40u}, // lmo -> Latn
+    {0xBD8B0000u, 40u}, // lmp -> Latn
+    {0x6C6E0000u, 40u}, // ln -> Latn
+    {0xC9AB0000u, 40u}, // lns -> Latn
+    {0xD1AB0000u, 40u}, // lnu -> Latn
+    {0x6C6F0000u, 39u}, // lo -> Laoo
+    {0xA5CB0000u, 40u}, // loj -> Latn
+    {0xA9CB0000u, 40u}, // lok -> Latn
+    {0xADCB0000u, 40u}, // lol -> Latn
+    {0xC5CB0000u, 40u}, // lor -> Latn
+    {0xC9CB0000u, 40u}, // los -> Latn
+    {0xE5CB0000u, 40u}, // loz -> Latn
     {0x8A2B0000u,  1u}, // lrc -> Arab
-    {0x6C740000u, 41u}, // lt -> Latn
-    {0x9A6B0000u, 41u}, // ltg -> Latn
-    {0x6C750000u, 41u}, // lu -> Latn
-    {0x828B0000u, 41u}, // lua -> Latn
-    {0xBA8B0000u, 41u}, // luo -> Latn
-    {0xE28B0000u, 41u}, // luy -> Latn
+    {0x6C740000u, 40u}, // lt -> Latn
+    {0x9A6B0000u, 40u}, // ltg -> Latn
+    {0x6C750000u, 40u}, // lu -> Latn
+    {0x828B0000u, 40u}, // lua -> Latn
+    {0xBA8B0000u, 40u}, // luo -> Latn
+    {0xE28B0000u, 40u}, // luy -> Latn
     {0xE68B0000u,  1u}, // luz -> Arab
-    {0x6C760000u, 41u}, // lv -> Latn
-    {0xAECB0000u, 79u}, // lwl -> Thai
+    {0x6C760000u, 40u}, // lv -> Latn
+    {0xAECB0000u, 80u}, // lwl -> Thai
     {0x9F2B0000u, 24u}, // lzh -> Hans
-    {0xE72B0000u, 41u}, // lzz -> Latn
-    {0x8C0C0000u, 41u}, // mad -> Latn
-    {0x940C0000u, 41u}, // maf -> Latn
+    {0xE72B0000u, 40u}, // lzz -> Latn
+    {0x8C0C0000u, 40u}, // mad -> Latn
+    {0x940C0000u, 40u}, // maf -> Latn
     {0x980C0000u, 16u}, // mag -> Deva
     {0xA00C0000u, 16u}, // mai -> Deva
-    {0xA80C0000u, 41u}, // mak -> Latn
-    {0xB40C0000u, 41u}, // man -> Latn
-    {0xB40C474Eu, 55u}, // man-GN -> Nkoo
-    {0xC80C0000u, 41u}, // mas -> Latn
-    {0xE40C0000u, 41u}, // maz -> Latn
+    {0xA80C0000u, 40u}, // mak -> Latn
+    {0xB40C0000u, 40u}, // man -> Latn
+    {0xB40C474Eu, 54u}, // man-GN -> Nkoo
+    {0xC80C0000u, 40u}, // mas -> Latn
+    {0xD80C0000u, 40u}, // maw -> Latn
+    {0xE40C0000u, 40u}, // maz -> Latn
+    {0x9C2C0000u, 40u}, // mbh -> Latn
+    {0xB82C0000u, 40u}, // mbo -> Latn
+    {0xC02C0000u, 40u}, // mbq -> Latn
+    {0xD02C0000u, 40u}, // mbu -> Latn
+    {0xD82C0000u, 40u}, // mbw -> Latn
+    {0xA04C0000u, 40u}, // mci -> Latn
+    {0xBC4C0000u, 40u}, // mcp -> Latn
+    {0xC04C0000u, 40u}, // mcq -> Latn
+    {0xC44C0000u, 40u}, // mcr -> Latn
+    {0xD04C0000u, 40u}, // mcu -> Latn
+    {0x806C0000u, 40u}, // mda -> Latn
+    {0x906C0000u,  1u}, // mde -> Arab
     {0x946C0000u, 15u}, // mdf -> Cyrl
-    {0x9C6C0000u, 41u}, // mdh -> Latn
-    {0xC46C0000u, 41u}, // mdr -> Latn
-    {0xB48C0000u, 41u}, // men -> Latn
-    {0xC48C0000u, 41u}, // mer -> Latn
+    {0x9C6C0000u, 40u}, // mdh -> Latn
+    {0xA46C0000u, 40u}, // mdj -> Latn
+    {0xC46C0000u, 40u}, // mdr -> Latn
+    {0xDC6C0000u, 18u}, // mdx -> Ethi
+    {0x8C8C0000u, 40u}, // med -> Latn
+    {0x908C0000u, 40u}, // mee -> Latn
+    {0xA88C0000u, 40u}, // mek -> Latn
+    {0xB48C0000u, 40u}, // men -> Latn
+    {0xC48C0000u, 40u}, // mer -> Latn
+    {0xCC8C0000u, 40u}, // met -> Latn
+    {0xD08C0000u, 40u}, // meu -> Latn
     {0x80AC0000u,  1u}, // mfa -> Arab
-    {0x90AC0000u, 41u}, // mfe -> Latn
-    {0x6D670000u, 41u}, // mg -> Latn
-    {0x9CCC0000u, 41u}, // mgh -> Latn
-    {0xB8CC0000u, 41u}, // mgo -> Latn
+    {0x90AC0000u, 40u}, // mfe -> Latn
+    {0xB4AC0000u, 40u}, // mfn -> Latn
+    {0xB8AC0000u, 40u}, // mfo -> Latn
+    {0xC0AC0000u, 40u}, // mfq -> Latn
+    {0x6D670000u, 40u}, // mg -> Latn
+    {0x9CCC0000u, 40u}, // mgh -> Latn
+    {0xACCC0000u, 40u}, // mgl -> Latn
+    {0xB8CC0000u, 40u}, // mgo -> Latn
     {0xBCCC0000u, 16u}, // mgp -> Deva
-    {0xE0CC0000u, 41u}, // mgy -> Latn
-    {0x6D680000u, 41u}, // mh -> Latn
-    {0x6D690000u, 41u}, // mi -> Latn
-    {0xB50C0000u, 41u}, // min -> Latn
+    {0xE0CC0000u, 40u}, // mgy -> Latn
+    {0x6D680000u, 40u}, // mh -> Latn
+    {0xA0EC0000u, 40u}, // mhi -> Latn
+    {0xACEC0000u, 40u}, // mhl -> Latn
+    {0x6D690000u, 40u}, // mi -> Latn
+    {0x950C0000u, 40u}, // mif -> Latn
+    {0xB50C0000u, 40u}, // min -> Latn
     {0xC90C0000u, 26u}, // mis -> Hatr
+    {0xD90C0000u, 40u}, // miw -> Latn
     {0x6D6B0000u, 15u}, // mk -> Cyrl
-    {0x6D6C0000u, 50u}, // ml -> Mlym
-    {0xC96C0000u, 41u}, // mls -> Latn
+    {0xA14C0000u,  1u}, // mki -> Arab
+    {0xAD4C0000u, 40u}, // mkl -> Latn
+    {0xBD4C0000u, 40u}, // mkp -> Latn
+    {0xD94C0000u, 40u}, // mkw -> Latn
+    {0x6D6C0000u, 49u}, // ml -> Mlym
+    {0x916C0000u, 40u}, // mle -> Latn
+    {0xBD6C0000u, 40u}, // mlp -> Latn
+    {0xC96C0000u, 40u}, // mls -> Latn
+    {0xB98C0000u, 40u}, // mmo -> Latn
+    {0xD18C0000u, 40u}, // mmu -> Latn
+    {0xDD8C0000u, 40u}, // mmx -> Latn
     {0x6D6E0000u, 15u}, // mn -> Cyrl
-    {0x6D6E434Eu, 51u}, // mn-CN -> Mong
+    {0x6D6E434Eu, 50u}, // mn-CN -> Mong
+    {0x81AC0000u, 40u}, // mna -> Latn
+    {0x95AC0000u, 40u}, // mnf -> Latn
     {0xA1AC0000u,  7u}, // mni -> Beng
-    {0xD9AC0000u, 53u}, // mnw -> Mymr
-    {0x91CC0000u, 41u}, // moe -> Latn
-    {0x9DCC0000u, 41u}, // moh -> Latn
-    {0xC9CC0000u, 41u}, // mos -> Latn
+    {0xD9AC0000u, 52u}, // mnw -> Mymr
+    {0x81CC0000u, 40u}, // moa -> Latn
+    {0x91CC0000u, 40u}, // moe -> Latn
+    {0x9DCC0000u, 40u}, // moh -> Latn
+    {0xC9CC0000u, 40u}, // mos -> Latn
+    {0xDDCC0000u, 40u}, // mox -> Latn
+    {0xBDEC0000u, 40u}, // mpp -> Latn
+    {0xC9EC0000u, 40u}, // mps -> Latn
+    {0xCDEC0000u, 40u}, // mpt -> Latn
+    {0xDDEC0000u, 40u}, // mpx -> Latn
+    {0xAE0C0000u, 40u}, // mql -> Latn
     {0x6D720000u, 16u}, // mr -> Deva
     {0x8E2C0000u, 16u}, // mrd -> Deva
     {0xA62C0000u, 15u}, // mrj -> Cyrl
-    {0xD22C0000u, 52u}, // mru -> Mroo
-    {0x6D730000u, 41u}, // ms -> Latn
+    {0xBA2C0000u, 51u}, // mro -> Mroo
+    {0x6D730000u, 40u}, // ms -> Latn
     {0x6D734343u,  1u}, // ms-CC -> Arab
     {0x6D734944u,  1u}, // ms-ID -> Arab
-    {0x6D740000u, 41u}, // mt -> Latn
+    {0x6D740000u, 40u}, // mt -> Latn
+    {0x8A6C0000u, 40u}, // mtc -> Latn
+    {0x966C0000u, 40u}, // mtf -> Latn
+    {0xA26C0000u, 40u}, // mti -> Latn
     {0xC66C0000u, 16u}, // mtr -> Deva
-    {0x828C0000u, 41u}, // mua -> Latn
-    {0xCA8C0000u, 41u}, // mus -> Latn
+    {0x828C0000u, 40u}, // mua -> Latn
+    {0xC68C0000u, 40u}, // mur -> Latn
+    {0xCA8C0000u, 40u}, // mus -> Latn
+    {0x82AC0000u, 40u}, // mva -> Latn
+    {0xB6AC0000u, 40u}, // mvn -> Latn
     {0xE2AC0000u,  1u}, // mvy -> Arab
-    {0xAACC0000u, 41u}, // mwk -> Latn
+    {0xAACC0000u, 40u}, // mwk -> Latn
     {0xC6CC0000u, 16u}, // mwr -> Deva
-    {0xD6CC0000u, 41u}, // mwv -> Latn
-    {0x8AEC0000u, 41u}, // mxc -> Latn
-    {0x6D790000u, 53u}, // my -> Mymr
+    {0xD6CC0000u, 40u}, // mwv -> Latn
+    {0x8AEC0000u, 40u}, // mxc -> Latn
+    {0xB2EC0000u, 40u}, // mxm -> Latn
+    {0x6D790000u, 52u}, // my -> Mymr
+    {0xAB0C0000u, 40u}, // myk -> Latn
+    {0xB30C0000u, 18u}, // mym -> Ethi
     {0xD70C0000u, 15u}, // myv -> Cyrl
-    {0xDF0C0000u, 41u}, // myx -> Latn
-    {0xE70C0000u, 47u}, // myz -> Mand
+    {0xDB0C0000u, 40u}, // myw -> Latn
+    {0xDF0C0000u, 40u}, // myx -> Latn
+    {0xE70C0000u, 46u}, // myz -> Mand
+    {0xAB2C0000u, 40u}, // mzk -> Latn
+    {0xB32C0000u, 40u}, // mzm -> Latn
     {0xB72C0000u,  1u}, // mzn -> Arab
-    {0x6E610000u, 41u}, // na -> Latn
+    {0xBF2C0000u, 40u}, // mzp -> Latn
+    {0xDB2C0000u, 40u}, // mzw -> Latn
+    {0xE72C0000u, 40u}, // mzz -> Latn
+    {0x6E610000u, 40u}, // na -> Latn
+    {0x880D0000u, 40u}, // nac -> Latn
+    {0x940D0000u, 40u}, // naf -> Latn
+    {0xA80D0000u, 40u}, // nak -> Latn
     {0xB40D0000u, 24u}, // nan -> Hans
-    {0xBC0D0000u, 41u}, // nap -> Latn
-    {0xC00D0000u, 41u}, // naq -> Latn
-    {0x6E620000u, 41u}, // nb -> Latn
-    {0x9C4D0000u, 41u}, // nch -> Latn
-    {0x6E640000u, 41u}, // nd -> Latn
-    {0x886D0000u, 41u}, // ndc -> Latn
-    {0xC86D0000u, 41u}, // nds -> Latn
+    {0xBC0D0000u, 40u}, // nap -> Latn
+    {0xC00D0000u, 40u}, // naq -> Latn
+    {0xC80D0000u, 40u}, // nas -> Latn
+    {0x6E620000u, 40u}, // nb -> Latn
+    {0x804D0000u, 40u}, // nca -> Latn
+    {0x904D0000u, 40u}, // nce -> Latn
+    {0x944D0000u, 40u}, // ncf -> Latn
+    {0x9C4D0000u, 40u}, // nch -> Latn
+    {0xB84D0000u, 40u}, // nco -> Latn
+    {0xD04D0000u, 40u}, // ncu -> Latn
+    {0x6E640000u, 40u}, // nd -> Latn
+    {0x886D0000u, 40u}, // ndc -> Latn
+    {0xC86D0000u, 40u}, // nds -> Latn
     {0x6E650000u, 16u}, // ne -> Deva
+    {0x848D0000u, 40u}, // neb -> Latn
     {0xD88D0000u, 16u}, // new -> Deva
-    {0x6E670000u, 41u}, // ng -> Latn
-    {0xACCD0000u, 41u}, // ngl -> Latn
-    {0x90ED0000u, 41u}, // nhe -> Latn
-    {0xD8ED0000u, 41u}, // nhw -> Latn
-    {0xA50D0000u, 41u}, // nij -> Latn
-    {0xD10D0000u, 41u}, // niu -> Latn
-    {0xB92D0000u, 41u}, // njo -> Latn
-    {0x6E6C0000u, 41u}, // nl -> Latn
-    {0x998D0000u, 41u}, // nmg -> Latn
-    {0x6E6E0000u, 41u}, // nn -> Latn
-    {0x9DAD0000u, 41u}, // nnh -> Latn
-    {0x6E6F0000u, 41u}, // no -> Latn
-    {0x8DCD0000u, 39u}, // nod -> Lana
+    {0xDC8D0000u, 40u}, // nex -> Latn
+    {0xC4AD0000u, 40u}, // nfr -> Latn
+    {0x6E670000u, 40u}, // ng -> Latn
+    {0x80CD0000u, 40u}, // nga -> Latn
+    {0x84CD0000u, 40u}, // ngb -> Latn
+    {0xACCD0000u, 40u}, // ngl -> Latn
+    {0x84ED0000u, 40u}, // nhb -> Latn
+    {0x90ED0000u, 40u}, // nhe -> Latn
+    {0xD8ED0000u, 40u}, // nhw -> Latn
+    {0x950D0000u, 40u}, // nif -> Latn
+    {0xA10D0000u, 40u}, // nii -> Latn
+    {0xA50D0000u, 40u}, // nij -> Latn
+    {0xB50D0000u, 40u}, // nin -> Latn
+    {0xD10D0000u, 40u}, // niu -> Latn
+    {0xE10D0000u, 40u}, // niy -> Latn
+    {0xE50D0000u, 40u}, // niz -> Latn
+    {0xB92D0000u, 40u}, // njo -> Latn
+    {0x994D0000u, 40u}, // nkg -> Latn
+    {0xB94D0000u, 40u}, // nko -> Latn
+    {0x6E6C0000u, 40u}, // nl -> Latn
+    {0x998D0000u, 40u}, // nmg -> Latn
+    {0xE58D0000u, 40u}, // nmz -> Latn
+    {0x6E6E0000u, 40u}, // nn -> Latn
+    {0x95AD0000u, 40u}, // nnf -> Latn
+    {0x9DAD0000u, 40u}, // nnh -> Latn
+    {0xA9AD0000u, 40u}, // nnk -> Latn
+    {0xB1AD0000u, 40u}, // nnm -> Latn
+    {0x6E6F0000u, 40u}, // no -> Latn
+    {0x8DCD0000u, 38u}, // nod -> Lana
     {0x91CD0000u, 16u}, // noe -> Deva
     {0xB5CD0000u, 64u}, // non -> Runr
-    {0xBA0D0000u, 55u}, // nqo -> Nkoo
-    {0x6E720000u, 41u}, // nr -> Latn
+    {0xBDCD0000u, 40u}, // nop -> Latn
+    {0xD1CD0000u, 40u}, // nou -> Latn
+    {0xBA0D0000u, 54u}, // nqo -> Nkoo
+    {0x6E720000u, 40u}, // nr -> Latn
+    {0x862D0000u, 40u}, // nrb -> Latn
     {0xAA4D0000u,  9u}, // nsk -> Cans
-    {0xBA4D0000u, 41u}, // nso -> Latn
-    {0xCA8D0000u, 41u}, // nus -> Latn
-    {0x6E760000u, 41u}, // nv -> Latn
-    {0xC2ED0000u, 41u}, // nxq -> Latn
-    {0x6E790000u, 41u}, // ny -> Latn
-    {0xB30D0000u, 41u}, // nym -> Latn
-    {0xB70D0000u, 41u}, // nyn -> Latn
-    {0xA32D0000u, 41u}, // nzi -> Latn
-    {0x6F630000u, 41u}, // oc -> Latn
-    {0x6F6D0000u, 41u}, // om -> Latn
-    {0x6F720000u, 58u}, // or -> Orya
+    {0xB64D0000u, 40u}, // nsn -> Latn
+    {0xBA4D0000u, 40u}, // nso -> Latn
+    {0xCA4D0000u, 40u}, // nss -> Latn
+    {0xB26D0000u, 40u}, // ntm -> Latn
+    {0xC66D0000u, 40u}, // ntr -> Latn
+    {0xA28D0000u, 40u}, // nui -> Latn
+    {0xBE8D0000u, 40u}, // nup -> Latn
+    {0xCA8D0000u, 40u}, // nus -> Latn
+    {0xD68D0000u, 40u}, // nuv -> Latn
+    {0xDE8D0000u, 40u}, // nux -> Latn
+    {0x6E760000u, 40u}, // nv -> Latn
+    {0x86CD0000u, 40u}, // nwb -> Latn
+    {0xC2ED0000u, 40u}, // nxq -> Latn
+    {0xC6ED0000u, 40u}, // nxr -> Latn
+    {0x6E790000u, 40u}, // ny -> Latn
+    {0xB30D0000u, 40u}, // nym -> Latn
+    {0xB70D0000u, 40u}, // nyn -> Latn
+    {0xA32D0000u, 40u}, // nzi -> Latn
+    {0x6F630000u, 40u}, // oc -> Latn
+    {0x88CE0000u, 40u}, // ogc -> Latn
+    {0xC54E0000u, 40u}, // okr -> Latn
+    {0xD54E0000u, 40u}, // okv -> Latn
+    {0x6F6D0000u, 40u}, // om -> Latn
+    {0x99AE0000u, 40u}, // ong -> Latn
+    {0xB5AE0000u, 40u}, // onn -> Latn
+    {0xC9AE0000u, 40u}, // ons -> Latn
+    {0xB1EE0000u, 40u}, // opm -> Latn
+    {0x6F720000u, 57u}, // or -> Orya
+    {0xBA2E0000u, 40u}, // oro -> Latn
+    {0xD22E0000u,  1u}, // oru -> Arab
     {0x6F730000u, 15u}, // os -> Cyrl
-    {0xAA6E0000u, 57u}, // otk -> Orkh
+    {0x824E0000u, 58u}, // osa -> Osge
+    {0x826E0000u,  1u}, // ota -> Arab
+    {0xAA6E0000u, 56u}, // otk -> Orkh
+    {0xB32E0000u, 40u}, // ozm -> Latn
     {0x70610000u, 23u}, // pa -> Guru
     {0x7061504Bu,  1u}, // pa-PK -> Arab
-    {0x980F0000u, 41u}, // pag -> Latn
+    {0x980F0000u, 40u}, // pag -> Latn
     {0xAC0F0000u, 60u}, // pal -> Phli
-    {0xB00F0000u, 41u}, // pam -> Latn
-    {0xBC0F0000u, 41u}, // pap -> Latn
-    {0xD00F0000u, 41u}, // pau -> Latn
-    {0x8C4F0000u, 41u}, // pcd -> Latn
-    {0xB04F0000u, 41u}, // pcm -> Latn
-    {0x886F0000u, 41u}, // pdc -> Latn
-    {0xCC6F0000u, 41u}, // pdt -> Latn
-    {0xB88F0000u, 83u}, // peo -> Xpeo
-    {0xACAF0000u, 41u}, // pfl -> Latn
+    {0xB00F0000u, 40u}, // pam -> Latn
+    {0xBC0F0000u, 40u}, // pap -> Latn
+    {0xD00F0000u, 40u}, // pau -> Latn
+    {0xA02F0000u, 40u}, // pbi -> Latn
+    {0x8C4F0000u, 40u}, // pcd -> Latn
+    {0xB04F0000u, 40u}, // pcm -> Latn
+    {0x886F0000u, 40u}, // pdc -> Latn
+    {0xCC6F0000u, 40u}, // pdt -> Latn
+    {0x8C8F0000u, 40u}, // ped -> Latn
+    {0xB88F0000u, 84u}, // peo -> Xpeo
+    {0xDC8F0000u, 40u}, // pex -> Latn
+    {0xACAF0000u, 40u}, // pfl -> Latn
+    {0xACEF0000u,  1u}, // phl -> Arab
     {0xB4EF0000u, 61u}, // phn -> Phnx
+    {0xAD0F0000u, 40u}, // pil -> Latn
+    {0xBD0F0000u, 40u}, // pip -> Latn
     {0x814F0000u,  8u}, // pka -> Brah
-    {0xB94F0000u, 41u}, // pko -> Latn
-    {0x706C0000u, 41u}, // pl -> Latn
-    {0xC98F0000u, 41u}, // pms -> Latn
+    {0xB94F0000u, 40u}, // pko -> Latn
+    {0x706C0000u, 40u}, // pl -> Latn
+    {0x816F0000u, 40u}, // pla -> Latn
+    {0xC98F0000u, 40u}, // pms -> Latn
+    {0x99AF0000u, 40u}, // png -> Latn
+    {0xB5AF0000u, 40u}, // pnn -> Latn
     {0xCDAF0000u, 21u}, // pnt -> Grek
-    {0xB5CF0000u, 41u}, // pon -> Latn
+    {0xB5CF0000u, 40u}, // pon -> Latn
+    {0xB9EF0000u, 40u}, // ppo -> Latn
     {0x822F0000u, 34u}, // pra -> Khar
     {0x8E2F0000u,  1u}, // prd -> Arab
-    {0x9A2F0000u, 41u}, // prg -> Latn
+    {0x9A2F0000u, 40u}, // prg -> Latn
     {0x70730000u,  1u}, // ps -> Arab
-    {0x70740000u, 41u}, // pt -> Latn
-    {0xD28F0000u, 41u}, // puu -> Latn
-    {0x71750000u, 41u}, // qu -> Latn
-    {0x8A900000u, 41u}, // quc -> Latn
-    {0x9A900000u, 41u}, // qug -> Latn
+    {0xCA4F0000u, 40u}, // pss -> Latn
+    {0x70740000u, 40u}, // pt -> Latn
+    {0xBE6F0000u, 40u}, // ptp -> Latn
+    {0xD28F0000u, 40u}, // puu -> Latn
+    {0x82CF0000u, 40u}, // pwa -> Latn
+    {0x71750000u, 40u}, // qu -> Latn
+    {0x8A900000u, 40u}, // quc -> Latn
+    {0x9A900000u, 40u}, // qug -> Latn
+    {0xA0110000u, 40u}, // rai -> Latn
     {0xA4110000u, 16u}, // raj -> Deva
-    {0x94510000u, 41u}, // rcf -> Latn
-    {0xA4910000u, 41u}, // rej -> Latn
-    {0xB4D10000u, 41u}, // rgn -> Latn
-    {0x81110000u, 41u}, // ria -> Latn
-    {0x95110000u, 77u}, // rif -> Tfng
-    {0x95114E4Cu, 41u}, // rif-NL -> Latn
+    {0xB8110000u, 40u}, // rao -> Latn
+    {0x94510000u, 40u}, // rcf -> Latn
+    {0xA4910000u, 40u}, // rej -> Latn
+    {0xAC910000u, 40u}, // rel -> Latn
+    {0xC8910000u, 40u}, // res -> Latn
+    {0xB4D10000u, 40u}, // rgn -> Latn
+    {0x98F10000u,  1u}, // rhg -> Arab
+    {0x81110000u, 40u}, // ria -> Latn
+    {0x95110000u, 78u}, // rif -> Tfng
+    {0x95114E4Cu, 40u}, // rif-NL -> Latn
     {0xC9310000u, 16u}, // rjs -> Deva
     {0xCD510000u,  7u}, // rkt -> Beng
-    {0x726D0000u, 41u}, // rm -> Latn
-    {0x95910000u, 41u}, // rmf -> Latn
-    {0xB9910000u, 41u}, // rmo -> Latn
+    {0x726D0000u, 40u}, // rm -> Latn
+    {0x95910000u, 40u}, // rmf -> Latn
+    {0xB9910000u, 40u}, // rmo -> Latn
     {0xCD910000u,  1u}, // rmt -> Arab
-    {0xD1910000u, 41u}, // rmu -> Latn
-    {0x726E0000u, 41u}, // rn -> Latn
-    {0x99B10000u, 41u}, // rng -> Latn
-    {0x726F0000u, 41u}, // ro -> Latn
-    {0x85D10000u, 41u}, // rob -> Latn
-    {0x95D10000u, 41u}, // rof -> Latn
-    {0xB2710000u, 41u}, // rtm -> Latn
+    {0xD1910000u, 40u}, // rmu -> Latn
+    {0x726E0000u, 40u}, // rn -> Latn
+    {0x81B10000u, 40u}, // rna -> Latn
+    {0x99B10000u, 40u}, // rng -> Latn
+    {0x726F0000u, 40u}, // ro -> Latn
+    {0x85D10000u, 40u}, // rob -> Latn
+    {0x95D10000u, 40u}, // rof -> Latn
+    {0xB9D10000u, 40u}, // roo -> Latn
+    {0xBA310000u, 40u}, // rro -> Latn
+    {0xB2710000u, 40u}, // rtm -> Latn
     {0x72750000u, 15u}, // ru -> Cyrl
     {0x92910000u, 15u}, // rue -> Cyrl
-    {0x9A910000u, 41u}, // rug -> Latn
-    {0x72770000u, 41u}, // rw -> Latn
-    {0xAAD10000u, 41u}, // rwk -> Latn
+    {0x9A910000u, 40u}, // rug -> Latn
+    {0x72770000u, 40u}, // rw -> Latn
+    {0xAAD10000u, 40u}, // rwk -> Latn
+    {0xBAD10000u, 40u}, // rwo -> Latn
     {0xD3110000u, 33u}, // ryu -> Kana
     {0x73610000u, 16u}, // sa -> Deva
-    {0x94120000u, 41u}, // saf -> Latn
+    {0x94120000u, 40u}, // saf -> Latn
     {0x9C120000u, 15u}, // sah -> Cyrl
-    {0xC0120000u, 41u}, // saq -> Latn
-    {0xC8120000u, 41u}, // sas -> Latn
-    {0xCC120000u, 41u}, // sat -> Latn
+    {0xC0120000u, 40u}, // saq -> Latn
+    {0xC8120000u, 40u}, // sas -> Latn
+    {0xCC120000u, 40u}, // sat -> Latn
     {0xE4120000u, 67u}, // saz -> Saur
-    {0xBC320000u, 41u}, // sbp -> Latn
-    {0x73630000u, 41u}, // sc -> Latn
+    {0x80320000u, 40u}, // sba -> Latn
+    {0x90320000u, 40u}, // sbe -> Latn
+    {0xBC320000u, 40u}, // sbp -> Latn
+    {0x73630000u, 40u}, // sc -> Latn
     {0xA8520000u, 16u}, // sck -> Deva
-    {0xB4520000u, 41u}, // scn -> Latn
-    {0xB8520000u, 41u}, // sco -> Latn
-    {0xC8520000u, 41u}, // scs -> Latn
+    {0xAC520000u,  1u}, // scl -> Arab
+    {0xB4520000u, 40u}, // scn -> Latn
+    {0xB8520000u, 40u}, // sco -> Latn
+    {0xC8520000u, 40u}, // scs -> Latn
     {0x73640000u,  1u}, // sd -> Arab
-    {0x88720000u, 41u}, // sdc -> Latn
+    {0x88720000u, 40u}, // sdc -> Latn
     {0x9C720000u,  1u}, // sdh -> Arab
-    {0x73650000u, 41u}, // se -> Latn
-    {0x94920000u, 41u}, // sef -> Latn
-    {0x9C920000u, 41u}, // seh -> Latn
-    {0xA0920000u, 41u}, // sei -> Latn
-    {0xC8920000u, 41u}, // ses -> Latn
-    {0x73670000u, 41u}, // sg -> Latn
-    {0x80D20000u, 56u}, // sga -> Ogam
-    {0xC8D20000u, 41u}, // sgs -> Latn
-    {0x73680000u, 41u}, // sh -> Latn
-    {0xA0F20000u, 77u}, // shi -> Tfng
-    {0xB4F20000u, 53u}, // shn -> Mymr
+    {0x73650000u, 40u}, // se -> Latn
+    {0x94920000u, 40u}, // sef -> Latn
+    {0x9C920000u, 40u}, // seh -> Latn
+    {0xA0920000u, 40u}, // sei -> Latn
+    {0xC8920000u, 40u}, // ses -> Latn
+    {0x73670000u, 40u}, // sg -> Latn
+    {0x80D20000u, 55u}, // sga -> Ogam
+    {0xC8D20000u, 40u}, // sgs -> Latn
+    {0xD8D20000u, 18u}, // sgw -> Ethi
+    {0xE4D20000u, 40u}, // sgz -> Latn
+    {0x73680000u, 40u}, // sh -> Latn
+    {0xA0F20000u, 78u}, // shi -> Tfng
+    {0xA8F20000u, 40u}, // shk -> Latn
+    {0xB4F20000u, 52u}, // shn -> Mymr
+    {0xD0F20000u,  1u}, // shu -> Arab
     {0x73690000u, 69u}, // si -> Sinh
-    {0x8D120000u, 41u}, // sid -> Latn
-    {0x736B0000u, 41u}, // sk -> Latn
+    {0x8D120000u, 40u}, // sid -> Latn
+    {0x99120000u, 40u}, // sig -> Latn
+    {0xAD120000u, 40u}, // sil -> Latn
+    {0xB1120000u, 40u}, // sim -> Latn
+    {0xC5320000u, 40u}, // sjr -> Latn
+    {0x736B0000u, 40u}, // sk -> Latn
+    {0x89520000u, 40u}, // skc -> Latn
     {0xC5520000u,  1u}, // skr -> Arab
-    {0x736C0000u, 41u}, // sl -> Latn
-    {0xA1720000u, 41u}, // sli -> Latn
-    {0xE1720000u, 41u}, // sly -> Latn
-    {0x736D0000u, 41u}, // sm -> Latn
-    {0x81920000u, 41u}, // sma -> Latn
-    {0xA5920000u, 41u}, // smj -> Latn
-    {0xB5920000u, 41u}, // smn -> Latn
+    {0xC9520000u, 40u}, // sks -> Latn
+    {0x736C0000u, 40u}, // sl -> Latn
+    {0x8D720000u, 40u}, // sld -> Latn
+    {0xA1720000u, 40u}, // sli -> Latn
+    {0xAD720000u, 40u}, // sll -> Latn
+    {0xE1720000u, 40u}, // sly -> Latn
+    {0x736D0000u, 40u}, // sm -> Latn
+    {0x81920000u, 40u}, // sma -> Latn
+    {0xA5920000u, 40u}, // smj -> Latn
+    {0xB5920000u, 40u}, // smn -> Latn
     {0xBD920000u, 65u}, // smp -> Samr
-    {0xC9920000u, 41u}, // sms -> Latn
-    {0x736E0000u, 41u}, // sn -> Latn
-    {0xA9B20000u, 41u}, // snk -> Latn
-    {0x736F0000u, 41u}, // so -> Latn
-    {0xD1D20000u, 79u}, // sou -> Thai
-    {0x73710000u, 41u}, // sq -> Latn
+    {0xC1920000u, 40u}, // smq -> Latn
+    {0xC9920000u, 40u}, // sms -> Latn
+    {0x736E0000u, 40u}, // sn -> Latn
+    {0x89B20000u, 40u}, // snc -> Latn
+    {0xA9B20000u, 40u}, // snk -> Latn
+    {0xBDB20000u, 40u}, // snp -> Latn
+    {0xDDB20000u, 40u}, // snx -> Latn
+    {0xE1B20000u, 40u}, // sny -> Latn
+    {0x736F0000u, 40u}, // so -> Latn
+    {0xA9D20000u, 40u}, // sok -> Latn
+    {0xC1D20000u, 40u}, // soq -> Latn
+    {0xD1D20000u, 80u}, // sou -> Thai
+    {0xE1D20000u, 40u}, // soy -> Latn
+    {0x8DF20000u, 40u}, // spd -> Latn
+    {0xADF20000u, 40u}, // spl -> Latn
+    {0xC9F20000u, 40u}, // sps -> Latn
+    {0x73710000u, 40u}, // sq -> Latn
     {0x73720000u, 15u}, // sr -> Cyrl
-    {0x73724D45u, 41u}, // sr-ME -> Latn
-    {0x7372524Fu, 41u}, // sr-RO -> Latn
-    {0x73725255u, 41u}, // sr-RU -> Latn
-    {0x73725452u, 41u}, // sr-TR -> Latn
+    {0x73724D45u, 40u}, // sr-ME -> Latn
+    {0x7372524Fu, 40u}, // sr-RO -> Latn
+    {0x73725255u, 40u}, // sr-RU -> Latn
+    {0x73725452u, 40u}, // sr-TR -> Latn
     {0x86320000u, 70u}, // srb -> Sora
-    {0xB6320000u, 41u}, // srn -> Latn
-    {0xC6320000u, 41u}, // srr -> Latn
+    {0xB6320000u, 40u}, // srn -> Latn
+    {0xC6320000u, 40u}, // srr -> Latn
     {0xDE320000u, 16u}, // srx -> Deva
-    {0x73730000u, 41u}, // ss -> Latn
-    {0xE2520000u, 41u}, // ssy -> Latn
-    {0x73740000u, 41u}, // st -> Latn
-    {0xC2720000u, 41u}, // stq -> Latn
-    {0x73750000u, 41u}, // su -> Latn
-    {0xAA920000u, 41u}, // suk -> Latn
-    {0xCA920000u, 41u}, // sus -> Latn
-    {0x73760000u, 41u}, // sv -> Latn
-    {0x73770000u, 41u}, // sw -> Latn
+    {0x73730000u, 40u}, // ss -> Latn
+    {0x8E520000u, 40u}, // ssd -> Latn
+    {0x9A520000u, 40u}, // ssg -> Latn
+    {0xE2520000u, 40u}, // ssy -> Latn
+    {0x73740000u, 40u}, // st -> Latn
+    {0xAA720000u, 40u}, // stk -> Latn
+    {0xC2720000u, 40u}, // stq -> Latn
+    {0x73750000u, 40u}, // su -> Latn
+    {0x82920000u, 40u}, // sua -> Latn
+    {0x92920000u, 40u}, // sue -> Latn
+    {0xAA920000u, 40u}, // suk -> Latn
+    {0xC6920000u, 40u}, // sur -> Latn
+    {0xCA920000u, 40u}, // sus -> Latn
+    {0x73760000u, 40u}, // sv -> Latn
+    {0x73770000u, 40u}, // sw -> Latn
     {0x86D20000u,  1u}, // swb -> Arab
-    {0x8AD20000u, 41u}, // swc -> Latn
-    {0x9AD20000u, 41u}, // swg -> Latn
+    {0x8AD20000u, 40u}, // swc -> Latn
+    {0x9AD20000u, 40u}, // swg -> Latn
+    {0xBED20000u, 40u}, // swp -> Latn
     {0xD6D20000u, 16u}, // swv -> Deva
-    {0xB6F20000u, 41u}, // sxn -> Latn
+    {0xB6F20000u, 40u}, // sxn -> Latn
+    {0xDAF20000u, 40u}, // sxw -> Latn
     {0xAF120000u,  7u}, // syl -> Beng
     {0xC7120000u, 71u}, // syr -> Syrc
-    {0xAF320000u, 41u}, // szl -> Latn
+    {0xAF320000u, 40u}, // szl -> Latn
     {0x74610000u, 74u}, // ta -> Taml
     {0xA4130000u, 16u}, // taj -> Deva
-    {0xD8330000u, 41u}, // tbw -> Latn
+    {0xAC130000u, 40u}, // tal -> Latn
+    {0xB4130000u, 40u}, // tan -> Latn
+    {0xC0130000u, 40u}, // taq -> Latn
+    {0x88330000u, 40u}, // tbc -> Latn
+    {0x8C330000u, 40u}, // tbd -> Latn
+    {0x94330000u, 40u}, // tbf -> Latn
+    {0x98330000u, 40u}, // tbg -> Latn
+    {0xB8330000u, 40u}, // tbo -> Latn
+    {0xD8330000u, 40u}, // tbw -> Latn
+    {0xE4330000u, 40u}, // tbz -> Latn
+    {0xA0530000u, 40u}, // tci -> Latn
     {0xE0530000u, 36u}, // tcy -> Knda
     {0x8C730000u, 72u}, // tdd -> Tale
     {0x98730000u, 16u}, // tdg -> Deva
     {0x9C730000u, 16u}, // tdh -> Deva
-    {0x74650000u, 76u}, // te -> Telu
-    {0xB0930000u, 41u}, // tem -> Latn
-    {0xB8930000u, 41u}, // teo -> Latn
-    {0xCC930000u, 41u}, // tet -> Latn
+    {0x74650000u, 77u}, // te -> Telu
+    {0x8C930000u, 40u}, // ted -> Latn
+    {0xB0930000u, 40u}, // tem -> Latn
+    {0xB8930000u, 40u}, // teo -> Latn
+    {0xCC930000u, 40u}, // tet -> Latn
+    {0xA0B30000u, 40u}, // tfi -> Latn
     {0x74670000u, 15u}, // tg -> Cyrl
     {0x7467504Bu,  1u}, // tg-PK -> Arab
-    {0x74680000u, 79u}, // th -> Thai
+    {0x88D30000u, 40u}, // tgc -> Latn
+    {0xB8D30000u, 40u}, // tgo -> Latn
+    {0xD0D30000u, 40u}, // tgu -> Latn
+    {0x74680000u, 80u}, // th -> Thai
     {0xACF30000u, 16u}, // thl -> Deva
     {0xC0F30000u, 16u}, // thq -> Deva
     {0xC4F30000u, 16u}, // thr -> Deva
     {0x74690000u, 18u}, // ti -> Ethi
+    {0x95130000u, 40u}, // tif -> Latn
     {0x99130000u, 18u}, // tig -> Ethi
-    {0xD5130000u, 41u}, // tiv -> Latn
-    {0x746B0000u, 41u}, // tk -> Latn
-    {0xAD530000u, 41u}, // tkl -> Latn
-    {0xC5530000u, 41u}, // tkr -> Latn
+    {0xA9130000u, 40u}, // tik -> Latn
+    {0xB1130000u, 40u}, // tim -> Latn
+    {0xB9130000u, 40u}, // tio -> Latn
+    {0xD5130000u, 40u}, // tiv -> Latn
+    {0x746B0000u, 40u}, // tk -> Latn
+    {0xAD530000u, 40u}, // tkl -> Latn
+    {0xC5530000u, 40u}, // tkr -> Latn
     {0xCD530000u, 16u}, // tkt -> Deva
-    {0x746C0000u, 41u}, // tl -> Latn
-    {0xE1730000u, 41u}, // tly -> Latn
-    {0x9D930000u, 41u}, // tmh -> Latn
-    {0x746E0000u, 41u}, // tn -> Latn
-    {0x746F0000u, 41u}, // to -> Latn
-    {0x99D30000u, 41u}, // tog -> Latn
-    {0xA1F30000u, 41u}, // tpi -> Latn
-    {0x74720000u, 41u}, // tr -> Latn
-    {0xD2330000u, 41u}, // tru -> Latn
-    {0xD6330000u, 41u}, // trv -> Latn
-    {0x74730000u, 41u}, // ts -> Latn
+    {0x746C0000u, 40u}, // tl -> Latn
+    {0x95730000u, 40u}, // tlf -> Latn
+    {0xDD730000u, 40u}, // tlx -> Latn
+    {0xE1730000u, 40u}, // tly -> Latn
+    {0x9D930000u, 40u}, // tmh -> Latn
+    {0xE1930000u, 40u}, // tmy -> Latn
+    {0x746E0000u, 40u}, // tn -> Latn
+    {0x9DB30000u, 40u}, // tnh -> Latn
+    {0x746F0000u, 40u}, // to -> Latn
+    {0x95D30000u, 40u}, // tof -> Latn
+    {0x99D30000u, 40u}, // tog -> Latn
+    {0xC1D30000u, 40u}, // toq -> Latn
+    {0xA1F30000u, 40u}, // tpi -> Latn
+    {0xB1F30000u, 40u}, // tpm -> Latn
+    {0xE5F30000u, 40u}, // tpz -> Latn
+    {0xBA130000u, 40u}, // tqo -> Latn
+    {0x74720000u, 40u}, // tr -> Latn
+    {0xD2330000u, 40u}, // tru -> Latn
+    {0xD6330000u, 40u}, // trv -> Latn
+    {0xDA330000u,  1u}, // trw -> Arab
+    {0x74730000u, 40u}, // ts -> Latn
     {0x8E530000u, 21u}, // tsd -> Grek
     {0x96530000u, 16u}, // tsf -> Deva
-    {0x9A530000u, 41u}, // tsg -> Latn
-    {0xA6530000u, 80u}, // tsj -> Tibt
+    {0x9A530000u, 40u}, // tsg -> Latn
+    {0xA6530000u, 81u}, // tsj -> Tibt
+    {0xDA530000u, 40u}, // tsw -> Latn
     {0x74740000u, 15u}, // tt -> Cyrl
-    {0xA6730000u, 41u}, // ttj -> Latn
-    {0xCA730000u, 79u}, // tts -> Thai
-    {0xCE730000u, 41u}, // ttt -> Latn
-    {0xB2930000u, 41u}, // tum -> Latn
-    {0xAEB30000u, 41u}, // tvl -> Latn
-    {0xC2D30000u, 41u}, // twq -> Latn
-    {0x74790000u, 41u}, // ty -> Latn
+    {0x8E730000u, 40u}, // ttd -> Latn
+    {0x92730000u, 40u}, // tte -> Latn
+    {0xA6730000u, 40u}, // ttj -> Latn
+    {0xC6730000u, 40u}, // ttr -> Latn
+    {0xCA730000u, 80u}, // tts -> Thai
+    {0xCE730000u, 40u}, // ttt -> Latn
+    {0x9E930000u, 40u}, // tuh -> Latn
+    {0xAE930000u, 40u}, // tul -> Latn
+    {0xB2930000u, 40u}, // tum -> Latn
+    {0xC2930000u, 40u}, // tuq -> Latn
+    {0x8EB30000u, 40u}, // tvd -> Latn
+    {0xAEB30000u, 40u}, // tvl -> Latn
+    {0xD2B30000u, 40u}, // tvu -> Latn
+    {0x9ED30000u, 40u}, // twh -> Latn
+    {0xC2D30000u, 40u}, // twq -> Latn
+    {0x9AF30000u, 75u}, // txg -> Tang
+    {0x74790000u, 40u}, // ty -> Latn
+    {0x83130000u, 40u}, // tya -> Latn
     {0xD7130000u, 15u}, // tyv -> Cyrl
-    {0xB3330000u, 41u}, // tzm -> Latn
+    {0xB3330000u, 40u}, // tzm -> Latn
+    {0xD0340000u, 40u}, // ubu -> Latn
     {0xB0740000u, 15u}, // udm -> Cyrl
     {0x75670000u,  1u}, // ug -> Arab
     {0x75674B5Au, 15u}, // ug-KZ -> Cyrl
     {0x75674D4Eu, 15u}, // ug-MN -> Cyrl
-    {0x80D40000u, 81u}, // uga -> Ugar
+    {0x80D40000u, 82u}, // uga -> Ugar
     {0x756B0000u, 15u}, // uk -> Cyrl
-    {0xA1740000u, 41u}, // uli -> Latn
-    {0x85940000u, 41u}, // umb -> Latn
+    {0xA1740000u, 40u}, // uli -> Latn
+    {0x85940000u, 40u}, // umb -> Latn
     {0xC5B40000u,  7u}, // unr -> Beng
     {0xC5B44E50u, 16u}, // unr-NP -> Deva
     {0xDDB40000u,  7u}, // unx -> Beng
     {0x75720000u,  1u}, // ur -> Arab
-    {0x757A0000u, 41u}, // uz -> Latn
+    {0xA2340000u, 40u}, // uri -> Latn
+    {0xCE340000u, 40u}, // urt -> Latn
+    {0xDA340000u, 40u}, // urw -> Latn
+    {0x82540000u, 40u}, // usa -> Latn
+    {0xC6740000u, 40u}, // utr -> Latn
+    {0x9EB40000u, 40u}, // uvh -> Latn
+    {0xAEB40000u, 40u}, // uvl -> Latn
+    {0x757A0000u, 40u}, // uz -> Latn
     {0x757A4146u,  1u}, // uz-AF -> Arab
     {0x757A434Eu, 15u}, // uz-CN -> Cyrl
-    {0xA0150000u, 82u}, // vai -> Vaii
-    {0x76650000u, 41u}, // ve -> Latn
-    {0x88950000u, 41u}, // vec -> Latn
-    {0xBC950000u, 41u}, // vep -> Latn
-    {0x76690000u, 41u}, // vi -> Latn
-    {0x89150000u, 41u}, // vic -> Latn
-    {0xC9750000u, 41u}, // vls -> Latn
-    {0x95950000u, 41u}, // vmf -> Latn
-    {0xD9950000u, 41u}, // vmw -> Latn
-    {0x766F0000u, 41u}, // vo -> Latn
-    {0xCDD50000u, 41u}, // vot -> Latn
-    {0xBA350000u, 41u}, // vro -> Latn
-    {0xB6950000u, 41u}, // vun -> Latn
-    {0x77610000u, 41u}, // wa -> Latn
-    {0x90160000u, 41u}, // wae -> Latn
+    {0x98150000u, 40u}, // vag -> Latn
+    {0xA0150000u, 83u}, // vai -> Vaii
+    {0xB4150000u, 40u}, // van -> Latn
+    {0x76650000u, 40u}, // ve -> Latn
+    {0x88950000u, 40u}, // vec -> Latn
+    {0xBC950000u, 40u}, // vep -> Latn
+    {0x76690000u, 40u}, // vi -> Latn
+    {0x89150000u, 40u}, // vic -> Latn
+    {0xD5150000u, 40u}, // viv -> Latn
+    {0xC9750000u, 40u}, // vls -> Latn
+    {0x95950000u, 40u}, // vmf -> Latn
+    {0xD9950000u, 40u}, // vmw -> Latn
+    {0x766F0000u, 40u}, // vo -> Latn
+    {0xCDD50000u, 40u}, // vot -> Latn
+    {0xBA350000u, 40u}, // vro -> Latn
+    {0xB6950000u, 40u}, // vun -> Latn
+    {0xCE950000u, 40u}, // vut -> Latn
+    {0x77610000u, 40u}, // wa -> Latn
+    {0x90160000u, 40u}, // wae -> Latn
+    {0xA4160000u, 40u}, // waj -> Latn
     {0xAC160000u, 18u}, // wal -> Ethi
-    {0xC4160000u, 41u}, // war -> Latn
-    {0xBC360000u, 41u}, // wbp -> Latn
-    {0xC0360000u, 76u}, // wbq -> Telu
+    {0xB4160000u, 40u}, // wan -> Latn
+    {0xC4160000u, 40u}, // war -> Latn
+    {0xBC360000u, 40u}, // wbp -> Latn
+    {0xC0360000u, 77u}, // wbq -> Telu
     {0xC4360000u, 16u}, // wbr -> Deva
-    {0xC9760000u, 41u}, // wls -> Latn
+    {0xA0560000u, 40u}, // wci -> Latn
+    {0xC4960000u, 40u}, // wer -> Latn
+    {0xA0D60000u, 40u}, // wgi -> Latn
+    {0x98F60000u, 40u}, // whg -> Latn
+    {0x85160000u, 40u}, // wib -> Latn
+    {0xD1160000u, 40u}, // wiu -> Latn
+    {0xD5160000u, 40u}, // wiv -> Latn
+    {0x81360000u, 40u}, // wja -> Latn
+    {0xA1360000u, 40u}, // wji -> Latn
+    {0xC9760000u, 40u}, // wls -> Latn
+    {0xB9960000u, 40u}, // wmo -> Latn
+    {0x89B60000u, 40u}, // wnc -> Latn
     {0xA1B60000u,  1u}, // wni -> Arab
-    {0x776F0000u, 41u}, // wo -> Latn
+    {0xD1B60000u, 40u}, // wnu -> Latn
+    {0x776F0000u, 40u}, // wo -> Latn
+    {0x85D60000u, 40u}, // wob -> Latn
+    {0xC9D60000u, 40u}, // wos -> Latn
+    {0xCA360000u, 40u}, // wrs -> Latn
+    {0xAA560000u, 40u}, // wsk -> Latn
     {0xB2760000u, 16u}, // wtm -> Deva
     {0xD2960000u, 24u}, // wuu -> Hans
-    {0xD4170000u, 41u}, // xav -> Latn
+    {0xD6960000u, 40u}, // wuv -> Latn
+    {0x82D60000u, 40u}, // wwa -> Latn
+    {0xD4170000u, 40u}, // xav -> Latn
+    {0xA0370000u, 40u}, // xbi -> Latn
     {0xC4570000u, 10u}, // xcr -> Cari
-    {0x78680000u, 41u}, // xh -> Latn
-    {0x89770000u, 45u}, // xlc -> Lyci
-    {0x8D770000u, 46u}, // xld -> Lydi
+    {0xC8970000u, 40u}, // xes -> Latn
+    {0x78680000u, 40u}, // xh -> Latn
+    {0x81770000u, 40u}, // xla -> Latn
+    {0x89770000u, 44u}, // xlc -> Lyci
+    {0x8D770000u, 45u}, // xld -> Lydi
     {0x95970000u, 19u}, // xmf -> Geor
-    {0xB5970000u, 48u}, // xmn -> Mani
-    {0xC5970000u, 49u}, // xmr -> Merc
-    {0x81B70000u, 54u}, // xna -> Narb
+    {0xB5970000u, 47u}, // xmn -> Mani
+    {0xC5970000u, 48u}, // xmr -> Merc
+    {0x81B70000u, 53u}, // xna -> Narb
     {0xC5B70000u, 16u}, // xnr -> Deva
-    {0x99D70000u, 41u}, // xog -> Latn
+    {0x99D70000u, 40u}, // xog -> Latn
+    {0xB5D70000u, 40u}, // xon -> Latn
     {0xC5F70000u, 63u}, // xpr -> Prti
+    {0x86370000u, 40u}, // xrb -> Latn
     {0x82570000u, 66u}, // xsa -> Sarb
+    {0xA2570000u, 40u}, // xsi -> Latn
+    {0xB2570000u, 40u}, // xsm -> Latn
     {0xC6570000u, 16u}, // xsr -> Deva
-    {0xB8180000u, 41u}, // yao -> Latn
-    {0xBC180000u, 41u}, // yap -> Latn
-    {0xD4180000u, 41u}, // yav -> Latn
-    {0x84380000u, 41u}, // ybb -> Latn
+    {0x92D70000u, 40u}, // xwe -> Latn
+    {0xB0180000u, 40u}, // yam -> Latn
+    {0xB8180000u, 40u}, // yao -> Latn
+    {0xBC180000u, 40u}, // yap -> Latn
+    {0xC8180000u, 40u}, // yas -> Latn
+    {0xCC180000u, 40u}, // yat -> Latn
+    {0xD4180000u, 40u}, // yav -> Latn
+    {0xE0180000u, 40u}, // yay -> Latn
+    {0xE4180000u, 40u}, // yaz -> Latn
+    {0x80380000u, 40u}, // yba -> Latn
+    {0x84380000u, 40u}, // ybb -> Latn
+    {0xE0380000u, 40u}, // yby -> Latn
+    {0xC4980000u, 40u}, // yer -> Latn
+    {0xC4D80000u, 40u}, // ygr -> Latn
+    {0xD8D80000u, 40u}, // ygw -> Latn
     {0x79690000u, 27u}, // yi -> Hebr
-    {0x796F0000u, 41u}, // yo -> Latn
-    {0xAE380000u, 41u}, // yrl -> Latn
-    {0x82980000u, 41u}, // yua -> Latn
-    {0x7A610000u, 41u}, // za -> Latn
-    {0x98190000u, 41u}, // zag -> Latn
+    {0xB9580000u, 40u}, // yko -> Latn
+    {0x91780000u, 40u}, // yle -> Latn
+    {0x99780000u, 40u}, // ylg -> Latn
+    {0xAD780000u, 40u}, // yll -> Latn
+    {0xAD980000u, 40u}, // yml -> Latn
+    {0x796F0000u, 40u}, // yo -> Latn
+    {0xB5D80000u, 40u}, // yon -> Latn
+    {0x86380000u, 40u}, // yrb -> Latn
+    {0x92380000u, 40u}, // yre -> Latn
+    {0xAE380000u, 40u}, // yrl -> Latn
+    {0xCA580000u, 40u}, // yss -> Latn
+    {0x82980000u, 40u}, // yua -> Latn
+    {0x92980000u, 25u}, // yue -> Hant
+    {0x9298434Eu, 24u}, // yue-CN -> Hans
+    {0xA6980000u, 40u}, // yuj -> Latn
+    {0xCE980000u, 40u}, // yut -> Latn
+    {0xDA980000u, 40u}, // yuw -> Latn
+    {0x7A610000u, 40u}, // za -> Latn
+    {0x98190000u, 40u}, // zag -> Latn
     {0xA4790000u,  1u}, // zdj -> Arab
-    {0x80990000u, 41u}, // zea -> Latn
-    {0x9CD90000u, 77u}, // zgh -> Tfng
+    {0x80990000u, 40u}, // zea -> Latn
+    {0x9CD90000u, 78u}, // zgh -> Tfng
     {0x7A680000u, 24u}, // zh -> Hans
     {0x7A684155u, 25u}, // zh-AU -> Hant
     {0x7A68424Eu, 25u}, // zh-BN -> Hant
@@ -829,9 +1437,12 @@
     {0x7A685457u, 25u}, // zh-TW -> Hant
     {0x7A685553u, 25u}, // zh-US -> Hant
     {0x7A68564Eu, 25u}, // zh-VN -> Hant
-    {0xA1990000u, 41u}, // zmi -> Latn
-    {0x7A750000u, 41u}, // zu -> Latn
-    {0x83390000u, 41u}, // zza -> Latn
+    {0x81190000u, 40u}, // zia -> Latn
+    {0xB1790000u, 40u}, // zlm -> Latn
+    {0xA1990000u, 40u}, // zmi -> Latn
+    {0x91B90000u, 40u}, // zne -> Latn
+    {0x7A750000u, 40u}, // zu -> Latn
+    {0x83390000u, 40u}, // zza -> Latn
 });
 
 std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -854,6 +1465,7 @@
     0x616D455445746869llu, // am_Ethi_ET
     0xB9804E474C61746Ellu, // amo_Latn_NG
     0xE5C049444C61746Ellu, // aoz_Latn_ID
+    0x8DE0544741726162llu, // apd_Arab_TG
     0x6172454741726162llu, // ar_Arab_EG
     0x8A20495241726D69llu, // arc_Armi_IR
     0x8A204A4F4E626174llu, // arc_Nbat_JO
@@ -896,7 +1508,6 @@
     0x88C1494E44657661llu, // bgc_Deva_IN
     0xB4C1504B41726162llu, // bgn_Arab_PK
     0xDCC154524772656Bllu, // bgx_Grek_TR
-    0x6268494E4B746869llu, // bh_Kthi_IN
     0x84E1494E44657661llu, // bhb_Deva_IN
     0xA0E1494E44657661llu, // bhi_Deva_IN
     0xA8E150484C61746Ellu, // bhk_Latn_PH
@@ -980,6 +1591,7 @@
     0x864344454C61746Ellu, // dsb_Latn_DE
     0xB2634D4C4C61746Ellu, // dtm_Latn_ML
     0xBE634D594C61746Ellu, // dtp_Latn_MY
+    0xE2634E5044657661llu, // dty_Deva_NP
     0x8283434D4C61746Ellu, // dua_Latn_CM
     0x64764D5654686161llu, // dv_Thaa_MV
     0xBB03534E4C61746Ellu, // dyo_Latn_SN
@@ -1006,6 +1618,7 @@
     0xCEE445534C61746Ellu, // ext_Latn_ES
     0x6661495241726162llu, // fa_Arab_IR
     0xB40547514C61746Ellu, // fan_Latn_GQ
+    0x6666474E41646C6Dllu, // ff_Adlm_GN
     0x6666534E4C61746Ellu, // ff_Latn_SN
     0xB0A54D4C4C61746Ellu, // ffm_Latn_ML
     0x666946494C61746Ellu, // fi_Latn_FI
@@ -1020,7 +1633,9 @@
     0xBE2546524C61746Ellu, // frp_Latn_FR
     0xC62544454C61746Ellu, // frr_Latn_DE
     0xCA2544454C61746Ellu, // frs_Latn_DE
+    0x8685434D41726162llu, // fub_Arab_CM
     0x8E8557464C61746Ellu, // fud_Latn_WF
+    0x9685474E4C61746Ellu, // fuf_Latn_GN
     0xC2854E454C61746Ellu, // fuq_Latn_NE
     0xC68549544C61746Ellu, // fur_Latn_IT
     0xD6854E474C61746Ellu, // fuv_Latn_NG
@@ -1104,7 +1719,6 @@
     0x6A614A504A70616Ellu, // ja_Jpan_JP
     0xB0094A4D4C61746Ellu, // jam_Latn_JM
     0xB8C9434D4C61746Ellu, // jgo_Latn_CM
-    0x6A69554148656272llu, // ji_Hebr_UA
     0x8989545A4C61746Ellu, // jmc_Latn_TZ
     0xAD894E5044657661llu, // jml_Deva_NP
     0xCE89444B4C61746Ellu, // jut_Latn_DK
@@ -1118,9 +1732,11 @@
     0xB00A4B454C61746Ellu, // kam_Latn_KE
     0xB80A4D4C4C61746Ellu, // kao_Latn_ML
     0x8C2A52554379726Cllu, // kbd_Cyrl_RU
+    0xE02A4E4541726162llu, // kby_Arab_NE
     0x984A4E474C61746Ellu, // kcg_Latn_NG
     0xA84A5A574C61746Ellu, // kck_Latn_ZW
     0x906A545A4C61746Ellu, // kde_Latn_TZ
+    0x9C6A544741726162llu, // kdh_Arab_TG
     0xCC6A544854686169llu, // kdt_Thai_TH
     0x808A43564C61746Ellu, // kea_Latn_CV
     0xB48A434D4C61746Ellu, // ken_Latn_CM
@@ -1251,7 +1867,7 @@
     0x6D72494E44657661llu, // mr_Deva_IN
     0x8E2C4E5044657661llu, // mrd_Deva_NP
     0xA62C52554379726Cllu, // mrj_Cyrl_RU
-    0xD22C42444D726F6Fllu, // mru_Mroo_BD
+    0xBA2C42444D726F6Fllu, // mro_Mroo_BD
     0x6D734D594C61746Ellu, // ms_Latn_MY
     0x6D744D544C61746Ellu, // mt_Latn_MT
     0xC66C494E44657661llu, // mtr_Deva_IN
@@ -1308,6 +1924,7 @@
     0x6F6D45544C61746Ellu, // om_Latn_ET
     0x6F72494E4F727961llu, // or_Orya_IN
     0x6F7347454379726Cllu, // os_Cyrl_GE
+    0x824E55534F736765llu, // osa_Osge_US
     0xAA6E4D4E4F726B68llu, // otk_Orkh_MN
     0x7061504B41726162llu, // pa_Arab_PK
     0x7061494E47757275llu, // pa_Guru_IN
@@ -1479,6 +2096,7 @@
     0xB2934D574C61746Ellu, // tum_Latn_MW
     0xAEB354564C61746Ellu, // tvl_Latn_TV
     0xC2D34E454C61746Ellu, // twq_Latn_NE
+    0x9AF3434E54616E67llu, // txg_Tang_CN
     0x747950464C61746Ellu, // ty_Latn_PF
     0xD71352554379726Cllu, // tyv_Cyrl_RU
     0xB3334D414C61746Ellu, // tzm_Latn_MA
@@ -1540,14 +2158,18 @@
     0x796F4E474C61746Ellu, // yo_Latn_NG
     0xAE3842524C61746Ellu, // yrl_Latn_BR
     0x82984D584C61746Ellu, // yua_Latn_MX
+    0x9298434E48616E73llu, // yue_Hans_CN
+    0x9298484B48616E74llu, // yue_Hant_HK
     0x7A61434E4C61746Ellu, // za_Latn_CN
     0x981953444C61746Ellu, // zag_Latn_SD
     0xA4794B4D41726162llu, // zdj_Arab_KM
     0x80994E4C4C61746Ellu, // zea_Latn_NL
     0x9CD94D4154666E67llu, // zgh_Tfng_MA
     0x7A685457426F706Fllu, // zh_Bopo_TW
+    0x7A68545748616E62llu, // zh_Hanb_TW
     0x7A68434E48616E73llu, // zh_Hans_CN
     0x7A68545748616E74llu, // zh_Hant_TW
+    0xB17954474C61746Ellu, // zlm_Latn_TG
     0xA1994D594C61746Ellu, // zmi_Latn_MY
     0x7A755A414C61746Ellu, // zu_Latn_ZA
     0x833954524C61746Ellu, // zza_Latn_TR
@@ -1662,6 +2284,7 @@
     {0x656E5A57u, 0x656E8400u}, // en-ZW -> en-001
     {0x65734152u, 0x6573A424u}, // es-AR -> es-419
     {0x6573424Fu, 0x6573A424u}, // es-BO -> es-419
+    {0x65734252u, 0x6573A424u}, // es-BR -> es-419
     {0x6573434Cu, 0x6573A424u}, // es-CL -> es-419
     {0x6573434Fu, 0x6573A424u}, // es-CO -> es-419
     {0x65734352u, 0x6573A424u}, // es-CR -> es-419
@@ -1681,8 +2304,11 @@
     {0x65735559u, 0x6573A424u}, // es-UY -> es-419
     {0x65735645u, 0x6573A424u}, // es-VE -> es-419
     {0x7074414Fu, 0x70745054u}, // pt-AO -> pt-PT
+    {0x70744348u, 0x70745054u}, // pt-CH -> pt-PT
     {0x70744356u, 0x70745054u}, // pt-CV -> pt-PT
+    {0x70744751u, 0x70745054u}, // pt-GQ -> pt-PT
     {0x70744757u, 0x70745054u}, // pt-GW -> pt-PT
+    {0x70744C55u, 0x70745054u}, // pt-LU -> pt-PT
     {0x70744D4Fu, 0x70745054u}, // pt-MO -> pt-PT
     {0x70744D5Au, 0x70745054u}, // pt-MZ -> pt-PT
     {0x70745354u, 0x70745054u}, // pt-ST -> pt-PT
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 7fbfffe..763a178 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -140,7 +140,7 @@
     patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t));
 }
 
-inline void Res_value::copyFrom_dtoh(const Res_value& src)
+void Res_value::copyFrom_dtoh(const Res_value& src)
 {
     size = dtohs(src.size);
     res0 = src.res0;
@@ -1907,6 +1907,8 @@
     if (diff != 0) return diff;
     diff = (int32_t)(screenLayout2 - o.screenLayout2);
     if (diff != 0) return diff;
+    diff = (int32_t)(colorMode - o.colorMode);
+    if (diff != 0) return diff;
     diff = (int32_t)(uiMode - o.uiMode);
     if (diff != 0) return diff;
     diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp);
@@ -1967,6 +1969,9 @@
     if (screenLayout2 != o.screenLayout2) {
         return screenLayout2 < o.screenLayout2 ? -1 : 1;
     }
+    if (colorMode != o.colorMode) {
+        return colorMode < o.colorMode ? -1 : 1;
+    }
     if (uiMode != o.uiMode) {
         return uiMode < o.uiMode ? -1 : 1;
     }
@@ -1992,6 +1997,8 @@
     if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) diffs |= CONFIG_LAYOUTDIR;
     if ((screenLayout & ~MASK_LAYOUTDIR) != (o.screenLayout & ~MASK_LAYOUTDIR)) diffs |= CONFIG_SCREEN_LAYOUT;
     if ((screenLayout2 & MASK_SCREENROUND) != (o.screenLayout2 & MASK_SCREENROUND)) diffs |= CONFIG_SCREEN_ROUND;
+    if ((colorMode & MASK_WIDE_COLOR_GAMUT) != (o.colorMode & MASK_WIDE_COLOR_GAMUT)) diffs |= CONFIG_COLOR_MODE;
+    if ((colorMode & MASK_HDR) != (o.colorMode & MASK_HDR)) diffs |= CONFIG_COLOR_MODE;
     if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
     if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
     if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE;
@@ -2103,6 +2110,17 @@
         }
     }
 
+    if (colorMode || o.colorMode) {
+        if (((colorMode^o.colorMode) & MASK_HDR) != 0) {
+            if (!(colorMode & MASK_HDR)) return false;
+            if (!(o.colorMode & MASK_HDR)) return true;
+        }
+        if (((colorMode^o.colorMode) & MASK_WIDE_COLOR_GAMUT) != 0) {
+            if (!(colorMode & MASK_WIDE_COLOR_GAMUT)) return false;
+            if (!(o.colorMode & MASK_WIDE_COLOR_GAMUT)) return true;
+        }
+    }
+
     if (orientation != o.orientation) {
         if (!orientation) return false;
         if (!o.orientation) return true;
@@ -2390,6 +2408,17 @@
             }
         }
 
+        if (colorMode || o.colorMode) {
+            if (((colorMode^o.colorMode) & MASK_WIDE_COLOR_GAMUT) != 0 &&
+                    (requested->colorMode & MASK_WIDE_COLOR_GAMUT)) {
+                return colorMode & MASK_WIDE_COLOR_GAMUT;
+            }
+            if (((colorMode^o.colorMode) & MASK_HDR) != 0 &&
+                    (requested->colorMode & MASK_HDR)) {
+                return colorMode & MASK_HDR;
+            }
+        }
+
         if ((orientation != o.orientation) && requested->orientation) {
             return (orientation);
         }
@@ -2639,6 +2668,18 @@
         if (screenRound != 0 && screenRound != setScreenRound) {
             return false;
         }
+
+        const int hdr = colorMode & MASK_HDR;
+        const int setHdr = settings.colorMode & MASK_HDR;
+        if (hdr != 0 && hdr != setHdr) {
+            return false;
+        }
+
+        const int wideColorGamut = colorMode & MASK_WIDE_COLOR_GAMUT;
+        const int setWideColorGamut = settings.colorMode & MASK_WIDE_COLOR_GAMUT;
+        if (wideColorGamut != 0 && wideColorGamut != setWideColorGamut) {
+            return false;
+        }
     }
 
     if (screenSizeDp != 0) {
@@ -2959,6 +3000,34 @@
                 break;
         }
     }
+    if ((colorMode&MASK_HDR) != 0) {
+        if (res.size() > 0) res.append("-");
+        switch (colorMode&MASK_HDR) {
+            case ResTable_config::HDR_NO:
+                res.append("lowdr");
+                break;
+            case ResTable_config::HDR_YES:
+                res.append("highdr");
+                break;
+            default:
+                res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR));
+                break;
+        }
+    }
+    if ((colorMode&MASK_WIDE_COLOR_GAMUT) != 0) {
+        if (res.size() > 0) res.append("-");
+        switch (colorMode&MASK_WIDE_COLOR_GAMUT) {
+            case ResTable_config::WIDE_COLOR_GAMUT_NO:
+                res.append("nowidecg");
+                break;
+            case ResTable_config::WIDE_COLOR_GAMUT_YES:
+                res.append("widecg");
+                break;
+            default:
+                res.appendFormat("wideColorGamut=%d", dtohs(colorMode&MASK_WIDE_COLOR_GAMUT));
+                break;
+        }
+    }
     if (orientation != ORIENTATION_ANY) {
         if (res.size() > 0) res.append("-");
         switch (orientation) {
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
new file mode 100644
index 0000000..a3d67f0
--- /dev/null
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APKASSETS_H_
+#define APKASSETS_H_
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "ziparchive/zip_archive.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/LoadedArsc.h"
+
+namespace android {
+
+// Holds an APK.
+class ApkAssets {
+ public:
+  static std::unique_ptr<ApkAssets> Load(const std::string& path);
+
+  std::unique_ptr<Asset> Open(const std::string& path,
+                              Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
+
+  inline const std::string& GetPath() const { return path_; }
+
+  inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ApkAssets);
+
+  ApkAssets() = default;
+
+  struct ZipArchivePtrCloser {
+    void operator()(::ZipArchiveHandle handle) { ::CloseArchive(handle); }
+  };
+
+  using ZipArchivePtr =
+      std::unique_ptr<typename std::remove_pointer<::ZipArchiveHandle>::type, ZipArchivePtrCloser>;
+  ZipArchivePtr zip_handle_;
+  std::string path_;
+  std::unique_ptr<Asset> resources_asset_;
+  std::unique_ptr<LoadedArsc> loaded_arsc_;
+};
+
+}  // namespace android
+
+#endif /* APKASSETS_H_ */
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
new file mode 100644
index 0000000..66d5034
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_ASSETMANAGER2_H_
+#define ANDROIDFW_ASSETMANAGER2_H_
+
+#include "android-base/macros.h"
+
+#include <limits>
+#include <unordered_map>
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/Util.h"
+
+namespace android {
+
+class Theme;
+
+using ApkAssetsCookie = int32_t;
+
+enum : ApkAssetsCookie {
+  kInvalidCookie = -1,
+};
+
+// Holds a bag that has been merged with its parent, if one exists.
+struct ResolvedBag {
+  // A single key-value entry in a bag.
+  struct Entry {
+    // The key, as described in ResTable_map::name.
+    uint32_t key;
+
+    Res_value value;
+
+    // Which ApkAssets this entry came from.
+    ApkAssetsCookie cookie;
+
+    ResStringPool* key_pool;
+    ResStringPool* type_pool;
+  };
+
+  // Denotes the configuration axis that this bag varies with.
+  // If a configuration changes with respect to one of these axis,
+  // the bag should be reloaded.
+  uint32_t type_spec_flags;
+
+  // The number of entries in this bag. Access them by indexing into `entries`.
+  uint32_t entry_count;
+
+  // The array of entries for this bag. An empty array is a neat trick to force alignment
+  // of the Entry structs that follow this structure and avoids a bunch of casts.
+  Entry entries[0];
+};
+
+// AssetManager2 is the main entry point for accessing assets and resources.
+// AssetManager2 provides caching of resources retrieved via the underlying
+// ApkAssets.
+class AssetManager2 : public ::AAssetManager {
+ public:
+  struct ResourceName {
+    const char* package = nullptr;
+    size_t package_len = 0u;
+
+    const char* type = nullptr;
+    const char16_t* type16 = nullptr;
+    size_t type_len = 0u;
+
+    const char* entry = nullptr;
+    const char16_t* entry16 = nullptr;
+    size_t entry_len = 0u;
+  };
+
+  AssetManager2();
+
+  // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
+  // are not owned by the AssetManager, and must have a longer lifetime.
+  //
+  // Only pass invalidate_caches=false when it is known that the structure
+  // change in ApkAssets is due to a safe addition of resources with completely
+  // new resource IDs.
+  bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
+
+  const std::vector<const ApkAssets*> GetApkAssets() const;
+
+  // Returns the string pool for the given asset cookie.
+  // Use the string pool returned here with a valid Res_value object of
+  // type Res_value::TYPE_STRING.
+  const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const;
+
+  // Sets/resets the configuration for this AssetManager. This will cause all
+  // caches that are related to the configuration change to be invalidated.
+  void SetConfiguration(const ResTable_config& configuration);
+
+  const ResTable_config& GetConfiguration() const;
+
+  // Searches the set of APKs loaded by this AssetManager and opens the first one found located
+  // in the assets/ directory.
+  // `mode` controls how the file is opened.
+  //
+  // NOTE: The loaded APKs are searched in reverse order.
+  std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode);
+
+  // Opens a file within the assets/ directory of the APK specified by `cookie`.
+  // `mode` controls how the file is opened.
+  std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
+                              Asset::AccessMode mode);
+
+  // Searches the set of APKs loaded by this AssetManager and opens the first one found.
+  // `mode` controls how the file is opened.
+  // `out_cookie` is populated with the cookie of the APK this file was found in.
+  //
+  // NOTE: The loaded APKs are searched in reverse order.
+  std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode,
+                                      ApkAssetsCookie* out_cookie = nullptr);
+
+  // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
+  // This is typically used to open a specific AndroidManifest.xml, or a binary XML file
+  // referenced by a resource lookup with GetResource().
+  std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
+                                      Asset::AccessMode mode);
+
+  // Populates the `out_name` parameter with resource name information.
+  // Utf8 strings are preferred, and only if they are unavailable are
+  // the Utf16 variants populated.
+  // Returns false if the resource was not found or the name was missing/corrupt.
+  bool GetResourceName(uint32_t resid, ResourceName* out_name);
+
+  // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
+  // See ResTable_config for the list of configuration axis.
+  // Returns false if the resource was not found.
+  bool GetResourceFlags(uint32_t resid, uint32_t* out_flags);
+
+  // Retrieves the best matching resource with ID `resid`. The resource value is filled into
+  // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
+  // `out_flags` holds the same flags as retrieved with GetResourceFlags().
+  // If `density_override` is non-zero, the configuration to match against is overridden with that
+  // density.
+  //
+  // Returns a valid cookie if the resource was found. If the resource was not found, or if the
+  // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
+  // this function logs if the resource was a map/bag type before returning kInvalidCookie.
+  ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
+                              Res_value* out_value, ResTable_config* out_selected_config,
+                              uint32_t* out_flags);
+
+  // Retrieves the best matching bag/map resource with ID `resid`.
+  // This method will resolve all parent references for this bag and merge keys with the child.
+  // To iterate over the keys, use the following idiom:
+  //
+  //  const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
+  //  if (bag != nullptr) {
+  //    for (auto iter = begin(bag); iter != end(bag); ++iter) {
+  //      ...
+  //    }
+  //  }
+  const ResolvedBag* GetBag(uint32_t resid);
+
+  // Creates a new Theme from this AssetManager.
+  std::unique_ptr<Theme> NewTheme();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AssetManager2);
+
+  // Finds the best entry for `resid` amongst all the ApkAssets. The entry can be a simple
+  // Res_value, or a complex map/bag type.
+  //
+  // `density_override` overrides the density of the current configuration when doing a search.
+  //
+  // When `stop_at_first_match` is true, the first match found is selected and the search
+  // terminates. This is useful for methods that just look up the name of a resource and don't
+  // care about the value. In this case, the value of `out_flags` is incomplete and should not
+  // be used.
+  //
+  // `out_flags` stores the resulting bitmask of configuration axis with which the resource
+  // value varies.
+  ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
+                            LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+                            uint32_t* out_flags);
+
+  // Purge all resources that are cached and vary by the configuration axis denoted by the
+  // bitmask `diff`.
+  void InvalidateCaches(uint32_t diff);
+
+  // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
+  // have a longer lifetime.
+  std::vector<const ApkAssets*> apk_assets_;
+
+  // The current configuration set for this AssetManager. When this changes, cached resources
+  // may need to be purged.
+  ResTable_config configuration_;
+
+  // Cached set of bags. These are cached because they can inherit keys from parent bags,
+  // which involves some calculation.
+  std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
+};
+
+class Theme {
+  friend class AssetManager2;
+
+ public:
+  // Applies the style identified by `resid` to this theme. This can be called
+  // multiple times with different styles. By default, any theme attributes that
+  // are already defined before this call are not overridden. If `force` is set
+  // to true, this behavior is changed and all theme attributes from the style at
+  // `resid` are applied.
+  // Returns false if the style failed to apply.
+  bool ApplyStyle(uint32_t resid, bool force = false);
+
+  // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
+  // Returns false if the AssetManagers of the Themes were not compatible.
+  bool SetTo(const Theme& o);
+
+  void Clear();
+
+  inline const AssetManager2* GetAssetManager() const { return asset_manager_; }
+
+  // Returns a bit mask of configuration changes that will impact this
+  // theme (and thus require completely reloading it).
+  inline uint32_t GetChangingConfigurations() const { return type_spec_flags_; }
+
+  // Retrieve a value in the theme. If the theme defines this value,
+  // returns an asset cookie indicating which ApkAssets it came from
+  // and populates `out_value` with the value. If `out_flags` is non-null,
+  // populates it with a bitmask of the configuration axis the resource
+  // varies with.
+  //
+  // If the attribute is not found, returns kInvalidCookie.
+  //
+  // NOTE: This function does not do reference traversal. If you want
+  // to follow references to other resources to get the "real" value to
+  // use, you need to call ResolveReference() after this function.
+  ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value,
+                               uint32_t* out_flags = nullptr) const;
+
+  // This is like AssetManager2::ResolveReference(), but also takes
+  // care of resolving attribute references to the theme.
+  ApkAssetsCookie ResolveAttributeReference(Res_value* in_out_value, ApkAssetsCookie src_cookie,
+                                            uint32_t* out_last_ref = nullptr,
+                                            uint32_t* in_out_type_spec_flags = nullptr,
+                                            ResTable_config* out_selected_config = nullptr) const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Theme);
+
+  // Called by AssetManager2.
+  explicit inline Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {}
+
+  struct Entry {
+    ApkAssetsCookie cookie;
+    uint32_t type_spec_flags;
+    Res_value value;
+  };
+
+  struct Type {
+    // Use uint32_t for fewer cycles when loading from memory.
+    uint32_t entry_count;
+    uint32_t entry_capacity;
+    Entry entries[0];
+  };
+
+  static constexpr const size_t kPackageCount = std::numeric_limits<uint8_t>::max() + 1;
+  static constexpr const size_t kTypeCount = std::numeric_limits<uint8_t>::max() + 1;
+
+  struct Package {
+    // Each element of Type will be a dynamically sized object
+    // allocated to have the entries stored contiguously with the Type.
+    util::unique_cptr<Type> types[kTypeCount];
+  };
+
+  AssetManager2* asset_manager_;
+  uint32_t type_spec_flags_ = 0u;
+  std::unique_ptr<Package> packages_[kPackageCount];
+};
+
+inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { return bag->entries; }
+
+inline const ResolvedBag::Entry* end(const ResolvedBag* bag) {
+  return bag->entries + bag->entry_count;
+}
+
+}  // namespace android
+
+#endif /* ANDROIDFW_ASSETMANAGER2_H_ */
diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
index 87c6b12..d84a207 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -17,9 +17,10 @@
 #ifndef __BYTE_BUCKET_ARRAY_H
 #define __BYTE_BUCKET_ARRAY_H
 
-#include <utils/Log.h>
-#include <stdint.h>
-#include <string.h>
+#include <cstdint>
+#include <cstring>
+
+#include "android-base/logging.h"
 
 namespace android {
 
@@ -27,71 +28,65 @@
  * Stores a sparsely populated array. Has a fixed size of 256
  * (number of entries that a byte can represent).
  */
-template<typename T>
+template <typename T>
 class ByteBucketArray {
-public:
-    ByteBucketArray() : mDefault() {
-        memset(mBuckets, 0, sizeof(mBuckets));
+ public:
+  ByteBucketArray() : default_() { memset(buckets_, 0, sizeof(buckets_)); }
+
+  ~ByteBucketArray() {
+    for (size_t i = 0; i < kNumBuckets; i++) {
+      if (buckets_[i] != NULL) {
+        delete[] buckets_[i];
+      }
+    }
+    memset(buckets_, 0, sizeof(buckets_));
+  }
+
+  inline size_t size() const { return kNumBuckets * kBucketSize; }
+
+  inline const T& get(size_t index) const { return (*this)[index]; }
+
+  const T& operator[](size_t index) const {
+    if (index >= size()) {
+      return default_;
     }
 
-    ~ByteBucketArray() {
-        for (size_t i = 0; i < NUM_BUCKETS; i++) {
-            if (mBuckets[i] != NULL) {
-                delete [] mBuckets[i];
-            }
-        }
-        memset(mBuckets, 0, sizeof(mBuckets));
+    uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
+    T* bucket = buckets_[bucket_index];
+    if (bucket == NULL) {
+      return default_;
+    }
+    return bucket[0x0f & static_cast<uint8_t>(index)];
+  }
+
+  T& editItemAt(size_t index) {
+    CHECK(index < size()) << "ByteBucketArray.getOrCreate(index=" << index
+                          << ") with size=" << size();
+
+    uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
+    T* bucket = buckets_[bucket_index];
+    if (bucket == NULL) {
+      bucket = buckets_[bucket_index] = new T[kBucketSize]();
+    }
+    return bucket[0x0f & static_cast<uint8_t>(index)];
+  }
+
+  bool set(size_t index, const T& value) {
+    if (index >= size()) {
+      return false;
     }
 
-    inline size_t size() const {
-        return NUM_BUCKETS * BUCKET_SIZE;
-    }
+    editItemAt(index) = value;
+    return true;
+  }
 
-    inline const T& get(size_t index) const {
-        return (*this)[index];
-    }
+ private:
+  enum { kNumBuckets = 16, kBucketSize = 16 };
 
-    const T& operator[](size_t index) const {
-        if (index >= size()) {
-            return mDefault;
-        }
-
-        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
-        T* bucket = mBuckets[bucketIndex];
-        if (bucket == NULL) {
-            return mDefault;
-        }
-        return bucket[0x0f & static_cast<uint8_t>(index)];
-    }
-
-    T& editItemAt(size_t index) {
-        ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u",
-                (uint32_t) index, (uint32_t) size());
-
-        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
-        T* bucket = mBuckets[bucketIndex];
-        if (bucket == NULL) {
-            bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE]();
-        }
-        return bucket[0x0f & static_cast<uint8_t>(index)];
-    }
-
-    bool set(size_t index, const T& value) {
-        if (index >= size()) {
-            return false;
-        }
-
-        editItemAt(index) = value;
-        return true;
-    }
-
-private:
-    enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 };
-
-    T*  mBuckets[NUM_BUCKETS];
-    T   mDefault;
+  T* buckets_[kNumBuckets];
+  T default_;
 };
 
-} // namespace android
+}  // namespace android
 
-#endif // __BYTE_BUCKET_ARRAY_H
+#endif  // __BYTE_BUCKET_ARRAY_H
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
new file mode 100644
index 0000000..e2e56c8
--- /dev/null
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOADEDARSC_H_
+#define LOADEDARSC_H_
+
+#include <memory>
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+class Chunk;
+class LoadedPackage;
+
+// Read-only view into a resource table. This class validates all data
+// when loading, including offsets and lengths.
+class LoadedArsc {
+ public:
+  // Load the resource table from memory. The data's lifetime must out-live the
+  // object returned from this method.
+  static std::unique_ptr<LoadedArsc> Load(const void* data, size_t len);
+
+  ~LoadedArsc();
+
+  // Returns the string pool where all string resource values
+  // (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
+  inline const ResStringPool* GetStringPool() const { return &global_string_pool_; }
+
+  struct Entry {
+    // A pointer to the resource table entry for this resource.
+    // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
+    // a ResTable_map_entry and processed as a bag/map.
+    const ResTable_entry* entry = nullptr;
+
+    // The string pool reference to the type's name. This uses a different string pool than
+    // the global string pool, but this is hidden from the caller.
+    StringPoolRef type_string_ref;
+
+    // The string pool reference to the entry's name. This uses a different string pool than
+    // the global string pool, but this is hidden from the caller.
+    StringPoolRef entry_string_ref;
+  };
+
+  // Finds the resource with ID `resid` with the best value for configuration `config`.
+  // The parameter `out_entry` will be filled with the resulting resource entry.
+  // The resource entry can be a simple entry (ResTable_entry) or a complex bag
+  // (ResTable_entry_map).
+  bool FindEntry(uint32_t resid, const ResTable_config& config, Entry* out_entry,
+                 ResTable_config* selected_config, uint32_t* out_flags) const;
+
+  // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist.
+  const std::string* GetPackageNameForId(uint32_t resid) const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
+
+  LoadedArsc() = default;
+  bool LoadTable(const Chunk& chunk);
+
+  ResStringPool global_string_pool_;
+  std::vector<std::unique_ptr<LoadedPackage>> packages_;
+};
+
+}  // namespace android
+
+#endif /* LOADEDARSC_H_ */
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 33b91b9..86ab123 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -265,7 +265,7 @@
     uint8_t res0;
         
     // Type of the data value.
-    enum {
+    enum : uint8_t {
         // The 'data' is either 0 or 1, specifying this resource is either
         // undefined or empty, respectively.
         TYPE_NULL = 0x00,
@@ -1146,11 +1146,26 @@
         SCREENROUND_YES = ACONFIGURATION_SCREENROUND_YES,
     };
 
+    enum {
+        // colorMode bits for wide-color gamut/narrow-color gamut.
+        MASK_WIDE_COLOR_GAMUT = 0x03,
+        WIDE_COLOR_GAMUT_ANY = ACONFIGURATION_WIDE_COLOR_GAMUT_ANY,
+        WIDE_COLOR_GAMUT_NO = ACONFIGURATION_WIDE_COLOR_GAMUT_NO,
+        WIDE_COLOR_GAMUT_YES = ACONFIGURATION_WIDE_COLOR_GAMUT_YES,
+
+        // colorMode bits for HDR/LDR.
+        MASK_HDR = 0x0c,
+        SHIFT_COLOR_MODE_HDR = 2,
+        HDR_ANY = ACONFIGURATION_HDR_ANY << SHIFT_COLOR_MODE_HDR,
+        HDR_NO = ACONFIGURATION_HDR_NO << SHIFT_COLOR_MODE_HDR,
+        HDR_YES = ACONFIGURATION_HDR_YES << SHIFT_COLOR_MODE_HDR,
+    };
+
     // An extension of screenConfig.
     union {
         struct {
             uint8_t screenLayout2;      // Contains round/notround qualifier.
-            uint8_t screenConfigPad1;   // Reserved padding.
+            uint8_t colorMode;          // Wide-gamut, HDR, etc.
             uint16_t screenConfigPad2;  // Reserved padding.
         };
         uint32_t screenConfig2;
@@ -1193,6 +1208,7 @@
         CONFIG_UI_MODE = ACONFIGURATION_UI_MODE,
         CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR,
         CONFIG_SCREEN_ROUND = ACONFIGURATION_SCREEN_ROUND,
+        CONFIG_COLOR_MODE = ACONFIGURATION_COLOR_MODE,
     };
     
     // Compare two configuration, returning CONFIG_* flags set for each value
diff --git a/tools/aapt2/util/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
similarity index 66%
rename from tools/aapt2/util/StringPiece.h
rename to libs/androidfw/include/androidfw/StringPiece.h
index 5144b1f..c9effd1 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -14,26 +14,24 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_STRING_PIECE_H
-#define AAPT_STRING_PIECE_H
+#ifndef ANDROIDFW_STRING_PIECE_H
+#define ANDROIDFW_STRING_PIECE_H
 
 #include <ostream>
 #include <string>
 
 #include "utils/JenkinsHash.h"
-#include "utils/String8.h"
 #include "utils/Unicode.h"
 
-namespace aapt {
+namespace android {
 
-/**
- * Read only wrapper around basic C strings.
- * Prevents excessive copying.
- *
- * WARNING: When creating from std::basic_string<>, moving the original
- * std::basic_string<> will invalidate the data held in a BasicStringPiece<>.
- * BasicStringPiece<> should only be used transitively.
- */
+// Read only wrapper around basic C strings. Prevents excessive copying.
+// StringPiece does not own the data it is wrapping. The lifetime of the underlying
+// data must outlive this StringPiece.
+//
+// WARNING: When creating from std::basic_string<>, moving the original
+// std::basic_string<> will invalidate the data held in a BasicStringPiece<>.
+// BasicStringPiece<> should only be used transitively.
 template <typename TChar>
 class BasicStringPiece {
  public:
@@ -53,15 +51,14 @@
   BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
 
   BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
-  BasicStringPiece<TChar> substr(
-      BasicStringPiece<TChar>::const_iterator begin,
-      BasicStringPiece<TChar>::const_iterator end) const;
+  BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
+                                 BasicStringPiece<TChar>::const_iterator end) const;
 
   const TChar* data() const;
   size_t length() const;
   size_t size() const;
   bool empty() const;
-  std::basic_string<TChar> ToString() const;
+  std::basic_string<TChar> to_string() const;
 
   bool contains(const BasicStringPiece<TChar>& rhs) const;
   int compare(const BasicStringPiece<TChar>& rhs) const;
@@ -89,17 +86,14 @@
 constexpr const size_t BasicStringPiece<TChar>::npos;
 
 template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece()
-    : data_(nullptr), length_(0) {}
+inline BasicStringPiece<TChar>::BasicStringPiece() : data_(nullptr), length_(0) {}
 
 template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(
-    const BasicStringPiece<TChar>& str)
+inline BasicStringPiece<TChar>::BasicStringPiece(const BasicStringPiece<TChar>& str)
     : data_(str.data_), length_(str.length_) {}
 
 template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(
-    const std::basic_string<TChar>& str)
+inline BasicStringPiece<TChar>::BasicStringPiece(const std::basic_string<TChar>& str)
     : data_(str.data()), length_(str.length()) {}
 
 template <>
@@ -123,16 +117,14 @@
 }
 
 template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(
-    const TChar* str, size_t len) {
+inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(const TChar* str, size_t len) {
   data_ = str;
   length_ = len;
   return *this;
 }
 
 template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
-    size_t start, size_t len) const {
+inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
   if (len == npos) {
     len = length_ - start;
   }
@@ -171,13 +163,12 @@
 }
 
 template <typename TChar>
-inline std::basic_string<TChar> BasicStringPiece<TChar>::ToString() const {
+inline std::basic_string<TChar> BasicStringPiece<TChar>::to_string() const {
   return std::basic_string<TChar>(data_, length_);
 }
 
 template <>
-inline bool BasicStringPiece<char>::contains(
-    const BasicStringPiece<char>& rhs) const {
+inline bool BasicStringPiece<char>::contains(const BasicStringPiece<char>& rhs) const {
   if (!data_ || !rhs.data_) {
     return false;
   }
@@ -188,8 +179,7 @@
 }
 
 template <>
-inline int BasicStringPiece<char>::compare(
-    const BasicStringPiece<char>& rhs) const {
+inline int BasicStringPiece<char>::compare(const BasicStringPiece<char>& rhs) const {
   const char nullStr = '\0';
   const char* b1 = data_ != nullptr ? data_ : &nullStr;
   const char* e1 = b1 + length_;
@@ -205,15 +195,21 @@
   return static_cast<int>(length_ - rhs.length_);
 }
 
-inline ::std::ostream& operator<<(::std::ostream& out,
-                                  const BasicStringPiece<char16_t>& str) {
-  android::String8 utf8(str.data(), str.size());
-  return out.write(utf8.string(), utf8.size());
+inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char16_t>& str) {
+  const ssize_t result_len = utf16_to_utf8_length(str.data(), str.size());
+  if (result_len < 0) {
+    // Empty string.
+    return out;
+  }
+
+  std::string result;
+  result.resize(static_cast<size_t>(result_len));
+  utf16_to_utf8(str.data(), str.length(), &*result.begin(), static_cast<size_t>(result_len) + 1);
+  return out << result;
 }
 
 template <>
-inline bool BasicStringPiece<char16_t>::contains(
-    const BasicStringPiece<char16_t>& rhs) const {
+inline bool BasicStringPiece<char16_t>::contains(const BasicStringPiece<char16_t>& rhs) const {
   if (!data_ || !rhs.data_) {
     return false;
   }
@@ -224,8 +220,7 @@
 }
 
 template <>
-inline int BasicStringPiece<char16_t>::compare(
-    const BasicStringPiece<char16_t>& rhs) const {
+inline int BasicStringPiece<char16_t>::compare(const BasicStringPiece<char16_t>& rhs) const {
   const char16_t nullStr = u'\0';
   const char16_t* b1 = data_ != nullptr ? data_ : &nullStr;
   const char16_t* b2 = rhs.data_ != nullptr ? rhs.data_ : &nullStr;
@@ -233,66 +228,52 @@
 }
 
 template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator<(
-    const BasicStringPiece<TChar>& rhs) const {
+inline bool BasicStringPiece<TChar>::operator<(const BasicStringPiece<TChar>& rhs) const {
   return compare(rhs) < 0;
 }
 
 template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator>(
-    const BasicStringPiece<TChar>& rhs) const {
+inline bool BasicStringPiece<TChar>::operator>(const BasicStringPiece<TChar>& rhs) const {
   return compare(rhs) > 0;
 }
 
 template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator==(
-    const BasicStringPiece<TChar>& rhs) const {
+inline bool BasicStringPiece<TChar>::operator==(const BasicStringPiece<TChar>& rhs) const {
   return compare(rhs) == 0;
 }
 
 template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator!=(
-    const BasicStringPiece<TChar>& rhs) const {
+inline bool BasicStringPiece<TChar>::operator!=(const BasicStringPiece<TChar>& rhs) const {
   return compare(rhs) != 0;
 }
 
 template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator
-BasicStringPiece<TChar>::begin() const {
+inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::begin() const {
   return data_;
 }
 
 template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator
-BasicStringPiece<TChar>::end() const {
+inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::end() const {
   return data_ + length_;
 }
 
-inline ::std::ostream& operator<<(::std::ostream& out,
-                                  const BasicStringPiece<char>& str) {
+inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) {
   return out.write(str.data(), str.size());
 }
 
-}  // namespace aapt
-
-inline ::std::ostream& operator<<(::std::ostream& out,
-                                  const std::u16string& str) {
-  android::String8 utf8(str.data(), str.size());
-  return out.write(utf8.string(), utf8.size());
-}
+}  // namespace android
 
 namespace std {
 
 template <typename TChar>
-struct hash<aapt::BasicStringPiece<TChar>> {
-  size_t operator()(const aapt::BasicStringPiece<TChar>& str) const {
+struct hash<android::BasicStringPiece<TChar>> {
+  size_t operator()(const android::BasicStringPiece<TChar>& str) const {
     uint32_t hashCode = android::JenkinsHashMixBytes(
-        0, reinterpret_cast<const uint8_t*>(str.data()),
-        sizeof(TChar) * str.size());
+        0, reinterpret_cast<const uint8_t*>(str.data()), sizeof(TChar) * str.size());
     return static_cast<size_t>(hashCode);
   }
 };
 
 }  // namespace std
 
-#endif  // AAPT_STRING_PIECE_H
+#endif  // ANDROIDFW_STRING_PIECE_H
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
new file mode 100644
index 0000000..5266d09
--- /dev/null
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <cstdlib>
+#include <memory>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace util {
+
+/**
+ * Makes a std::unique_ptr<> with the template parameter inferred by the
+ * compiler.
+ * This will be present in C++14 and can be removed then.
+ */
+template <typename T, class... Args>
+std::unique_ptr<T> make_unique(Args&&... args) {
+  return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
+}
+
+// Based on std::unique_ptr, but uses free() to release malloc'ed memory
+// without incurring the size increase of holding on to a custom deleter.
+template <typename T>
+class unique_cptr {
+ public:
+  using pointer = typename std::add_pointer<T>::type;
+
+  constexpr unique_cptr() : ptr_(nullptr) {}
+  constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
+  explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
+  unique_cptr(unique_cptr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; }
+
+  ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
+
+  inline unique_cptr& operator=(unique_cptr&& o) {
+    if (&o == this) {
+      return *this;
+    }
+
+    std::free(reinterpret_cast<void*>(ptr_));
+    ptr_ = o.ptr_;
+    o.ptr_ = nullptr;
+    return *this;
+  }
+
+  inline unique_cptr& operator=(std::nullptr_t) {
+    std::free(reinterpret_cast<void*>(ptr_));
+    ptr_ = nullptr;
+    return *this;
+  }
+
+  pointer release() {
+    pointer result = ptr_;
+    ptr_ = nullptr;
+    return result;
+  }
+
+  inline pointer get() const { return ptr_; }
+
+  void reset(pointer ptr = pointer()) {
+    if (ptr == ptr_) {
+      return;
+    }
+
+    pointer old_ptr = ptr_;
+    ptr_ = ptr;
+    std::free(reinterpret_cast<void*>(old_ptr));
+  }
+
+  inline void swap(unique_cptr& o) { std::swap(ptr_, o.ptr_); }
+
+  inline explicit operator bool() const { return ptr_ != nullptr; }
+
+  inline typename std::add_lvalue_reference<T>::type operator*() const { return *ptr_; }
+
+  inline pointer operator->() const { return ptr_; }
+
+  inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
+
+  inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(unique_cptr);
+
+  pointer ptr_;
+};
+
+inline uint8_t get_package_id(uint32_t resid) {
+  return static_cast<uint8_t>((resid >> 24) & 0x000000ffu);
+}
+
+// The type ID is 1-based, so if the returned value is 0 it is invalid.
+inline uint8_t get_type_id(uint32_t resid) {
+  return static_cast<uint8_t>((resid >> 16) & 0x000000ffu);
+}
+
+inline uint16_t get_entry_id(uint32_t resid) { return static_cast<uint16_t>(resid & 0x0000ffffu); }
+
+inline bool is_internal_id(uint32_t resid) {
+  return (resid & 0xffff0000u) != 0 && (resid & 0x00ff0000u) == 0;
+}
+
+inline bool is_valid_resid(uint32_t resid) {
+  return (resid & 0x00ff0000u) != 0 && (resid & 0xff000000u) != 0;
+}
+
+}  // namespace util
+}  // namespace android
+
+#endif /* UTIL_H_ */
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index d91a133..650f813 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -21,22 +21,32 @@
 LOCAL_PATH:= $(call my-dir)
 
 testFiles := \
+    ApkAssets_test.cpp \
     AppAsLib_test.cpp \
     Asset_test.cpp \
+    AssetManager2_test.cpp \
     AttributeFinder_test.cpp \
     AttributeResolution_test.cpp \
     ByteBucketArray_test.cpp \
     Config_test.cpp \
     ConfigLocale_test.cpp \
     Idmap_test.cpp \
-    Main.cpp \
+    LoadedArsc_test.cpp \
     ResTable_test.cpp \
     Split_test.cpp \
+    StringPiece_test.cpp \
     TestHelpers.cpp \
+    TestMain.cpp \
     Theme_test.cpp \
     TypeWrappers_test.cpp \
     ZipUtils_test.cpp
 
+benchmarkFiles := \
+    AssetManager2_bench.cpp \
+    BenchMain.cpp \
+    TestHelpers.cpp \
+    Theme_bench.cpp
+
 androidfw_test_cflags := \
     -Wall \
     -Werror \
@@ -89,5 +99,25 @@
 LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
 
 include $(BUILD_NATIVE_TEST)
+
+# ==========================================================
+# Build the device benchmarks: libandroidfw_benchmarks
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libandroidfw_benchmarks
+LOCAL_CFLAGS := $(androidfw_test_cflags)
+LOCAL_SRC_FILES := $(benchmarkFiles)
+LOCAL_STATIC_LIBRARIES := \
+    libgoogle-benchmark
+LOCAL_SHARED_LIBRARIES := \
+    libandroidfw \
+    libbase \
+    libcutils \
+    libutils \
+    libziparchive
+LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
+
+include $(BUILD_NATIVE_TEST)
 endif # Not SDK_ONLY
 
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
new file mode 100644
index 0000000..3a1fc8f
--- /dev/null
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/ApkAssets.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+
+using com::android::basic::R;
+
+namespace android {
+
+TEST(ApkAssetsTest, LoadApk) {
+  std::unique_ptr<ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+  ASSERT_NE(nullptr, loaded_apk);
+
+  std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
+  ASSERT_NE(nullptr, asset);
+}
+
+}  // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
new file mode 100644
index 0000000..9ff9478
--- /dev/null
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark/benchmark.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace basic = com::android::basic;
+namespace app = com::android::app;
+
+namespace android {
+
+constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
+
+static void BM_AssetManagerLoadAssets(benchmark::State& state) {
+  std::string path = GetTestDataPath() + "/basic/basic.apk";
+  while (state.KeepRunning()) {
+    std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+    AssetManager2 assets;
+    assets.SetApkAssets({apk.get()});
+  }
+}
+BENCHMARK(BM_AssetManagerLoadAssets);
+
+static void BM_AssetManagerLoadAssetsOld(benchmark::State& state) {
+  String8 path((GetTestDataPath() + "/basic/basic.apk").data());
+  while (state.KeepRunning()) {
+    AssetManager assets;
+    assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */,
+                        false /* isSystemAsset */);
+
+    // Force creation.
+    assets.getResources(true);
+  }
+}
+BENCHMARK(BM_AssetManagerLoadAssetsOld);
+
+static void BM_AssetManagerLoadFrameworkAssets(benchmark::State& state) {
+  std::string path = kFrameworkPath;
+  while (state.KeepRunning()) {
+    std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+    AssetManager2 assets;
+    assets.SetApkAssets({apk.get()});
+  }
+}
+BENCHMARK(BM_AssetManagerLoadFrameworkAssets);
+
+static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) {
+  String8 path(kFrameworkPath);
+  while (state.KeepRunning()) {
+    AssetManager assets;
+    assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */,
+                        false /* isSystemAsset */);
+
+    // Force creation.
+    assets.getResources(true);
+  }
+}
+BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld);
+
+static void BM_AssetManagerGetResource(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+  if (apk == nullptr) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    assets.GetResource(basic::R::integer::number1, false /* may_be_bag */,
+                       0u /* density_override */, &value, &selected_config, &flags);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResource);
+
+static void BM_AssetManagerGetResourceOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()),
+                           nullptr /* cookie */, false /* appAsLib */,
+                           false /* isSystemAssets */)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  const ResTable& table = assets.getResources(true);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    table.getResource(basic::R::integer::number1, &value, false /* may_be_bag */,
+                      0u /* density_override */, &flags, &selected_config);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResourceOld);
+
+constexpr static const uint32_t kStringOkId = 0x0104000au;
+
+static void BM_AssetManagerGetResourceFrameworkLocale(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+  if (apk == nullptr) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+  memcpy(config.language, "fr", 2);
+  assets.SetConfiguration(config);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    assets.GetResource(kStringOkId, false /* may_be_bag */, 0u /* density_override */, &value,
+                       &selected_config, &flags);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResourceFrameworkLocale);
+
+static void BM_AssetManagerGetResourceFrameworkLocaleOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()),
+                           nullptr /* cookie */, false /* appAsLib */,
+                           false /* isSystemAssets */)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+  memcpy(config.language, "fr", 2);
+  assets.setConfiguration(config, nullptr);
+
+  const ResTable& table = assets.getResources(true);
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    table.getResource(kStringOkId, &value, false /* may_be_bag */, 0u /* density_override */,
+                      &flags, &selected_config);
+  }
+}
+BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld);
+
+static void BM_AssetManagerGetBag(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+  if (apk == nullptr) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  while (state.KeepRunning()) {
+    const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo);
+    const auto bag_end = end(bag);
+    for (auto iter = begin(bag); iter != bag_end; ++iter) {
+      uint32_t key = iter->key;
+      Res_value value = iter->value;
+      benchmark::DoNotOptimize(key);
+      benchmark::DoNotOptimize(value);
+    }
+  }
+}
+BENCHMARK(BM_AssetManagerGetBag);
+
+static void BM_AssetManagerGetBagOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8((GetTestDataPath() + "/styles/styles.apk").data()),
+                           nullptr /* cookie */, false /* appAsLib */,
+                           false /* isSystemAssets */)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  const ResTable& table = assets.getResources(true);
+
+  while (state.KeepRunning()) {
+    const ResTable::bag_entry* bag_begin;
+    const ssize_t N = table.lockBag(app::R::style::StyleTwo, &bag_begin);
+    const ResTable::bag_entry* const bag_end = bag_begin + N;
+    for (auto iter = bag_begin; iter != bag_end; ++iter) {
+      uint32_t key = iter->map.name.ident;
+      Res_value value = iter->map.value;
+      benchmark::DoNotOptimize(key);
+      benchmark::DoNotOptimize(value);
+    }
+    table.unlockBag(bag_begin);
+  }
+}
+BENCHMARK(BM_AssetManagerGetBagOld);
+
+}  // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
new file mode 100644
index 0000000..39c5381
--- /dev/null
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/AssetManager2.h"
+#include "androidfw/AssetManager.h"
+
+#include "android-base/logging.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace basic = com::android::basic;
+namespace app = com::android::app;
+
+namespace android {
+
+class AssetManager2Test : public ::testing::Test {
+ public:
+  void SetUp() override {
+    basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+    ASSERT_NE(nullptr, basic_assets_);
+
+    basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk");
+    ASSERT_NE(nullptr, basic_de_fr_assets_);
+
+    style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+    ASSERT_NE(nullptr, style_assets_);
+  }
+
+ protected:
+  std::unique_ptr<ApkAssets> basic_assets_;
+  std::unique_ptr<ApkAssets> basic_de_fr_assets_;
+  std::unique_ptr<ApkAssets> style_assets_;
+};
+
+TEST_F(AssetManager2Test, FindsResourcesFromSingleApkAssets) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  AssetManager2 assetmanager;
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  // Came from our ApkAssets.
+  EXPECT_EQ(0, cookie);
+
+  // It is the default config.
+  EXPECT_EQ(0, selected_config.language[0]);
+  EXPECT_EQ(0, selected_config.language[1]);
+
+  // It is a string.
+  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+}
+
+TEST_F(AssetManager2Test, FindsResourcesFromMultipleApkAssets) {
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  AssetManager2 assetmanager;
+  assetmanager.SetConfiguration(desired_config);
+  assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
+
+  Res_value value;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ApkAssetsCookie cookie =
+      assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+                               0 /*density_override*/, &value, &selected_config, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+
+  // Came from our de_fr ApkAssets.
+  EXPECT_EQ(1, cookie);
+
+  // The configuration is german.
+  EXPECT_EQ('d', selected_config.language[0]);
+  EXPECT_EQ('e', selected_config.language[1]);
+
+  // It is a string.
+  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+}
+
+TEST_F(AssetManager2Test, FindsBagResourcesFromSingleApkAssets) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({basic_assets_.get()});
+
+  const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1);
+  ASSERT_NE(nullptr, bag);
+  ASSERT_EQ(3u, bag->entry_count);
+
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType);
+  EXPECT_EQ(1u, bag->entries[0].value.data);
+  EXPECT_EQ(0, bag->entries[0].cookie);
+
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType);
+  EXPECT_EQ(2u, bag->entries[1].value.data);
+  EXPECT_EQ(0, bag->entries[1].cookie);
+
+  EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType);
+  EXPECT_EQ(3u, bag->entries[2].value.data);
+  EXPECT_EQ(0, bag->entries[2].cookie);
+}
+
+TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne);
+  ASSERT_NE(nullptr, bag_one);
+  ASSERT_EQ(2u, bag_one->entry_count);
+
+  EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType);
+  EXPECT_EQ(1u, bag_one->entries[0].value.data);
+  EXPECT_EQ(0, bag_one->entries[0].cookie);
+
+  EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType);
+  EXPECT_EQ(2u, bag_one->entries[1].value.data);
+  EXPECT_EQ(0, bag_one->entries[1].cookie);
+
+  const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
+  ASSERT_NE(nullptr, bag_two);
+  ASSERT_EQ(5u, bag_two->entry_count);
+
+  // attr_one is inherited from StyleOne.
+  EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType);
+  EXPECT_EQ(1u, bag_two->entries[0].value.data);
+  EXPECT_EQ(0, bag_two->entries[0].cookie);
+
+  // attr_two should be overridden from StyleOne by StyleTwo.
+  EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key);
+  EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType);
+  EXPECT_EQ(0, bag_two->entries[1].cookie);
+  EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0),
+                                                     bag_two->entries[1].value.data));
+
+  // The rest are new attributes.
+
+  EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key);
+  EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType);
+  EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data);
+  EXPECT_EQ(0, bag_two->entries[2].cookie);
+
+  EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType);
+  EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data);
+  EXPECT_EQ(0, bag_two->entries[3].cookie);
+
+  EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType);
+  EXPECT_EQ(3u, bag_two->entries[4].value.data);
+  EXPECT_EQ(0, bag_two->entries[4].cookie);
+}
+
+TEST_F(AssetManager2Test, FindsBagResourcesFromMultipleApkAssets) {}
+
+TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
+
+TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
+
+}  // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 7550517..1ff2ed4 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -205,4 +205,5 @@
   EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
 }
 
-}  // namespace android
+} // namespace android
+
diff --git a/libs/androidfw/tests/BenchMain.cpp b/libs/androidfw/tests/BenchMain.cpp
new file mode 100644
index 0000000..105c5f9
--- /dev/null
+++ b/libs/androidfw/tests/BenchMain.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "benchmark/benchmark.h"
+
+#include "TestHelpers.h"
+
+int main(int argc, char** argv) {
+  ::benchmark::Initialize(&argc, argv);
+  ::android::InitializeTest(&argc, argv);
+
+  std::cerr << "using --testdata=" << ::android::GetTestDataPath() << "\n";
+
+  size_t result = ::benchmark::RunSpecifiedBenchmarks();
+  return result == 0 ? 1 : 0;
+}
diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp
index 778c7bf..b54915f 100644
--- a/libs/androidfw/tests/Config_test.cpp
+++ b/libs/androidfw/tests/Config_test.cpp
@@ -182,4 +182,24 @@
   EXPECT_TRUE(targetConfigC.isBetterThan(targetConfigB, &deviceConfig));
 }
 
+TEST(ConfigTest, ScreenIsWideGamut) {
+  ResTable_config defaultConfig;
+  memset(&defaultConfig, 0, sizeof(defaultConfig));
+
+  ResTable_config wideGamutConfig = defaultConfig;
+  wideGamutConfig.colorMode = ResTable_config::WIDE_COLOR_GAMUT_YES;
+
+  EXPECT_EQ(defaultConfig.diff(wideGamutConfig), ResTable_config::CONFIG_COLOR_MODE);
+}
+
+TEST(ConfigTest, ScreenIsHdr) {
+  ResTable_config defaultConfig;
+  memset(&defaultConfig, 0, sizeof(defaultConfig));
+
+  ResTable_config hdrConfig = defaultConfig;
+  hdrConfig.colorMode = ResTable_config::HDR_YES;
+
+  EXPECT_EQ(defaultConfig.diff(hdrConfig), ResTable_config::CONFIG_COLOR_MODE);
+}
+
 }  // namespace android.
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
new file mode 100644
index 0000000..47b3894
--- /dev/null
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/LoadedArsc.h"
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace app = com::android::app;
+namespace basic = com::android::basic;
+
+namespace android {
+
+TEST(LoadedArscTest, LoadSinglePackageArsc) {
+  base::ScopedLogSeverity _log(base::LogSeverity::DEBUG);
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
+                                      &contents));
+
+  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  ResTable_config config;
+  memset(&config, 0, sizeof(config));
+  config.sdkVersion = 24;
+
+  LoadedArsc::Entry entry;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ASSERT_TRUE(
+      loaded_arsc->FindEntry(app::R::string::string_one, config, &entry, &selected_config, &flags));
+  ASSERT_NE(nullptr, entry.entry);
+}
+
+TEST(LoadedArscTest, FindDefaultEntry) {
+  base::ScopedLogSeverity _log(base::LogSeverity::DEBUG);
+  std::string contents;
+  ASSERT_TRUE(
+      ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
+
+  std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+  ASSERT_NE(nullptr, loaded_arsc);
+
+  ResTable_config desired_config;
+  memset(&desired_config, 0, sizeof(desired_config));
+  desired_config.language[0] = 'd';
+  desired_config.language[1] = 'e';
+
+  LoadedArsc::Entry entry;
+  ResTable_config selected_config;
+  uint32_t flags;
+
+  ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry,
+                                     &selected_config, &flags));
+  ASSERT_NE(nullptr, entry.entry);
+}
+
+// structs with size fields (like Res_value, ResTable_entry) should be
+// backwards and forwards compatible (aka checking the size field against
+// sizeof(Res_value) might not be backwards compatible.
+TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+
+}  // namespace android
diff --git a/libs/androidfw/tests/Main.cpp b/libs/androidfw/tests/Main.cpp
deleted file mode 100644
index 6a50691..0000000
--- a/libs/androidfw/tests/Main.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libgen.h>
-
-#include <iostream>
-#include <memory>
-#include <string>
-
-#include "android-base/file.h"
-#include "android-base/strings.h"
-#include "gtest/gtest.h"
-
-#include "TestHelpers.h"
-
-// Extract the directory of the current executable path.
-static std::string GetExecutableDir() {
-  const std::string path = android::base::GetExecutablePath();
-  std::unique_ptr<char, decltype(&std::free)> mutable_path = {
-      strdup(path.c_str()), std::free};
-  std::string executable_dir = dirname(mutable_path.get());
-  return executable_dir;
-}
-
-int main(int argc, char** argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-
-  // Set the default test data path to be the executable path directory.
-  android::SetTestDataPath(GetExecutableDir());
-
-  const char* command = argv[0];
-  ++argv;
-  --argc;
-
-  while (argc > 0) {
-    const std::string arg = *argv;
-    if (android::base::StartsWith(arg, "--testdata=")) {
-      android::SetTestDataPath(arg.substr(strlen("--testdata=")));
-    } else if (arg == "-h" || arg == "--help") {
-      std::cerr
-          << "\nAdditional options specific to this test:\n"
-             "  --testdata=[PATH]\n"
-             "      Specify the location of test data used within the tests.\n";
-      return 1;
-    } else {
-      std::cerr << command << ": Unrecognized argument '" << *argv << "'.\n";
-      return 1;
-    }
-
-    --argc;
-    ++argv;
-  }
-
-  std::cerr << "using --testdata=" << android::GetTestDataPath() << "\n";
-  return RUN_ALL_TESTS();
-}
diff --git a/tools/aapt2/util/StringPiece_test.cpp b/libs/androidfw/tests/StringPiece_test.cpp
similarity index 96%
rename from tools/aapt2/util/StringPiece_test.cpp
rename to libs/androidfw/tests/StringPiece_test.cpp
index 048961d..316a5c1 100644
--- a/tools/aapt2/util/StringPiece_test.cpp
+++ b/libs/androidfw/tests/StringPiece_test.cpp
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#include "util/StringPiece.h"
+#include "androidfw/StringPiece.h"
 
 #include <algorithm>
 #include <string>
 #include <vector>
 
-#include "test/Test.h"
+#include "TestHelpers.h"
 
-namespace aapt {
+namespace android {
 
 TEST(StringPieceTest, CompareNonNullTerminatedPiece) {
   StringPiece a("hello world", 5);
@@ -92,4 +92,4 @@
   EXPECT_FALSE(text16.contains(long_needle16));
 }
 
-}  // namespace aapt
+}  // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index 2c834b1..1e763a5 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -16,15 +16,51 @@
 
 #include "TestHelpers.h"
 
+#include <libgen.h>
 #include <unistd.h>
 
+#include <memory>
+#include <string>
+
+#include "android-base/file.h"
 #include "android-base/logging.h"
+#include "android-base/strings.h"
 #include "ziparchive/zip_archive.h"
 
 namespace android {
 
 static std::string sTestDataPath;
 
+// Extract the directory of the current executable path.
+static std::string GetExecutableDir() {
+  const std::string path = base::GetExecutablePath();
+  std::unique_ptr<char, decltype(&std::free)> mutable_path = {strdup(path.c_str()), std::free};
+  std::string executable_dir = dirname(mutable_path.get());
+  return executable_dir;
+}
+
+void InitializeTest(int* argc, char** argv) {
+  // Set the default test data path to be the executable path directory.
+  SetTestDataPath(GetExecutableDir());
+
+  for (int i = 1; i < *argc; i++) {
+    const std::string arg = argv[i];
+    if (base::StartsWith(arg, "--testdata=")) {
+      SetTestDataPath(arg.substr(strlen("--testdata=")));
+      for (int j = i; j != *argc; j++) {
+        argv[j] = argv[j + 1];
+      }
+      --(*argc);
+      --i;
+    } else if (arg == "-h" || arg == "--help") {
+      std::cerr << "\nAdditional options specific to this test:\n"
+                   "  --testdata=[PATH]\n"
+                   "      Specify the location of test data used within the tests.\n";
+      exit(1);
+    }
+  }
+}
+
 void SetTestDataPath(const std::string& path) { sTestDataPath = path; }
 
 const std::string& GetTestDataPath() {
@@ -90,4 +126,9 @@
   return ::testing::AssertionSuccess() << actual_str.string();
 }
 
+std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
+  String8 str = pool->string8ObjectAt(idx);
+  return std::string(str.string(), str.length());
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index d9cee22..a11ea84 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -35,6 +35,8 @@
 
 namespace android {
 
+void InitializeTest(int* argc, char** argv);
+
 enum { MAY_NOT_BE_BAG = false };
 
 void SetTestDataPath(const std::string& path);
@@ -56,6 +58,8 @@
 ::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
                                          const char* expected_str);
 
+std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx);
+
 }  // namespace android
 
 #endif  // TEST_HELPERS_H_
diff --git a/libs/androidfw/tests/TestMain.cpp b/libs/androidfw/tests/TestMain.cpp
new file mode 100644
index 0000000..d1c0f60
--- /dev/null
+++ b/libs/androidfw/tests/TestMain.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "TestHelpers.h"
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+  ::android::InitializeTest(&argc, argv);
+
+  std::cerr << "using --testdata=" << ::android::GetTestDataPath() << "\n";
+
+  return RUN_ALL_TESTS();
+}
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
new file mode 100644
index 0000000..c471be6
--- /dev/null
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark/benchmark.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
+constexpr const static uint32_t kStyleId = 0x01030237u;  // android:style/Theme.Material.Light
+constexpr const static uint32_t kAttrId = 0x01010030u;   // android:attr/colorForeground
+
+static void BM_ThemeApplyStyleFramework(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+  if (apk == nullptr) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  while (state.KeepRunning()) {
+    auto theme = assets.NewTheme();
+    theme->ApplyStyle(kStyleId, false /* force */);
+  }
+}
+BENCHMARK(BM_ThemeApplyStyleFramework);
+
+static void BM_ThemeApplyStyleFrameworkOld(benchmark::State& state) {
+  AssetManager assets;
+  if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /* cookie */, false /* appAsLib */,
+                           true /* isSystemAsset */)) {
+    state.SkipWithError("Failed to load assets");
+    return;
+  }
+
+  const ResTable& res_table = assets.getResources(true);
+
+  while (state.KeepRunning()) {
+    std::unique_ptr<ResTable::Theme> theme{new ResTable::Theme(res_table)};
+    theme->applyStyle(kStyleId, false /* force */);
+  }
+}
+BENCHMARK(BM_ThemeApplyStyleFrameworkOld);
+
+static void BM_ThemeGetAttribute(benchmark::State& state) {
+  std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+
+  AssetManager2 assets;
+  assets.SetApkAssets({apk.get()});
+
+  auto theme = assets.NewTheme();
+  theme->ApplyStyle(kStyleId, false /* force */);
+
+  Res_value value;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    theme->GetAttribute(kAttrId, &value, &flags);
+  }
+}
+BENCHMARK(BM_ThemeGetAttribute);
+
+static void BM_ThemeGetAttributeOld(benchmark::State& state) {
+  AssetManager assets;
+  assets.addAssetPath(String8(kFrameworkPath), nullptr /* cookie */, false /* appAsLib */,
+                      true /* isSystemAsset */);
+  const ResTable& res_table = assets.getResources(true);
+  std::unique_ptr<ResTable::Theme> theme{new ResTable::Theme(res_table)};
+  theme->applyStyle(kStyleId, false /* force */);
+
+  Res_value value;
+  uint32_t flags;
+
+  while (state.KeepRunning()) {
+    theme->getAttribute(kAttrId, &value, &flags);
+  }
+}
+BENCHMARK(BM_ThemeGetAttributeOld);
+
+}  // namespace android
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 3774657..c0011b6d 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,59 +14,221 @@
  * limitations under the License.
  */
 
-#include "androidfw/ResourceTypes.h"
+#include "androidfw/AssetManager2.h"
 
-#include "utils/String16.h"
-#include "utils/String8.h"
+#include "android-base/logging.h"
 
 #include "TestHelpers.h"
-#include "data/app/R.h"
-#include "data/system/R.h"
+#include "data/styles/R.h"
 
 namespace app = com::android::app;
 
 namespace android {
 
-/**
- * TODO(adamlesinski): Enable when fixed.
- */
-TEST(ThemeTest, DISABLED_shouldCopyThemeFromDifferentResTable) {
-  ResTable table;
+class ThemeTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+    ASSERT_NE(nullptr, style_assets_);
+  }
 
-  std::string system_contents;
-  ASSERT_TRUE(ReadFileFromZipToString("/system/system.apk", "resources.arsc",
-                                      &system_contents));
-  ASSERT_EQ(NO_ERROR,
-            table.add(system_contents.data(), system_contents.size()));
+ protected:
+  std::unique_ptr<ApkAssets> style_assets_;
+};
 
-  std::string app_contents;
-  ASSERT_TRUE(ReadFileFromZipToString("/basic/basic.apk", "resources.arsc",
-                                      &app_contents));
-  ASSERT_EQ(NO_ERROR, table.add(app_contents.data(), app_contents.size()));
+TEST_F(ThemeTest, EmptyTheme) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
 
-  ResTable::Theme theme1(table);
-  ASSERT_EQ(NO_ERROR, theme1.applyStyle(app::R::style::Theme_One));
-  Res_value val;
-  ASSERT_GE(theme1.getAttribute(android::R::attr::background, &val), 0);
-  ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
-  ASSERT_EQ(uint32_t(0xffff0000), val.data);
-  ASSERT_GE(theme1.getAttribute(app::R::attr::number, &val), 0);
-  ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
-  ASSERT_EQ(uint32_t(1), val.data);
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  EXPECT_EQ(0u, theme->GetChangingConfigurations());
+  EXPECT_EQ(&assetmanager, theme->GetAssetManager());
 
-  ResTable table2;
-  ASSERT_EQ(NO_ERROR,
-            table2.add(system_contents.data(), system_contents.size()));
-  ASSERT_EQ(NO_ERROR, table2.add(app_contents.data(), app_contents.size()));
+  Res_value value;
+  uint32_t flags;
+  EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags));
+}
 
-  ResTable::Theme theme2(table2);
-  ASSERT_EQ(NO_ERROR, theme2.setTo(theme1));
-  ASSERT_GE(theme2.getAttribute(android::R::attr::background, &val), 0);
-  ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
-  ASSERT_EQ(uint32_t(0xffff0000), val.data);
-  ASSERT_GE(theme2.getAttribute(app::R::attr::number, &val), 0);
-  ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
-  ASSERT_EQ(uint32_t(1), val.data);
+TEST_F(ThemeTest, SingleThemeNoParent) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(2u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, SingleThemeWithParent) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+  EXPECT_EQ(0, cookie);
+  EXPECT_EQ(std::string("string"),
+            GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data));
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // This attribute should point to an attr_indirect, so the result should be 3.
+  cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(3u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  // attr_one is still here from the base.
+  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // check for the new attr_six
+  cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(6u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // check for the old attr_five (force=true was not used).
+  cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+  EXPECT_EQ(app::R::string::string_one, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, MultipleThemesOverlaidForced) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  // attr_one is still here from the base.
+  cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // check for the new attr_six
+  cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(6u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // check for the new attr_five (force=true was used).
+  cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(5u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, CopyThemeSameAssetManager) {
+  AssetManager2 assetmanager;
+  assetmanager.SetApkAssets({style_assets_.get()});
+
+  std::unique_ptr<Theme> theme_one = assetmanager.NewTheme();
+  ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
+
+  Res_value value;
+  uint32_t flags;
+  ApkAssetsCookie cookie;
+
+  // attr_one is still here from the base.
+  cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(1u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+  // attr_six is not here.
+  EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags));
+
+  std::unique_ptr<Theme> theme_two = assetmanager.NewTheme();
+  ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
+
+  // Copy the theme to theme_one.
+  ASSERT_TRUE(theme_one->SetTo(*theme_two));
+
+  // Clear theme_two to make sure we test that there WAS a copy.
+  theme_two->Clear();
+
+  // attr_one is now not here.
+  EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+
+  // attr_six is now here because it was copied.
+  cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags);
+  ASSERT_NE(kInvalidCookie, cookie);
+  EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+  EXPECT_EQ(6u, value.data);
+  EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, FailToCopyThemeWithDifferentAssetManager) {
+  AssetManager2 assetmanager_one;
+  assetmanager_one.SetApkAssets({style_assets_.get()});
+
+  AssetManager2 assetmanager_two;
+  assetmanager_two.SetApkAssets({style_assets_.get()});
+
+  auto theme_one = assetmanager_one.NewTheme();
+  ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
+
+  auto theme_two = assetmanager_two.NewTheme();
+  ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo));
+
+  EXPECT_FALSE(theme_one->SetTo(*theme_two));
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h
index 4127aa0..68527c7 100644
--- a/libs/androidfw/tests/data/styles/R.h
+++ b/libs/androidfw/tests/data/styles/R.h
@@ -32,6 +32,7 @@
       attr_four = 0x7f010003u,
       attr_five = 0x7f010004u,
       attr_indirect = 0x7f010005u,
+      attr_six = 0x7f010006u,
     };
   };
 
@@ -45,6 +46,7 @@
     enum : uint32_t {
       StyleOne = 0x7f020000u,
       StyleTwo = 0x7f020001u,
+      StyleThree = 0x7f020002u,
     };
   };
 };
diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml
index 70c54f6..da592f8 100644
--- a/libs/androidfw/tests/data/styles/res/values/styles.xml
+++ b/libs/androidfw/tests/data/styles/res/values/styles.xml
@@ -39,6 +39,7 @@
     <public type="style" name="StyleOne" id="0x7f020000" />
     <style name="StyleOne">
         <item name="attr_one">1</item>
+        <item name="attr_two">2</item>
     </style>
 
     <public type="style" name="StyleTwo" id="0x7f020001" />
@@ -48,5 +49,14 @@
         <item name="attr_three">?attr/attr_indirect</item>
         <item name="attr_five">@string/string_one</item>
     </style>
+    
+    <public type="attr" name="attr_six" id="0x7f010006" />
+    <attr name="attr_six" />
+    
+    <public type="style" name="StyleThree" id="0x7f020002" />
+    <style name="StyleThree">
+        <item name="attr_six">6</item>
+        <item name="attr_five">5</item>
+    </style>
 
 </resources>
diff --git a/libs/androidfw/tests/data/styles/styles.apk b/libs/androidfw/tests/data/styles/styles.apk
index 6064c48..d4ccb83 100644
--- a/libs/androidfw/tests/data/styles/styles.apk
+++ b/libs/androidfw/tests/data/styles/styles.apk
Binary files differ
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 3e8e8a1..0ae50e9 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -131,7 +131,7 @@
                         mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
 
     static const mat4 identityMatrix;
-    updateLayer(false, identityMatrix.data);
+    updateLayer(false, GL_NONE, identityMatrix.data);
 
     VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
     vkLayer->updateTexture();
@@ -139,26 +139,20 @@
 
 void DeferredLayerUpdater::updateLayer(bool forceFilter, GLenum renderTarget,
         const float* textureTransform) {
-    LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
-                        "updateLayer non GL backend %x, GL %x, VK %x",
-                        mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
-
-    updateLayer(forceFilter, textureTransform);
-
-    GlLayer* glLayer = static_cast<GlLayer*>(mLayer);
-    if (renderTarget != glLayer->getRenderTarget()) {
-        glLayer->setRenderTarget(renderTarget);
-        glLayer->bindTexture();
-        glLayer->setFilter(GL_NEAREST, false, true);
-        glLayer->setWrap(GL_CLAMP_TO_EDGE, false, true);
-    }
-}
-
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform) {
     mLayer->setBlend(mBlend);
     mLayer->setForceFilter(forceFilter);
     mLayer->setSize(mWidth, mHeight);
     mLayer->getTexTransform().load(textureTransform);
+
+    if (mLayer->getApi() == Layer::Api::OpenGL) {
+        GlLayer* glLayer = static_cast<GlLayer*>(mLayer);
+        if (renderTarget != glLayer->getRenderTarget()) {
+            glLayer->setRenderTarget(renderTarget);
+            glLayer->bindTexture();
+            glLayer->setFilter(GL_NEAREST, false, true);
+            glLayer->setWrap(GL_CLAMP_TO_EDGE, false, true);
+        }
+    }
 }
 
 void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index ead8314..3814be2 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -114,7 +114,6 @@
 
     void doUpdateTexImage();
     void doUpdateVkTexImage();
-    void updateLayer(bool forceFilter, const float* textureTransform);
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 5e4a7f7..3853356 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -104,15 +104,15 @@
     }
 }
 
-bool DisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
-        std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
+bool DisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+        std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
     info.prepareTextures = info.canvasContext.pinImages(bitmapResources);
 
     for (auto&& op : children) {
         RenderNode* childNode = op->renderNode;
         info.damageAccumulator->pushTransform(&op->localMatrix);
         bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
-        childFn(childNode, info, childFunctorsNeedLayer);
+        childFn(childNode, observer, info, childFunctorsNeedLayer);
         info.damageAccumulator->popTransform();
     }
 
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index cab092f..ef0fd31 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -125,8 +125,8 @@
 
     virtual void syncContents();
     virtual void updateChildren(std::function<void(RenderNode*)> updateFn);
-    virtual bool prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
-            std::function<void(RenderNode*, TreeInfo&, bool)> childFn);
+    virtual bool prepareListAndChildren(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn);
 
 protected:
     // allocator into which all ops and LsaVector arrays allocated
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index a53a55a..1d8b021 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -180,16 +180,18 @@
         }
     }
 
-    if (!backdrop.isEmpty()) {
-        // content node translation to catch up with backdrop
-        float dx = contentDrawBounds.left - backdrop.left;
-        float dy = contentDrawBounds.top - backdrop.top;
+    if (!nodes[1]->nothingToDraw()) {
+        if (!backdrop.isEmpty()) {
+            // content node translation to catch up with backdrop
+            float dx = contentDrawBounds.left - backdrop.left;
+            float dy = contentDrawBounds.top - backdrop.top;
 
-        Rect contentLocalClip = backdrop;
-        contentLocalClip.translate(dx, dy);
-        deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
-    } else {
-        deferRenderNode(*nodes[1]);
+            Rect contentLocalClip = backdrop;
+            contentLocalClip.translate(dx, dy);
+            deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
+        } else {
+            deferRenderNode(*nodes[1]);
+        }
     }
 
     // remaining overlay nodes, simply defer
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 0702010..09e34bf 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -222,6 +222,12 @@
     return sRenderPipelineType;
 }
 
+#ifdef HWUI_GLES_WRAP_ENABLED
+void Properties::overrideRenderPipelineType(RenderPipelineType type) {
+    sRenderPipelineType = type;
+}
+#endif
+
 bool Properties::isSkiaEnabled() {
     auto renderType = getRenderPipelineType();
     return RenderPipelineType::SkiaGL == renderType
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index b4a3118..6dc0cb3 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -318,11 +318,15 @@
     // any overhead they add
     static bool filterOutTestOverhead;
 
+    // Used for testing only to change the render pipeline.
+#ifdef HWUI_GLES_WRAP_ENABLED
+    static void overrideRenderPipelineType(RenderPipelineType);
+#endif
+
 private:
     static ProfileType sProfileType;
     static bool sDisableProfileBars;
     static RenderPipelineType sRenderPipelineType;
-
 }; // class Caches
 
 }; // namespace uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index a5443d9..7d8f046 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -22,6 +22,7 @@
 #include "OpDumper.h"
 #include "RecordedOp.h"
 #include "TreeInfo.h"
+#include "utils/FatVector.h"
 #include "utils/MathUtils.h"
 #include "utils/StringUtils.h"
 #include "utils/TraceUtils.h"
@@ -39,6 +40,20 @@
 namespace android {
 namespace uirenderer {
 
+// Used for tree mutations that are purely destructive.
+// Generic tree mutations should use MarkAndSweepObserver instead
+class ImmediateRemoved : public TreeObserver {
+public:
+    explicit ImmediateRemoved(TreeInfo* info) : mTreeInfo(info) {}
+
+    void onMaybeRemovedFromTree(RenderNode* node) override {
+        node->onRemovedFromTree(mTreeInfo);
+    }
+
+private:
+    TreeInfo* mTreeInfo;
+};
+
 RenderNode::RenderNode()
         : mDirtyPropertyFields(0)
         , mNeedsDisplayListSync(false)
@@ -49,20 +64,17 @@
 }
 
 RenderNode::~RenderNode() {
-    deleteDisplayList(nullptr);
+    ImmediateRemoved observer(nullptr);
+    deleteDisplayList(observer);
     delete mStagingDisplayList;
     LOG_ALWAYS_FATAL_IF(hasLayer(), "layer missed detachment!");
 }
 
-void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* observer) {
+void RenderNode::setStagingDisplayList(DisplayList* displayList) {
+    mValid = (displayList != nullptr);
     mNeedsDisplayListSync = true;
     delete mStagingDisplayList;
     mStagingDisplayList = displayList;
-    // If mParentCount == 0 we are the sole reference to this RenderNode,
-    // so immediately free the old display list
-    if (!mParentCount && !mStagingDisplayList) {
-        deleteDisplayList(observer);
-    }
 }
 
 /**
@@ -187,12 +199,13 @@
 void RenderNode::prepareTree(TreeInfo& info) {
     ATRACE_CALL();
     LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
+    MarkAndSweepRemoved observer(&info);
 
     // The OpenGL renderer reserves the stencil buffer for overdraw debugging.  Functors
     // will need to be drawn in a layer.
     bool functorsNeedLayer = Properties::debugOverdraw && !Properties::isSkiaEnabled();
 
-    prepareTreeImpl(info, functorsNeedLayer);
+    prepareTreeImpl(observer, info, functorsNeedLayer);
 }
 
 void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
@@ -283,7 +296,7 @@
  * While traversing down the tree, functorsNeedLayer flag is set to true if anything that uses the
  * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
  */
-void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
+void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
     info.damageAccumulator->pushTransform(this);
 
     if (info.mode == TreeInfo::MODE_FULL) {
@@ -309,14 +322,14 @@
 
     prepareLayer(info, animatorDirtyMask);
     if (info.mode == TreeInfo::MODE_FULL) {
-        pushStagingDisplayListChanges(info);
+        pushStagingDisplayListChanges(observer, info);
     }
 
     if (mDisplayList) {
         info.out.hasFunctors |= mDisplayList->hasFunctor();
-        bool isDirty = mDisplayList->prepareListAndChildren(info, childFunctorsNeedLayer,
-                [](RenderNode* child, TreeInfo& info, bool functorsNeedLayer) {
-            child->prepareTreeImpl(info, functorsNeedLayer);
+        bool isDirty = mDisplayList->prepareListAndChildren(observer, info, childFunctorsNeedLayer,
+                [](RenderNode* child, TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
+            child->prepareTreeImpl(observer, info, functorsNeedLayer);
         });
         if (isDirty) {
             damageSelf(info);
@@ -353,7 +366,7 @@
     }
 }
 
-void RenderNode::syncDisplayList(TreeInfo* info) {
+void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
     // Make sure we inc first so that we don't fluctuate between 0 and 1,
     // which would thrash the layer cache
     if (mStagingDisplayList) {
@@ -361,7 +374,7 @@
             child->incParentRefCount();
         });
     }
-    deleteDisplayList(info ? info->observer : nullptr, info);
+    deleteDisplayList(observer, info);
     mDisplayList = mStagingDisplayList;
     mStagingDisplayList = nullptr;
     if (mDisplayList) {
@@ -369,20 +382,20 @@
     }
 }
 
-void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
+void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
     if (mNeedsDisplayListSync) {
         mNeedsDisplayListSync = false;
         // Damage with the old display list first then the new one to catch any
         // changes in isRenderable or, in the future, bounds
         damageSelf(info);
-        syncDisplayList(&info);
+        syncDisplayList(observer, &info);
         damageSelf(info);
     }
 }
 
-void RenderNode::deleteDisplayList(TreeObserver* observer, TreeInfo* info) {
+void RenderNode::deleteDisplayList(TreeObserver& observer, TreeInfo* info) {
     if (mDisplayList) {
-        mDisplayList->updateChildren([observer, info](RenderNode* child) {
+        mDisplayList->updateChildren([&observer, info](RenderNode* child) {
             child->decParentRefCount(observer, info);
         });
         if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) {
@@ -392,38 +405,53 @@
     mDisplayList = nullptr;
 }
 
-void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) {
+void RenderNode::destroyHardwareResources(TreeInfo* info) {
+    ImmediateRemoved observer(info);
+    destroyHardwareResourcesImpl(observer, info);
+}
+
+void RenderNode::destroyHardwareResourcesImpl(TreeObserver& observer, TreeInfo* info) {
     if (hasLayer()) {
         renderthread::CanvasContext::destroyLayer(this);
     }
     if (mDisplayList) {
-        mDisplayList->updateChildren([observer, info](RenderNode* child) {
-            child->destroyHardwareResources(observer, info);
+        mDisplayList->updateChildren([&observer, info](RenderNode* child) {
+            child->destroyHardwareResourcesImpl(observer, info);
         });
-        if (mNeedsDisplayListSync) {
-            // Next prepare tree we are going to push a new display list, so we can
-            // drop our current one now
-            deleteDisplayList(observer, info);
+        setStagingDisplayList(nullptr);
+        deleteDisplayList(observer, info);
+    }
+}
+
+void RenderNode::destroyLayers() {
+    if (hasLayer()) {
+        renderthread::CanvasContext::destroyLayer(this);
+    }
+    if (mDisplayList) {
+        mDisplayList->updateChildren([](RenderNode* child) {
+            child->destroyLayers();
+        });
+    }
+}
+
+void RenderNode::decParentRefCount(TreeObserver& observer, TreeInfo* info) {
+    LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
+    mParentCount--;
+    if (!mParentCount) {
+        observer.onMaybeRemovedFromTree(this);
+        if (CC_UNLIKELY(mPositionListener.get())) {
+            mPositionListener->onPositionLost(*this, info);
         }
     }
 }
 
-void RenderNode::decParentRefCount(TreeObserver* observer, TreeInfo* info) {
-    LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
-    mParentCount--;
-    if (!mParentCount) {
-        if (observer) {
-            observer->onMaybeRemovedFromTree(this);
-        }
-        if (CC_UNLIKELY(mPositionListener.get())) {
-            mPositionListener->onPositionLost(*this, info);
-        }
-        // If a child of ours is being attached to our parent then this will incorrectly
-        // destroy its hardware resources. However, this situation is highly unlikely
-        // and the failure is "just" that the layer is re-created, so this should
-        // be safe enough
-        destroyHardwareResources(observer, info);
-    }
+void RenderNode::onRemovedFromTree(TreeInfo* info) {
+    destroyHardwareResources(info);
+}
+
+void RenderNode::clearRoot() {
+    ImmediateRemoved observer(nullptr);
+    decParentRefCount(observer);
 }
 
 /**
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index b8964f0..14a664c 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -34,6 +34,7 @@
 #include "RenderProperties.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaLayer.h"
+#include "utils/FatVector.h"
 
 #include <vector>
 
@@ -101,7 +102,7 @@
         kReplayFlag_ClipChildren = 0x1
     };
 
-    ANDROID_API void setStagingDisplayList(DisplayList* newData, TreeObserver* observer);
+    ANDROID_API void setStagingDisplayList(DisplayList* newData);
 
     void computeOrdering();
 
@@ -164,6 +165,10 @@
         return mStagingProperties;
     }
 
+    bool isValid() {
+        return mValid;
+    }
+
     int getWidth() const {
         return properties().getWidth();
     }
@@ -173,7 +178,8 @@
     }
 
     ANDROID_API virtual void prepareTree(TreeInfo& info);
-    void destroyHardwareResources(TreeObserver* observer, TreeInfo* info = nullptr);
+    void destroyHardwareResources(TreeInfo* info = nullptr);
+    void destroyLayers();
 
     // UI thread only!
     ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
@@ -232,24 +238,35 @@
         return mParentCount;
     }
 
+    void onRemovedFromTree(TreeInfo* info);
+
+    // Called by CanvasContext to promote a RenderNode to be a root node
+    void makeRoot() {
+        incParentRefCount();
+    }
+
+    // Called by CanvasContext when it drops a RenderNode from being a root node
+    void clearRoot();
+
 private:
     void computeOrderingImpl(RenderNodeOp* opState,
             std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
             const mat4* transformFromProjectionSurface);
 
     void syncProperties();
-    void syncDisplayList(TreeInfo* info);
+    void syncDisplayList(TreeObserver& observer, TreeInfo* info);
 
-    void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
+    void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer);
     void pushStagingPropertiesChanges(TreeInfo& info);
-    void pushStagingDisplayListChanges(TreeInfo& info);
+    void pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info);
     void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
     void pushLayerUpdate(TreeInfo& info);
-    void deleteDisplayList(TreeObserver* observer, TreeInfo* info = nullptr);
+    void deleteDisplayList(TreeObserver& observer, TreeInfo* info = nullptr);
+    void destroyHardwareResourcesImpl(TreeObserver& observer, TreeInfo* info = nullptr);
     void damageSelf(TreeInfo& info);
 
     void incParentRefCount() { mParentCount++; }
-    void decParentRefCount(TreeObserver* observer, TreeInfo* info = nullptr);
+    void decParentRefCount(TreeObserver& observer, TreeInfo* info = nullptr);
     void output(std::ostream& output, uint32_t level);
 
     String8 mName;
@@ -259,6 +276,10 @@
     RenderProperties mProperties;
     RenderProperties mStagingProperties;
 
+    // Owned by UI. Set when DL is set, cleared when DL cleared or when node detached
+    // (likely by parent re-record/removal)
+    bool mValid = false;
+
     bool mNeedsDisplayListSync;
     // WARNING: Do not delete this directly, you must go through deleteDisplayList()!
     DisplayList* mDisplayList;
@@ -361,5 +382,28 @@
     std::unique_ptr<skiapipeline::SkiaLayer> mSkiaLayer;
 }; // class RenderNode
 
+class MarkAndSweepRemoved : public TreeObserver {
+PREVENT_COPY_AND_ASSIGN(MarkAndSweepRemoved);
+
+public:
+    explicit MarkAndSweepRemoved(TreeInfo* info) : mTreeInfo(info) {}
+
+    void onMaybeRemovedFromTree(RenderNode* node) override {
+        mMarked.emplace_back(node);
+    }
+
+    ~MarkAndSweepRemoved() {
+        for (auto& node : mMarked) {
+            if (!node->hasParents()) {
+                node->onRemovedFromTree(mTreeInfo);
+            }
+        }
+    }
+
+private:
+    FatVector<sp<RenderNode>, 10> mMarked;
+    TreeInfo* mTreeInfo;
+};
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 89e2a01..e54bc36 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -367,7 +367,7 @@
 // (see https://code.google.com/p/skia/issues/detail?id=1303)
 bool SkiaCanvas::getClipBounds(SkRect* outRect) const {
     SkIRect ibounds;
-    if (!mCanvas->getClipDeviceBounds(&ibounds)) {
+    if (!mCanvas->getDeviceClipBounds(&ibounds)) {
         return false;
     }
 
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 749efdd..c6fbe2b 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -47,9 +47,9 @@
     // Due to the unordered nature of tree pushes, once prepareTree
     // is finished it is possible that the node was "resurrected" and has
     // a non-zero parent count.
-    virtual void onMaybeRemovedFromTree(RenderNode* node) {}
+    virtual void onMaybeRemovedFromTree(RenderNode* node) = 0;
 protected:
-    ~TreeObserver() {}
+    virtual ~TreeObserver() {}
 };
 
 // This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
@@ -91,10 +91,6 @@
     LayerUpdateQueue* layerUpdateQueue = nullptr;
     ErrorHandler* errorHandler = nullptr;
 
-    // Optional, may be nullptr. Used to allow things to observe interesting
-    // tree state changes
-    TreeObserver* observer = nullptr;
-
     int32_t windowInsetLeft = 0;
     int32_t windowInsetTop = 0;
     bool updateWindowPositions = false;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 6a00379..956f66e 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -17,8 +17,7 @@
 #include "MinikinSkia.h"
 
 #include <log/log.h>
-#include <SkFontDescriptor.h>
-#include <SkFontMgr.h>
+
 #include <SkPaint.h>
 #include <SkTypeface.h>
 
@@ -87,28 +86,6 @@
     return mTtcIndex;
 }
 
-minikin::MinikinFont* MinikinFontSkia::createFontWithVariation(
-        const std::vector<minikin::FontVariation>& variations) const {
-    SkFontMgr::FontParameters params;
-
-    int ttcIndex;
-    SkStreamAsset* stream = mTypeface->openStream(&ttcIndex);
-    LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed");
-
-    params.setCollectionIndex(ttcIndex);
-    std::vector<SkFontMgr::FontParameters::Axis> skAxes;
-    skAxes.resize(variations.size());
-    for (size_t i = 0; i < variations.size(); i++) {
-        skAxes[i].fTag = variations[i].axisTag;
-        skAxes[i].fStyleValue = SkFloatToScalar(variations[i].value);
-    }
-    params.setAxes(skAxes.data(), skAxes.size());
-    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
-    sk_sp<SkTypeface> face(fm->createFromStream(stream, params));
-
-    return new MinikinFontSkia(std::move(face), mFontData, mFontSize, ttcIndex);
-}
-
 uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) {
     uint32_t flags = paint->getFlags();
     SkPaint::Hinting hinting = paint->getHinting();
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index 249b0cb..3ee916c 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -44,8 +44,6 @@
     const void* GetFontData() const;
     size_t GetFontSize() const;
     int GetFontIndex() const;
-    minikin::MinikinFont* createFontWithVariation(
-            const std::vector<minikin::FontVariation>&) const;
 
     static uint32_t packPaintFlags(const SkPaint* paint);
     static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags);
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index b69b0cb..9041b44 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -62,7 +62,7 @@
 Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) {
     Typeface* resolvedFace = Typeface::resolveDefault(src);
     Typeface* result = new Typeface;
-    if (result != nullptr) {
+    if (result != 0) {
         result->fFontCollection = resolvedFace->fFontCollection;
         result->fFontCollection->Ref();
         result->fSkiaStyle = style;
@@ -72,30 +72,10 @@
     return result;
 }
 
-Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src,
-        const std::vector<minikin::FontVariation>& variations) {
-    Typeface* resolvedFace = Typeface::resolveDefault(src);
-    Typeface* result = new Typeface();
-    if (result != nullptr) {
-        result->fFontCollection =
-                resolvedFace->fFontCollection->createCollectionWithVariation(variations);
-        if (result->fFontCollection == nullptr) {
-            // None of passed axes are supported by this collection.
-            // So we will reuse the same collection with incrementing reference count.
-            result->fFontCollection = resolvedFace->fFontCollection;
-            result->fFontCollection->Ref();
-        }
-        result->fSkiaStyle = resolvedFace->fSkiaStyle;
-        result->fBaseWeight = resolvedFace->fBaseWeight;
-        resolveStyle(result);
-    }
-    return result;
-}
-
 Typeface* Typeface::createWeightAlias(Typeface* src, int weight) {
     Typeface* resolvedFace = Typeface::resolveDefault(src);
     Typeface* result = new Typeface;
-    if (result != nullptr) {
+    if (result != 0) {
         result->fFontCollection = resolvedFace->fFontCollection;
         result->fFontCollection->Ref();
         result->fSkiaStyle = resolvedFace->fSkiaStyle;
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index 4392ebc..1be630c 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -43,9 +43,6 @@
 
     static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style);
 
-    static Typeface* createFromTypefaceWithVariation(Typeface* src,
-            const std::vector<minikin::FontVariation>& variations);
-
     static Typeface* createWeightAlias(Typeface* src, int baseweight);
 
     static Typeface* createFromFamilies(const std::vector<minikin::FontFamily*>& families);
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 419c8a9..ea302a1 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -19,7 +19,6 @@
 #include "RenderNode.h"
 #include "SkClipStack.h"
 #include <private/hwui/DrawGlInfo.h>
-#include <SkPath.h>
 #include <GrContext.h>
 
 namespace android {
@@ -60,8 +59,7 @@
     SkImageInfo canvasInfo = canvas->imageInfo();
     SkMatrix44 mat4(canvas->getTotalMatrix());
 
-    SkIRect ibounds;
-    canvas->getClipDeviceBounds(&ibounds);
+    SkIRect ibounds = canvas->getDeviceClipBounds();
 
     DrawGlInfo info;
     info.clipLeft = ibounds.fLeft;
@@ -76,9 +74,7 @@
 
     //apply a simple clip with a scissor or a complex clip with a stencil
     SkRegion clipRegion;
-    SkPath path;
-    canvas->getClipStack()->asPath(&path);
-    clipRegion.setPath(path, SkRegion(ibounds));
+    canvas->temporary_internal_getRgnClip(&clipRegion);
     if (CC_UNLIKELY(clipRegion.isComplex())) {
         //It is only a temporary solution to use a scissor to draw the stencil.
         //There is a bug 31489986 to implement efficiently non-rectangular clips.
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index bf39dad..012c948 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -37,9 +37,9 @@
 public:
     GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
             : mFunctor(functor)
-            , mListener(listener) {
-        canvas->getClipBounds(&mBounds);
-    }
+            , mListener(listener)
+            , mBounds(canvas->getLocalClipBounds())
+    {}
     virtual ~GLFunctorDrawable();
 
     void syncFunctor() const;
@@ -51,7 +51,7 @@
  private:
      Functor* mFunctor;
      sp<GlFunctorLifecycleListener> mListener;
-     SkRect mBounds;
+     const SkRect mBounds;
 };
 
 }; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 9db8cd3..36d02ecb 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -51,8 +51,9 @@
     }
 }
 
-bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
-        std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
+bool SkiaDisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& info,
+        bool functorsNeedLayer,
+        std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
     // If the prepare tree is triggered by the UI thread and no previous call to
     // pinImages has failed then we must pin all mutable images in the GPU cache
     // until the next UI thread draw.
@@ -74,7 +75,7 @@
         info.damageAccumulator->pushTransform(&mat4);
         // TODO: a layer is needed if the canvas is rotated or has a non-rect clip
         info.hasBackwardProjectedNodes = false;
-        childFn(childNode, info, functorsNeedLayer);
+        childFn(childNode, observer, info, functorsNeedLayer);
         hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes;
         info.damageAccumulator->popTransform();
     }
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index ff86fd1..2a01330 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -113,8 +113,8 @@
      *       to subclass from DisplayList
      */
 
-    bool prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
-            std::function<void(RenderNode*, TreeInfo&, bool)> childFn) override;
+    bool prepareListAndChildren(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) override;
 
     /**
      *  Calls the provided function once for each child of this DisplayList
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1b3bf96..a53e5e0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -146,21 +146,38 @@
         , mProfiler(mFrames)
         , mContentDrawBounds(0, 0, 0, 0)
         , mRenderPipeline(std::move(renderPipeline)) {
+    rootRenderNode->makeRoot();
     mRenderNodes.emplace_back(rootRenderNode);
     mRenderThread.renderState().registerCanvasContext(this);
     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
 }
 
 CanvasContext::~CanvasContext() {
-    destroy(nullptr);
+    destroy();
     mRenderThread.renderState().unregisterCanvasContext(this);
+    for (auto& node : mRenderNodes) {
+        node->clearRoot();
+    }
+    mRenderNodes.clear();
 }
 
-void CanvasContext::destroy(TreeObserver* observer) {
+void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
+    int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
+    node->makeRoot();
+    mRenderNodes.emplace(mRenderNodes.begin() + pos, node);
+}
+
+void CanvasContext::removeRenderNode(RenderNode* node) {
+    node->clearRoot();
+    mRenderNodes.erase(std::remove(mRenderNodes.begin(), mRenderNodes.end(), node),
+            mRenderNodes.end());
+}
+
+void CanvasContext::destroy() {
     stopDrawing();
     setSurface(nullptr);
-    freePrefetchedLayers(observer);
-    destroyHardwareResources(observer);
+    freePrefetchedLayers();
+    destroyHardwareResources();
     mAnimationContext->destroy();
 }
 
@@ -320,7 +337,7 @@
     mAnimationContext->runRemainingAnimations(info);
     GL_CHECKPOINT(MODERATE);
 
-    freePrefetchedLayers(info.observer);
+    freePrefetchedLayers();
     GL_CHECKPOINT(MODERATE);
 
     mIsDirty = true;
@@ -504,19 +521,19 @@
     }
 }
 
-void CanvasContext::freePrefetchedLayers(TreeObserver* observer) {
+void CanvasContext::freePrefetchedLayers() {
     if (mPrefetchedLayers.size()) {
         for (auto& node : mPrefetchedLayers) {
             ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...",
                     node->getName());
-            node->destroyHardwareResources(observer);
-            node->decStrong(observer);
+            node->destroyLayers();
+            node->decStrong(nullptr);
         }
         mPrefetchedLayers.clear();
     }
 }
 
-void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) {
+void CanvasContext::buildLayer(RenderNode* node) {
     ATRACE_CALL();
     if (!mRenderPipeline->isContextReady()) return;
 
@@ -525,7 +542,6 @@
 
     TreeInfo info(TreeInfo::MODE_FULL, *this);
     info.damageAccumulator = &mDamageAccumulator;
-    info.observer = observer;
     info.layerUpdateQueue = &mLayerUpdateQueue;
     info.runAnimations = false;
     node->prepareTree(info);
@@ -545,12 +561,12 @@
     return mRenderPipeline->copyLayerInto(layer, bitmap);
 }
 
-void CanvasContext::destroyHardwareResources(TreeObserver* observer) {
+void CanvasContext::destroyHardwareResources() {
     stopDrawing();
     if (mRenderPipeline->isContextReady()) {
-        freePrefetchedLayers(observer);
+        freePrefetchedLayers();
         for (const sp<RenderNode>& node : mRenderNodes) {
-            node->destroyHardwareResources(observer);
+            node->destroyHardwareResources();
         }
         mRenderPipeline->onDestroyHardwareResources();
     }
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0174b86..aa01caa 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -132,17 +132,17 @@
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
             int64_t syncQueued, RenderNode* target);
     void draw();
-    void destroy(TreeObserver* observer);
+    void destroy();
 
     // IFrameCallback, Choreographer-driven frame callback entry point
     virtual void doFrame() override;
     void prepareAndDraw(RenderNode* node);
 
-    void buildLayer(RenderNode* node, TreeObserver* observer);
+    void buildLayer(RenderNode* node);
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
     void markLayerInUse(RenderNode* node);
 
-    void destroyHardwareResources(TreeObserver* observer);
+    void destroyHardwareResources();
     static void trimMemory(RenderThread& thread, int level);
 
     DeferredLayerUpdater* createTextureLayer();
@@ -160,15 +160,8 @@
 
     void serializeDisplayListTree();
 
-    void addRenderNode(RenderNode* node, bool placeFront) {
-        int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
-        mRenderNodes.emplace(mRenderNodes.begin() + pos, node);
-    }
-
-    void removeRenderNode(RenderNode* node) {
-        mRenderNodes.erase(std::remove(mRenderNodes.begin(), mRenderNodes.end(), node),
-                mRenderNodes.end());
-    }
+    void addRenderNode(RenderNode* node, bool placeFront);
+    void removeRenderNode(RenderNode* node);
 
     void setContentDrawBounds(int left, int top, int right, int bottom) {
         mContentDrawBounds.set(left, top, right, bottom);
@@ -213,7 +206,7 @@
 
     void setSurface(Surface* window);
 
-    void freePrefetchedLayers(TreeObserver* observer);
+    void freePrefetchedLayers();
 
     bool isSwapChainStuffed();
 
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 4ff54a5..7d641d3 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -65,12 +65,11 @@
     }
 }
 
-int DrawFrameTask::drawFrame(TreeObserver* observer) {
+int DrawFrameTask::drawFrame() {
     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
     mSyncResult = SyncResult::OK;
     mSyncQueued = systemTime(CLOCK_MONOTONIC);
-    mObserver = observer;
     postAndWait();
 
     return mSyncResult;
@@ -89,7 +88,6 @@
     bool canDrawThisFrame;
     {
         TreeInfo info(TreeInfo::MODE_FULL, *mContext);
-        info.observer = mObserver;
         canUnblockUiThread = syncFrameState(info);
         canDrawThisFrame = info.out.canDrawThisFrame;
     }
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index c02d376..fb48062 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -65,7 +65,7 @@
     void pushLayerUpdate(DeferredLayerUpdater* layer);
     void removeLayerUpdate(DeferredLayerUpdater* layer);
 
-    int drawFrame(TreeObserver* observer);
+    int drawFrame();
 
     int64_t* frameInfo() { return mFrameInfo; }
 
@@ -90,7 +90,6 @@
 
     int mSyncResult;
     int64_t mSyncQueued;
-    TreeObserver* mObserver;
 
     int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
 };
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 022e871..fb79272 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -230,19 +230,18 @@
     return mDrawFrameTask.frameInfo();
 }
 
-int RenderProxy::syncAndDrawFrame(TreeObserver* observer) {
-    return mDrawFrameTask.drawFrame(observer);
+int RenderProxy::syncAndDrawFrame() {
+    return mDrawFrameTask.drawFrame();
 }
 
-CREATE_BRIDGE2(destroy, CanvasContext* context, TreeObserver* observer) {
-    args->context->destroy(args->observer);
+CREATE_BRIDGE1(destroy, CanvasContext* context) {
+    args->context->destroy();
     return nullptr;
 }
 
-void RenderProxy::destroy(TreeObserver* observer) {
+void RenderProxy::destroy() {
     SETUP_TASK(destroy);
     args->context = mContext;
-    args->observer = observer;
     // destroyCanvasAndSurface() needs a fence as when it returns the
     // underlying BufferQueue is going to be released from under
     // the render thread.
@@ -282,16 +281,15 @@
     return layer;
 }
 
-CREATE_BRIDGE3(buildLayer, CanvasContext* context, RenderNode* node, TreeObserver* observer) {
-    args->context->buildLayer(args->node, args->observer);
+CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) {
+    args->context->buildLayer(args->node);
     return nullptr;
 }
 
-void RenderProxy::buildLayer(RenderNode* node, TreeObserver* observer) {
+void RenderProxy::buildLayer(RenderNode* node) {
     SETUP_TASK(buildLayer);
     args->context = mContext;
     args->node = node;
-    args->observer = observer;
     postAndWait(task);
 }
 
@@ -328,15 +326,14 @@
     postAndWait(task);
 }
 
-CREATE_BRIDGE2(destroyHardwareResources, CanvasContext* context, TreeObserver* observer) {
-    args->context->destroyHardwareResources(args->observer);
+CREATE_BRIDGE1(destroyHardwareResources, CanvasContext* context) {
+    args->context->destroyHardwareResources();
     return nullptr;
 }
 
-void RenderProxy::destroyHardwareResources(TreeObserver* observer) {
+void RenderProxy::destroyHardwareResources() {
     SETUP_TASK(destroyHardwareResources);
     args->context = mContext;
-    args->observer = observer;
     postAndWait(task);
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 44a5a14..1629090 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -44,7 +44,6 @@
 class DisplayList;
 class Layer;
 class Rect;
-class TreeObserver;
 
 namespace renderthread {
 
@@ -87,19 +86,19 @@
     ANDROID_API void setLightCenter(const Vector3& lightCenter);
     ANDROID_API void setOpaque(bool opaque);
     ANDROID_API int64_t* frameInfo();
-    ANDROID_API int syncAndDrawFrame(TreeObserver* observer);
-    ANDROID_API void destroy(TreeObserver* observer);
+    ANDROID_API int syncAndDrawFrame();
+    ANDROID_API void destroy();
 
     ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
 
     ANDROID_API DeferredLayerUpdater* createTextureLayer();
-    ANDROID_API void buildLayer(RenderNode* node, TreeObserver* observer);
+    ANDROID_API void buildLayer(RenderNode* node);
     ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap);
     ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer);
     ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer);
     ANDROID_API void detachSurfaceTexture(DeferredLayerUpdater* layer);
 
-    ANDROID_API void destroyHardwareResources(TreeObserver* observer);
+    ANDROID_API void destroyHardwareResources();
     ANDROID_API static void trimMemory(int level);
     ANDROID_API static void overrideProperty(const char* name, const char* value);
 
diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp
index 6d2e8599..38c4848 100644
--- a/libs/hwui/tests/common/TestListViewSceneBase.cpp
+++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp
@@ -69,7 +69,7 @@
         // draw it to parent DisplayList
         canvas->drawRenderNode(mListItems[ci].get());
     }
-    mListView->setStagingDisplayList(canvas->finishRecording(), nullptr);
+    mListView->setStagingDisplayList(canvas->finishRecording());
 }
 
 } // namespace test
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 5f6bcb3..0916d72 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -21,6 +21,9 @@
 
 #include <renderthread/EglManager.h>
 #include <renderthread/OpenGLPipeline.h>
+#include <pipeline/skia/SkiaOpenGLPipeline.h>
+#include <pipeline/skia/SkiaVulkanPipeline.h>
+#include <renderthread/VulkanManager.h>
 #include <utils/Unicode.h>
 #include <SkClipStack.h>
 
@@ -47,10 +50,24 @@
 }
 
 sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
+        renderthread::RenderThread& renderThread) {
+    android::uirenderer::renderthread::IRenderPipeline* pipeline;
+    if (Properties::getRenderPipelineType() == RenderPipelineType::OpenGL) {
+        pipeline = new renderthread::OpenGLPipeline(renderThread);
+    } else if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+        pipeline = new skiapipeline::SkiaOpenGLPipeline(renderThread);
+    } else {
+        pipeline = new skiapipeline::SkiaVulkanPipeline(renderThread);
+    }
+    sp<DeferredLayerUpdater> layerUpdater = pipeline->createTextureLayer();
+    delete pipeline;
+    return layerUpdater;
+}
+
+sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
         renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
         const SkMatrix& transform) {
-    renderthread::OpenGLPipeline pipeline(renderThread);
-    sp<DeferredLayerUpdater> layerUpdater = pipeline.createTextureLayer();
+    sp<DeferredLayerUpdater> layerUpdater = createTextureLayerUpdater(renderThread);
     layerUpdater->backingLayer()->getTransform().load(transform);
     layerUpdater->setSize(width, height);
     layerUpdater->setTransform(&transform);
@@ -111,12 +128,20 @@
 void TestUtils::TestTask::run() {
     // RenderState only valid once RenderThread is running, so queried here
     renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance();
-    renderThread.eglManager().initialize();
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        renderThread.vulkanManager().initialize();
+    } else {
+        renderThread.eglManager().initialize();
+    }
 
     rtCallback(renderThread);
 
-    renderThread.renderState().flush(Caches::FlushMode::Full);
-    renderThread.eglManager().destroy();
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        renderThread.vulkanManager().destroy();
+    } else {
+        renderThread.renderState().flush(Caches::FlushMode::Full);
+        renderThread.eglManager().destroy();
+    }
 }
 
 std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
@@ -168,10 +193,7 @@
 }
 
 SkRect TestUtils::getClipBounds(const SkCanvas* canvas) {
-    SkClipStack::BoundsType boundType;
-    SkRect clipBounds;
-    canvas->getClipStack()->getBounds(&clipBounds, &boundType);
-    return clipBounds;
+    return SkRect::Make(canvas->getDeviceClipBounds());
 }
 
 SkRect TestUtils::getLocalClipBounds(const SkCanvas* canvas) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 80cbb24..16bc6f7 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -19,6 +19,7 @@
 #include <DeviceInfo.h>
 #include <DisplayList.h>
 #include <Matrix.h>
+#include <Properties.h>
 #include <Rect.h>
 #include <RenderNode.h>
 #include <hwui/Bitmap.h>
@@ -51,6 +52,31 @@
         } else { \
             ADD_FAILURE() << "ClipState not a rect"; \
         }
+
+#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
+    TEST(test_case_name, test_name##_##pipeline) { \
+        RenderPipelineType oldType = Properties::getRenderPipelineType(); \
+        Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \
+        functionCall; \
+        Properties::overrideRenderPipelineType(oldType); \
+    };
+
+/**
+ * Like gtests' TEST, but only runs with the OpenGL RenderPipelineType
+ */
+#define OPENGL_PIPELINE_TEST(test_case_name, test_name) \
+    class test_case_name##_##test_name##_HwuiTest { \
+    public: \
+        static void doTheThing(); \
+    }; \
+    INNER_PIPELINE_TEST(test_case_name, test_name, OpenGL, \
+            test_case_name##_##test_name##_HwuiTest::doTheThing()) \
+    void test_case_name##_##test_name##_HwuiTest::doTheThing()
+
+#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
+    INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
+            TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing))
+
 /**
  * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
  * (for e.g. accessing its RenderState)
@@ -60,9 +86,32 @@
     public: \
         static void doTheThing(renderthread::RenderThread& renderThread); \
     }; \
-    TEST(test_case_name, test_name) { \
-        TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \
+    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
+
+/**
+ * Like RENDERTHREAD_TEST, but only runs with the OpenGL RenderPipelineType
+ */
+#define RENDERTHREAD_OPENGL_PIPELINE_TEST(test_case_name, test_name) \
+    class test_case_name##_##test_name##_RenderThreadTest { \
+    public: \
+        static void doTheThing(renderthread::RenderThread& renderThread); \
     }; \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
+    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
+
+/**
+ * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
+ */
+#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \
+    class test_case_name##_##test_name##_RenderThreadTest { \
+    public: \
+        static void doTheThing(renderthread::RenderThread& renderThread); \
+    }; \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \
     void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
 
 /**
@@ -107,6 +156,11 @@
         int* mSignal;
     };
 
+    class MockTreeObserver : public TreeObserver {
+    public:
+        virtual void onMaybeRemovedFromTree(RenderNode* node) {}
+    };
+
     static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
         for (int i = 0; i < 16; i++) {
             if (!MathUtils::areEqual(a[i], b[i])) {
@@ -137,6 +191,9 @@
     }
 
     static sp<DeferredLayerUpdater> createTextureLayerUpdater(
+            renderthread::RenderThread& renderThread);
+
+    static sp<DeferredLayerUpdater> createTextureLayerUpdater(
             renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
             const SkMatrix& transform);
 
@@ -163,7 +220,7 @@
             std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(props.getWidth(),
                     props.getHeight()));
             setup(props, *canvas.get());
-            node->setStagingDisplayList(canvas->finishRecording(), nullptr);
+            node->setStagingDisplayList(canvas->finishRecording());
         }
         node->setPropertyFieldsDirty(0xFFFFFFFF);
         return node;
@@ -184,7 +241,7 @@
         if (setup) {
             RecordingCanvasType canvas(props.getWidth(), props.getHeight());
             setup(props, canvas);
-            node->setStagingDisplayList(canvas.finishRecording(), nullptr);
+            node->setStagingDisplayList(canvas.finishRecording());
         }
         node->setPropertyFieldsDirty(0xFFFFFFFF);
         return node;
@@ -195,7 +252,7 @@
        std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
             node.stagingProperties().getWidth(), node.stagingProperties().getHeight()));
        contentCallback(*canvas.get());
-       node.setStagingDisplayList(canvas->finishRecording(), nullptr);
+       node.setStagingDisplayList(canvas->finishRecording());
     }
 
     static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom,
@@ -213,14 +270,14 @@
         RenderProperties& props = node->mutateStagingProperties();
         props.setLeftTopRightBottom(left, top, right, bottom);
         if (displayList) {
-            node->setStagingDisplayList(displayList, nullptr);
+            node->setStagingDisplayList(displayList);
         }
         if (setup) {
             std::unique_ptr<skiapipeline::SkiaRecordingCanvas> canvas(
                 new skiapipeline::SkiaRecordingCanvas(nullptr,
                 props.getWidth(), props.getHeight()));
             setup(props, *canvas.get());
-            node->setStagingDisplayList(canvas->finishRecording(), nullptr);
+            node->setStagingDisplayList(canvas->finishRecording());
         }
         node->setPropertyFieldsDirty(0xFFFFFFFF);
         TestUtils::syncHierarchyPropertiesAndDisplayList(node);
@@ -298,8 +355,9 @@
 
 private:
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
+        MarkAndSweepRemoved observer(nullptr);
         node->syncProperties();
-        node->syncDisplayList(nullptr);
+        node->syncDisplayList(observer, nullptr);
         auto displayList = node->getDisplayList();
         if (displayList) {
             for (auto&& childOp : displayList->getChildren()) {
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index c0d9450..5b685bb 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -60,6 +60,6 @@
                     0, 100 * (i + 2), minikin::kBidi_Force_LTR, paint, nullptr);
         }
 
-        container->setStagingDisplayList(canvas->finishRecording(), nullptr);
+        container->setStagingDisplayList(canvas->finishRecording());
     }
 };
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 396e896..f8d6397 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -151,7 +151,7 @@
         testContext.waitForVsync();
         nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
         UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
-        proxy->syncAndDrawFrame(nullptr);
+        proxy->syncAndDrawFrame();
     }
 
     proxy->resetProfileInfo();
@@ -167,7 +167,7 @@
             ATRACE_NAME("UI-Draw Frame");
             UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
             scene->doFrame(i);
-            proxy->syncAndDrawFrame(nullptr);
+            proxy->syncAndDrawFrame();
         }
         if (opts.reportFrametimeWeight) {
             proxy->fence();
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index d44be7d..9a3b81c 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -80,7 +80,7 @@
             << "Glop(s) expected";
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
     SkPaint strokePaint;
     strokePaint.setStyle(SkPaint::kStroke_Style);
     strokePaint.setStrokeWidth(4);
@@ -113,7 +113,7 @@
     testUnmergedGlopDispatch(renderThread, &ovalOp, textureGlopVerifier);
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
     SkPaint layerPaint;
     layerPaint.setAlpha(128);
     OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case
@@ -131,7 +131,7 @@
     return result;
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) {
     Rect bounds(10, 15, 20, 25);
     SkPaint paint;
     SkPaint aaPaint;
@@ -157,7 +157,7 @@
             << "Expect an offset for non-AA lines.";
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) {
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
             [](RenderProperties& props, RecordingCanvas& canvas) {
 
@@ -232,7 +232,7 @@
     return c;
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) {
     for (bool debugOverdraw : { false, true }) {
         for (bool debugLayersUpdates : { false, true }) {
             ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw);
@@ -273,7 +273,7 @@
     }
 }
 
-RENDERTHREAD_TEST(BakedOpDispatcher, pathTextureSnapping) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTextureSnapping) {
     Rect bounds(10, 15, 20, 25);
     SkPaint paint;
     SkPath path;
diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
index 59bd75e..380062a 100644
--- a/libs/hwui/tests/unit/BakedOpRendererTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
@@ -23,7 +23,7 @@
 
 const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
 
-RENDERTHREAD_TEST(BakedOpRenderer, startRepaintLayer_clear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) {
     BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, sLightInfo);
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u);
 
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index d3d80a9..ef5ce0d 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -40,11 +40,16 @@
 
     ASSERT_FALSE(canvasContext->hasSurface());
 
-    canvasContext->destroy(nullptr);
+    canvasContext->destroy();
 }
 
 RENDERTHREAD_TEST(CanvasContext, invokeFunctor) {
     TestUtils::MockFunctor functor;
     CanvasContext::invokeFunctor(renderThread, &functor);
-    ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess);
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        // we currently don't support OpenGL WebViews on the Vulkan backend
+        ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcessNoContext);
+    } else {
+        ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess);
+    }
 }
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index f1b8882..1ef9dba 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -16,8 +16,8 @@
 
 #include "DeferredLayerUpdater.h"
 #include "GlLayer.h"
+#include "Properties.h"
 
-#include "renderthread/OpenGLPipeline.h"
 #include "tests/common/TestUtils.h"
 
 #include <gtest/gtest.h>
@@ -26,12 +26,10 @@
 using namespace android::uirenderer;
 
 RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
-    renderthread::OpenGLPipeline pipeline(renderThread);
-    sp<DeferredLayerUpdater> layerUpdater = pipeline.createTextureLayer();
+    sp<DeferredLayerUpdater> layerUpdater = TestUtils::createTextureLayerUpdater(renderThread);
     layerUpdater->setSize(100, 100);
     layerUpdater->setBlend(true);
 
-
     // updates are deferred so the backing layer should still be in its default state
     if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
         GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
diff --git a/libs/hwui/tests/unit/DeviceInfoTests.cpp b/libs/hwui/tests/unit/DeviceInfoTests.cpp
index 17236bd..af37938 100644
--- a/libs/hwui/tests/unit/DeviceInfoTests.cpp
+++ b/libs/hwui/tests/unit/DeviceInfoTests.cpp
@@ -17,11 +17,12 @@
 #include <DeviceInfo.h>
 
 #include <gtest/gtest.h>
+#include "tests/common/TestUtils.h"
 
 using namespace android;
 using namespace android::uirenderer;
 
-TEST(DeviceInfo, basic) {
+OPENGL_PIPELINE_TEST(DeviceInfo, basic) {
     // can't assert state before init - another test may have initialized the singleton
     DeviceInfo::initialize();
     const DeviceInfo* di = DeviceInfo::get();
diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp
index 99080ac..ee20236 100644
--- a/libs/hwui/tests/unit/FontRendererTests.cpp
+++ b/libs/hwui/tests/unit/FontRendererTests.cpp
@@ -28,7 +28,7 @@
     return true;
 }
 
-RENDERTHREAD_TEST(FontRenderer, renderDropShadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) {
     SkPaint paint;
     paint.setTextSize(10);
     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 71c7516..a391d1e 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -109,7 +109,7 @@
 
 class FailRenderer : public TestRendererBase {};
 
-RENDERTHREAD_TEST(FrameBuilder, simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simple) {
     class SimpleTestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -143,7 +143,7 @@
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
 }
 
-RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) {
     class SimpleStrokeTestRenderer : public TestRendererBase {
     public:
         void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -171,7 +171,7 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) {
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
@@ -187,7 +187,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleBatching) {
     const int LOOPS = 5;
     class SimpleBatchingTestRenderer : public TestRendererBase {
     public:
@@ -225,7 +225,7 @@
             << "Expect number of ops = 2 * loop count";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) {
     class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -251,7 +251,7 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) {
     class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -311,16 +311,39 @@
         TestUtils::syncHierarchyPropertiesAndDisplayList(node);
     }
 
-    FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
-            sLightGeometry, Caches::getInstance());
-    frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
+    {
+        FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
+                sLightGeometry, Caches::getInstance());
+        frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
 
-    DeferRenderNodeSceneTestRenderer renderer;
-    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-    EXPECT_EQ(4, renderer.getIndex());
+        DeferRenderNodeSceneTestRenderer renderer;
+        frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+        EXPECT_EQ(4, renderer.getIndex());
+    }
+
+    for (auto& node : nodes) {
+        EXPECT_TRUE(node->isValid());
+        EXPECT_FALSE(node->nothingToDraw());
+        node->setStagingDisplayList(nullptr);
+        EXPECT_FALSE(node->isValid());
+        EXPECT_FALSE(node->nothingToDraw());
+        node->destroyHardwareResources();
+        EXPECT_TRUE(node->nothingToDraw());
+        EXPECT_FALSE(node->isValid());
+    }
+
+    {
+        // Validate no crashes if any nodes are missing DisplayLists
+        FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600,
+                sLightGeometry, Caches::getInstance());
+        frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
+
+        FailRenderer renderer;
+        frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    }
 }
 
-RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_noFbo0) {
     class EmptyNoFbo0TestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -338,7 +361,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_withFbo0) {
     class EmptyWithFbo0TestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -364,7 +387,7 @@
             " but fbo0 update lifecycle should still be observed";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) {
     class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -394,7 +417,7 @@
     EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
     static sk_sp<Bitmap> opaqueBitmap(TestUtils::createBitmap(50, 50,
             SkColorType::kRGB_565_SkColorType));
     static sk_sp<Bitmap> transpBitmap(TestUtils::createBitmap(50, 50,
@@ -437,7 +460,7 @@
     EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clippedMerging) {
     class ClippedMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -479,7 +502,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) {
     class RegionClipStopsMergeTestRenderer : public TestRendererBase {
     public:
         void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
@@ -508,7 +531,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textMerging) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -538,7 +561,7 @@
     EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) {
     const int LOOPS = 5;
     class TextStrikethroughTestRenderer : public TestRendererBase {
     public:
@@ -576,7 +599,7 @@
 static auto styles = {
         SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
 
-RENDERTHREAD_TEST(FrameBuilder, textStyle) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) {
     class TextStyleTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -630,7 +653,7 @@
     EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
     class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
     public:
         void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -664,7 +687,7 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) {
     class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
     public:
         void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -696,10 +719,10 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_reject) {
     auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
             SkMatrix::MakeTrans(5, 5));
-    if (layerUpdater->backingLayer()->getApi() != Layer::Api::OpenGL) return;
+    EXPECT_EQ(Layer::Api::OpenGL, layerUpdater->backingLayer()->getApi());
 
     GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
     glLayer->setRenderTarget(GL_NONE); // Should be rejected
@@ -717,7 +740,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, functor_reject) {
     class FunctorTestRenderer : public TestRendererBase {
     public:
         void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
@@ -742,7 +765,7 @@
     EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) {
     class ColorTestRenderer : public TestRendererBase {
     public:
         void onColorOp(const ColorOp& op, const BakedOpState& state) override {
@@ -767,7 +790,7 @@
     EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
 }
 
-TEST(FrameBuilder, renderNode) {
+OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) {
     class RenderNodeTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -814,7 +837,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, clipped) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clipped) {
     class ClippedTestRenderer : public TestRendererBase {
     public:
         void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -840,7 +863,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) {
     class SaveLayerSimpleTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -890,7 +913,7 @@
     EXPECT_EQ(5, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) {
     /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
      * - startTemporaryLayer2, rect2 endLayer2
      * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -973,7 +996,7 @@
     EXPECT_EQ(12, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_contentRejection) {
         auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
@@ -996,7 +1019,7 @@
     frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_simple) {
     class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1041,7 +1064,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_round) {
     class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1075,7 +1098,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
     class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1133,7 +1156,7 @@
             << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
     class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
     public:
         void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1175,7 +1198,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_reject) {
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // unclipped savelayer + rect both in area that won't intersect with dirty
@@ -1197,7 +1220,7 @@
  * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
  * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
  */
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) {
     class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -1262,7 +1285,7 @@
     EXPECT_EQ(13, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) {
     class HwLayerSimpleTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1326,7 +1349,7 @@
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) {
     /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
      * - startRepaintLayer(child), rect(grey), endLayer
      * - startTemporaryLayer, drawLayer(child), endLayer
@@ -1435,7 +1458,7 @@
 }
 
 
-RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) {
     class BuildLayerTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1531,7 +1554,7 @@
 
 } // end anonymous namespace
 
-RENDERTHREAD_TEST(FrameBuilder, zReorder) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, zReorder) {
     auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.insertReorderBarrier(true);
@@ -1566,7 +1589,7 @@
     EXPECT_EQ(13, renderer.getIndex());
 };
 
-RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) {
     static const int scrollX = 5;
     static const int scrollY = 10;
     class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1659,7 +1682,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) {
     static const int scrollX = 5;
     static const int scrollY = 10;
     class ProjectionHwLayerTestRenderer : public TestRendererBase {
@@ -1750,7 +1773,7 @@
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) {
     static const int scrollX = 500000;
     static const int scrollY = 0;
     class ProjectionChildScrollTestRenderer : public TestRendererBase {
@@ -1817,7 +1840,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) {
     class ShadowTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1850,7 +1873,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) {
     class ShadowSaveLayerTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -1896,7 +1919,7 @@
     EXPECT_EQ(6, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowHwLayer) {
     class ShadowHwLayerTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1954,7 +1977,7 @@
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowLayering) {
     class ShadowLayeringTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1981,7 +2004,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) {
     class ShadowClippingTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -2041,7 +2064,7 @@
     EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
     testProperty([](RenderProperties& properties) {
         properties.setAlpha(0.5f);
         properties.setHasOverlappingRendering(false);
@@ -2050,7 +2073,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropClipping) {
     testProperty([](RenderProperties& properties) {
         properties.setClipToBounds(true);
         properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -2060,7 +2083,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropRevealClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableRevealClip().set(true, 50, 50, 25);
     }, [](const RectOp& op, const BakedOpState& state) {
@@ -2071,7 +2094,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOutlineClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableOutline().setShouldClip(true);
         properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -2083,7 +2106,7 @@
     });
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropTransform) {
     testProperty([](RenderProperties& properties) {
         properties.setLeftTopRightBottom(10, 10, 110, 110);
 
@@ -2192,7 +2215,7 @@
     ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setTranslationX(10); // offset rendering content
@@ -2211,7 +2234,7 @@
                 << "expect drawLayer to be translated as part of being clipped";
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         // Translate and rotate the view so that the only visible part is the top left corner of
@@ -2230,7 +2253,7 @@
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setPivotX(0);
@@ -2244,7 +2267,7 @@
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
-RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) {
     class ClipReplaceTestRenderer : public TestRendererBase {
     public:
         void onColorOp(const ColorOp& op, const BakedOpState& state) override {
@@ -2269,7 +2292,7 @@
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
     /* R is backward projected on B
                 A
                / \
@@ -2299,7 +2322,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderProjectLast) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) {
     /* R is backward projected on E
                   A
                 / | \
@@ -2331,7 +2354,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderNoReceivable) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) {
     /* R is backward projected without receiver
                 A
                / \
@@ -2360,7 +2383,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderParentReceivable) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) {
     /* R is backward projected on C
                 A
                / \
@@ -2389,7 +2412,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
      auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         drawOrderedNode(&canvas, 0, nullptr); //nodeB
@@ -2412,7 +2435,7 @@
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderProjectedSibling) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) {
     //TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a
     //bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical
     //tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling
@@ -2445,7 +2468,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderProjectedSibling2) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) {
     /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
                 A
                 |
@@ -2478,7 +2501,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
     /* R is backward projected on B
                 A
                 |
@@ -2510,7 +2533,7 @@
     EXPECT_EQ(3, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderTwoReceivables) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) {
     /* B and G are receivables, R is backward projected
                 A
                / \
@@ -2543,7 +2566,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
     /* B and G are receivables, G is backward projected
                 A
                / \
@@ -2576,7 +2599,7 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
     /* B and G are receivables, R is backward projected
                 A
                / \
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
index ce1db05..caeb6bf 100644
--- a/libs/hwui/tests/unit/GlopBuilderTests.cpp
+++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp
@@ -116,7 +116,7 @@
     return glop;
 }
 
-RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(GlopBuilder, rectSnapTest) {
     RenderState& renderState = renderThread.renderState();
     Caches& caches = Caches::getInstance();
     SkPaint paint;
diff --git a/libs/hwui/tests/unit/GradientCacheTests.cpp b/libs/hwui/tests/unit/GradientCacheTests.cpp
index 0ee9647..a3b346f 100644
--- a/libs/hwui/tests/unit/GradientCacheTests.cpp
+++ b/libs/hwui/tests/unit/GradientCacheTests.cpp
@@ -23,7 +23,7 @@
 using namespace android;
 using namespace android::uirenderer;
 
-RENDERTHREAD_TEST(GradientCache, addRemove) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(GradientCache, addRemove) {
     Extensions extensions;
     GradientCache cache(extensions);
     ASSERT_LT(1000u, cache.getMaxSize()) << "Expect non-trivial size";
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 06599dd..6c42ca1 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -29,7 +29,7 @@
 const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
 const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
 
-RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) {
     auto node = TestUtils::createNode(0, 0, 100, 100,
             [](RenderProperties& props, Canvas& canvas) {
         canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
@@ -49,7 +49,7 @@
     frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, Canvas& canvas) {
         canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
diff --git a/libs/hwui/tests/unit/MeshStateTests.cpp b/libs/hwui/tests/unit/MeshStateTests.cpp
index 0881fa2..511d6d2 100644
--- a/libs/hwui/tests/unit/MeshStateTests.cpp
+++ b/libs/hwui/tests/unit/MeshStateTests.cpp
@@ -24,7 +24,7 @@
 using namespace android::uirenderer;
 using namespace testing;
 
-RENDERTHREAD_TEST(MeshState, genOrUpdate) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(MeshState, genOrUpdate) {
     debug::ScopedReplaceDriver<debug::MockGlesDriver> driverRef;
     auto& mockGlDriver = driverRef.get();
     EXPECT_CALL(mockGlDriver, glGenBuffers_(_, _)).WillOnce(SetArgPointee<1>(35));
@@ -33,4 +33,4 @@
 
     GLuint buffer = 0;
     renderThread.renderState().meshState().genOrUpdateMeshBuffer(&buffer, 10, nullptr, GL_DYNAMIC_DRAW);
-}
\ No newline at end of file
+}
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index b7950aa..6cd595a 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -30,7 +30,7 @@
     EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
 }
 
-RENDERTHREAD_TEST(OffscreenBuffer, construct) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, construct) {
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u);
     EXPECT_EQ(49u, layer.viewportWidth);
     EXPECT_EQ(149u, layer.viewportHeight);
@@ -41,7 +41,7 @@
     EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
 }
 
-RENDERTHREAD_TEST(OffscreenBuffer, getTextureCoordinates) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, getTextureCoordinates) {
     OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
     EXPECT_EQ(Rect(0, 1, 1, 0),
             layerAligned.getTextureCoordinates());
@@ -51,7 +51,7 @@
             layerUnaligned.getTextureCoordinates());
 }
 
-RENDERTHREAD_TEST(OffscreenBuffer, dirty) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, dirty) {
     OffscreenBuffer buffer(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
     buffer.dirty(Rect(-100, -100, 100, 100));
     EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
@@ -65,7 +65,7 @@
             << "pool must read size from Properties";
 }
 
-RENDERTHREAD_TEST(OffscreenBufferPool, getPutClear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClear) {
     OffscreenBufferPool pool;
 
     auto layer = pool.get(renderThread.renderState(), 100u, 200u);
@@ -88,7 +88,7 @@
     EXPECT_EQ(0u, pool.getCount());
 }
 
-RENDERTHREAD_TEST(OffscreenBufferPool, resize) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resize) {
     OffscreenBufferPool pool;
 
     auto layer = pool.get(renderThread.renderState(), 64u, 64u);
@@ -123,7 +123,7 @@
     pool.putOrDelete(layer2);
 }
 
-RENDERTHREAD_TEST(OffscreenBufferPool, putAndDestroy) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, putAndDestroy) {
     OffscreenBufferPool pool;
     // layer too big to return to the pool
     // Note: this relies on the fact that the pool won't reject based on max texture size
@@ -133,7 +133,7 @@
     EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
 }
 
-RENDERTHREAD_TEST(OffscreenBufferPool, clear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) {
     EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
     OffscreenBufferPool pool;
 
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 4a73383..124f5fa 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -47,7 +47,13 @@
     opValidator(*(dl->getOps()[0]));
 }
 
-TEST(RecordingCanvas, emptyPlayback) {
+// The RecordingCanvas is only ever used by the OpenGL RenderPipeline and never when Skia is in use.
+// Thus, even though many of these test are not directly dependent on the current RenderPipeline, we
+// set them all to be OPENGL_PIPELINE_TESTs in case the underlying code in RecordingCanvas ever
+// changes to require the use of the OPENGL_PIPELINE. Currently the textureLayer test is the only
+// test that requires being an OPENGL_PIPELINE_TEST.
+
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPlayback) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.restore();
@@ -55,7 +61,7 @@
     playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
 }
 
-TEST(RecordingCanvas, clipRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, clipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
@@ -71,7 +77,7 @@
             << "Clip should be serialized once";
 }
 
-TEST(RecordingCanvas, emptyClipRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyClipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
@@ -82,7 +88,7 @@
     ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
 }
 
-TEST(RecordingCanvas, emptyPaintRejection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPaintRejection) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint emptyPaint;
         emptyPaint.setColor(Color::Transparent);
@@ -103,7 +109,7 @@
     EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected";
 }
 
-TEST(RecordingCanvas, drawArc) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawArc) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
         canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
@@ -119,7 +125,7 @@
     EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
 }
 
-TEST(RecordingCanvas, drawLines) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawLines) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
@@ -136,7 +142,7 @@
             << "unmapped bounds must be size of line, and not outset for stroke width";
 }
 
-TEST(RecordingCanvas, drawRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.drawRect(10, 20, 90, 180, SkPaint());
     });
@@ -148,7 +154,7 @@
     EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
 }
 
-TEST(RecordingCanvas, drawRoundRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRoundRect) {
     // Round case - stays rounded
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
@@ -165,7 +171,7 @@
         << "Non-rounded rects should be converted";
 }
 
-TEST(RecordingCanvas, drawGlyphs) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -186,7 +192,7 @@
     ASSERT_EQ(1, count);
 }
 
-TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -218,7 +224,7 @@
     EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
 }
 
-TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
@@ -248,7 +254,7 @@
     ASSERT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, drawColor) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawColor) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
     });
@@ -260,7 +266,7 @@
     EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
 }
 
-TEST(RecordingCanvas, backgroundAndImage) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, backgroundAndImage) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
         SkPaint paint;
@@ -312,7 +318,7 @@
     ASSERT_EQ(2, count);
 }
 
-RENDERTHREAD_TEST(RecordingCanvas, textureLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas, textureLayer) {
     auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
             SkMatrix::MakeTrans(5, 5));
 
@@ -327,7 +333,7 @@
     });
 }
 
-TEST(RecordingCanvas, saveLayer_simple) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(10, 20, 190, 180, SkPaint());
@@ -361,7 +367,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_rounding) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rounding) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
             canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer);
             canvas.drawRect(20, 20, 80, 80, SkPaint());
@@ -391,7 +397,7 @@
         EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_missingRestore) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 200, 200, SkPaint());
@@ -406,7 +412,7 @@
     EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
 }
 
-TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
         canvas.drawRect(10, 20, 190, 180, SkPaint());
@@ -438,7 +444,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_addClipFlag) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_addClipFlag) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect);
@@ -457,7 +463,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_viewportCrop) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // shouldn't matter, since saveLayer will clip to its bounds
         canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace);
@@ -481,7 +487,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(100, 100);
@@ -507,7 +513,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_rotateClipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateClipped) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(100, 100);
@@ -545,7 +551,7 @@
     EXPECT_EQ(3, count);
 }
 
-TEST(RecordingCanvas, saveLayer_rejectBegin) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rejectBegin) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(0, -20); // avoid identity case
@@ -560,7 +566,7 @@
     ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
 }
 
-TEST(RecordingCanvas, drawRenderNode_rejection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_rejection) {
     auto child = TestUtils::createNode(50, 50, 150, 150,
             [](RenderProperties& props, Canvas& canvas) {
         SkPaint paint;
@@ -575,7 +581,7 @@
     ASSERT_TRUE(dl->isEmpty());
 }
 
-TEST(RecordingCanvas, drawRenderNode_projection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) {
     sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
             [](RenderProperties& props, Canvas& canvas) {
         SkPaint paint;
@@ -618,7 +624,7 @@
     }
 }
 
-TEST(RecordingCanvas, firstClipWillReplace) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, firstClipWillReplace) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         // since no explicit clip set on canvas, this should be the one observed on op:
@@ -635,7 +641,7 @@
     EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
 }
 
-TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace);
@@ -648,7 +654,7 @@
     EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
 }
 
-TEST(RecordingCanvas, insertReorderBarrier) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.insertReorderBarrier(true);
@@ -669,7 +675,7 @@
     EXPECT_TRUE(chunks[1].reorderChildren);
 }
 
-TEST(RecordingCanvas, insertReorderBarrier_clip) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier_clip) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // first chunk: no recorded clip
         canvas.insertReorderBarrier(true);
@@ -699,7 +705,7 @@
     EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
 }
 
-TEST(RecordingCanvas, refPaint) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) {
     SkPaint paint;
 
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
@@ -727,7 +733,7 @@
     EXPECT_NE(&paint, ops[2]->paint);
 }
 
-TEST(RecordingCanvas, refBitmap) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmap) {
     sk_sp<Bitmap> bitmap(TestUtils::createBitmap(100, 100));
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
         canvas.drawBitmap(*bitmap, 0, 0, nullptr);
@@ -736,7 +742,7 @@
     EXPECT_EQ(1u, bitmaps.size());
 }
 
-TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
     sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
         SkPaint paint;
@@ -755,7 +761,7 @@
     EXPECT_EQ(1u, bitmaps.size());
 }
 
-TEST(RecordingCanvas, refBitmapInShader_composeShader) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_composeShader) {
     sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
         SkPaint paint;
@@ -785,7 +791,7 @@
     EXPECT_EQ(1u, bitmaps.size());
 }
 
-TEST(RecordingCanvas, drawText) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         Paint paint;
         paint.setAntiAlias(true);
@@ -807,7 +813,7 @@
     ASSERT_EQ(1, count);
 }
 
-TEST(RecordingCanvas, drawTextInHighContrast) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.setHighContrastText(true);
         Paint paint;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index f5ff058..f21b3f7 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -321,7 +321,6 @@
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
-    info.observer = nullptr;
     parent->prepareTree(info);
 
     //parent(A)             -> (receiverBackground, child)
@@ -437,7 +436,6 @@
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
-    info.observer = nullptr;
     parent->prepareTree(info);
 
     int drawCounter = 0;
@@ -527,7 +525,6 @@
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
-    info.observer = nullptr;
     parent->prepareTree(info);
 
     std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
@@ -545,7 +542,6 @@
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
-    info.observer = nullptr;
     renderNode->prepareTree(info);
 
     //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
@@ -865,10 +861,8 @@
         void onDrawPaint(const SkPaint&) {
             switch (mDrawCounter++) {
             case 0:
-                // While this mirrors FrameBuilder::colorOp_unbounded, this value is different
-                // because there is no root (root is clipped in SkiaPipeline::renderFrame).
-                // SkiaPipeline.clipped and clip_replace verify the root clip.
-                EXPECT_TRUE(TestUtils::getClipBounds(this).isEmpty());
+                EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT),
+                        TestUtils::getClipBounds(this));
                 break;
             case 1:
                 EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 331a6ac..8af4bbe 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -66,6 +66,70 @@
     EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
 }
 
+TEST(RenderNode, validity) {
+    auto child = TestUtils::createNode(0, 0, 200, 400,
+            [](RenderProperties& props, Canvas& canvas) {
+        canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
+    });
+    auto parent = TestUtils::createNode(0, 0, 200, 400,
+            [&child](RenderProperties& props, Canvas& canvas) {
+        canvas.drawRenderNode(child.get());
+    });
+
+    EXPECT_TRUE(child->isValid());
+    EXPECT_TRUE(parent->isValid());
+    EXPECT_TRUE(child->nothingToDraw());
+    EXPECT_TRUE(parent->nothingToDraw());
+
+    TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+    EXPECT_TRUE(child->isValid());
+    EXPECT_TRUE(parent->isValid());
+    EXPECT_FALSE(child->nothingToDraw());
+    EXPECT_FALSE(parent->nothingToDraw());
+
+    TestUtils::recordNode(*parent, [](Canvas& canvas) {
+        canvas.drawColor(Color::Amber_500, SkBlendMode::kSrcOver);
+    });
+
+    EXPECT_TRUE(child->isValid());
+    EXPECT_TRUE(parent->isValid());
+    EXPECT_FALSE(child->nothingToDraw());
+    EXPECT_FALSE(parent->nothingToDraw());
+
+    TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+    EXPECT_FALSE(child->isValid());
+    EXPECT_TRUE(parent->isValid());
+    EXPECT_TRUE(child->nothingToDraw());
+    EXPECT_FALSE(parent->nothingToDraw());
+
+    TestUtils::recordNode(*child, [](Canvas& canvas) {
+        canvas.drawColor(Color::Amber_500, SkBlendMode::kSrcOver);
+    });
+
+    EXPECT_TRUE(child->isValid());
+    EXPECT_TRUE(child->nothingToDraw());
+
+    TestUtils::recordNode(*parent, [&child](Canvas& canvas) {
+        canvas.drawRenderNode(child.get());
+    });
+
+    TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+    EXPECT_TRUE(child->isValid());
+    EXPECT_TRUE(parent->isValid());
+    EXPECT_FALSE(child->nothingToDraw());
+    EXPECT_FALSE(parent->nothingToDraw());
+
+    parent->destroyHardwareResources();
+
+    EXPECT_FALSE(child->isValid());
+    EXPECT_FALSE(parent->isValid());
+    EXPECT_TRUE(child->nothingToDraw());
+    EXPECT_TRUE(parent->nothingToDraw());
+}
+
 TEST(RenderNode, releasedCallback) {
     class DecRefOnReleased : public GlFunctorLifecycleListener {
     public:
@@ -112,7 +176,6 @@
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
-    info.observer = nullptr;
 
     {
         auto nonNullDLNode = TestUtils::createNode(0, 0, 200, 400,
@@ -131,16 +194,17 @@
         nullDLNode->prepareTree(info);
     }
 
-    canvasContext->destroy(nullptr);
+    canvasContext->destroy();
 }
 
 RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
 
     VectorDrawable::Group* group = new VectorDrawable::Group();
-    VectorDrawableRoot* vectorDrawable = new VectorDrawableRoot(group);
+    sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
+
     auto rootNode = TestUtils::createNode(0, 0, 200, 400,
             [&](RenderProperties& props, Canvas& canvas) {
-        canvas.drawVectorDrawable(vectorDrawable);
+        canvas.drawVectorDrawable(vectorDrawable.get());
     });
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
@@ -150,7 +214,6 @@
     LayerUpdateQueue layerUpdateQueue;
     info.damageAccumulator = &damageAccumulator;
     info.layerUpdateQueue = &layerUpdateQueue;
-    info.observer = nullptr;
 
     // Put node on HW layer
     rootNode->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
@@ -164,7 +227,5 @@
     EXPECT_FALSE(info.layerUpdateQueue->entries().empty());
     EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode);
     EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
-
-    delete vectorDrawable;
-    canvasContext->destroy(nullptr);
+    canvasContext->destroy();
 }
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 95f9974..0ac09ac 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -28,7 +28,7 @@
  * Verify that we get the same culling bounds for text for (1) drawing glyphs
  * directly to a Canvas or (2) going through a SkPicture as an intermediate step.
  */
-TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
+OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // setup test variables
         SkPaint paint;
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 899758a..be460bf 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -118,7 +118,7 @@
     }
 };
 
-RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
@@ -126,7 +126,6 @@
     TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
-    info.observer = nullptr;
 
     SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
 
@@ -137,7 +136,9 @@
 
     ASSERT_FALSE(cleanVD.isDirty());
     ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
-    ASSERT_FALSE(skiaDL.prepareListAndChildren(info, false, [](RenderNode*, TreeInfo&, bool) {}));
+    TestUtils::MockTreeObserver observer;
+    ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false,
+            [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
     ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed());
 
     // prepare again this time adding a dirty VD
@@ -146,7 +147,8 @@
 
     ASSERT_TRUE(dirtyVD.isDirty());
     ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
-    ASSERT_TRUE(skiaDL.prepareListAndChildren(info, false, [](RenderNode*, TreeInfo&, bool) {}));
+    ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false,
+            [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
     ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
 
     // prepare again this time adding a RenderNode and a callback
@@ -155,8 +157,8 @@
     SkCanvas dummyCanvas;
     skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
     bool hasRun = false;
-    ASSERT_TRUE(skiaDL.prepareListAndChildren(info, false,
-            [&hasRun, renderNode, infoPtr](RenderNode* n, TreeInfo& i, bool r) {
+    ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false,
+            [&hasRun, renderNode, infoPtr](RenderNode* n, TreeObserver& observer, TreeInfo& i, bool r) {
         hasRun = true;
         ASSERT_EQ(renderNode.get(), n);
         ASSERT_EQ(infoPtr, &i);
@@ -164,7 +166,7 @@
     }));
     ASSERT_TRUE(hasRun);
 
-    canvasContext->destroy(nullptr);
+    canvasContext->destroy();
 }
 
 TEST(SkiaDisplayList, updateChildren) {
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 494585a..5bb0b6d 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -36,7 +36,7 @@
 using namespace android::uirenderer::renderthread;
 using namespace android::uirenderer::skiapipeline;
 
-RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
     auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
         [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
             redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -55,7 +55,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
     auto halfGreenNode = TestUtils::createSkiaNode(0, 0, 2, 2,
         [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
             SkPaint greenPaint;
@@ -80,7 +80,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
     auto redNode = TestUtils::createSkiaNode(0, 0, 2, 2,
         [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
             redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -101,7 +101,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
     auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
         [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
             redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -144,7 +144,7 @@
     blueNode->setLayerSurface(sk_sp<SkSurface>());
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
     ScopedProperty<bool> prop(Properties::debugOverdraw, true);
 
     auto whiteNode = TestUtils::createSkiaNode(0, 0, 1, 1,
@@ -170,9 +170,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
 
     // 1 Overdraw, should be blue blended onto white.
-    renderNodes.push_back(whiteNode); //this is the "content" node
-    renderNodes.push_back(whiteNode); //the "content" node above does not cause an overdraw, because
-    //it clips the first "background" node
+    renderNodes.push_back(whiteNode);
     pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0d0ff);
 
@@ -218,7 +216,7 @@
 };
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
     class DeferTestCanvas : public SkCanvas {
     public:
         DeferTestCanvas() : SkCanvas(800, 600) {}
@@ -284,7 +282,7 @@
     EXPECT_EQ(4, surface->canvas()->mDrawCounter);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, clipped) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
     static const int CANVAS_WIDTH = 200;
     static const int CANVAS_HEIGHT = 200;
     class ClippedTestCanvas : public SkCanvas {
@@ -315,7 +313,7 @@
     EXPECT_EQ(1, surface->canvas()->mDrawCounter);
 }
 
-RENDERTHREAD_TEST(SkiaPipeline, clip_replace) {
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
     static const int CANVAS_WIDTH = 50;
     static const int CANVAS_HEIGHT = 50;
     class ClipReplaceTestCanvas : public SkCanvas {
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index e7171c8..95c6ed6 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -73,7 +73,7 @@
 
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, renderPropClipping) {
+TEST(RenderNodeDrawable, renderPropClipping) {
     testProperty([](RenderProperties& properties) {
         properties.setClipToBounds(true);
         properties.setClipBounds(android::uirenderer::Rect(10, 20, 300, 400));
@@ -83,38 +83,24 @@
     });
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, renderPropRevealClip) {
+TEST(RenderNodeDrawable, renderPropRevealClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableRevealClip().set(true, 50, 50, 25);
     }, [](const SkCanvas& canvas) {
-        SkClipStack::Iter it(*canvas.getClipStack(), SkClipStack::Iter::kBottom_IterStart);
-        const SkClipStack::Element *top = it.next();
-        ASSERT_NE(nullptr, top);
-        SkPath clip;
-        top->asPath(&clip);
-        SkRect rect;
-        EXPECT_TRUE(clip.isOval(&rect));
-        EXPECT_EQ(SkRect::MakeLTRB(25, 25, 75, 75), rect);
+        EXPECT_EQ(SkRect::MakeLTRB(25, 25, 75, 75), TestUtils::getClipBounds(&canvas));
     });
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, renderPropOutlineClip) {
+TEST(RenderNodeDrawable, renderPropOutlineClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableOutline().setShouldClip(true);
         properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
     }, [](const SkCanvas& canvas) {
-        SkClipStack::Iter it(*canvas.getClipStack(), SkClipStack::Iter::kBottom_IterStart);
-        const SkClipStack::Element *top = it.next();
-        ASSERT_NE(nullptr, top);
-        SkPath clip;
-        top->asPath(&clip);
-        SkRRect rrect;
-        EXPECT_TRUE(clip.isRRect(&rrect));
-        EXPECT_EQ(SkRRect::MakeRectXY(SkRect::MakeLTRB(10, 20, 30, 40), 5.0f, 5.0f), rrect);
+        EXPECT_EQ(SkRect::MakeLTRB(10, 20, 30, 40), TestUtils::getClipBounds(&canvas));
     });
 }
 
-RENDERTHREAD_TEST(RenderNodeDrawable, renderPropTransform) {
+TEST(RenderNodeDrawable, renderPropTransform) {
     testProperty([](RenderProperties& properties) {
         properties.setLeftTopRightBottom(10, 10, 110, 110);
 
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index 0d26df2..8312bda 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -26,7 +26,7 @@
 using namespace android;
 using namespace android::uirenderer;
 
-RENDERTHREAD_TEST(TextDropShadowCache, addRemove) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) {
     SkPaint paint;
     paint.setTextSize(20);
 
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 8b80d69..79fc864 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -98,8 +98,8 @@
     }
 
     void finishDrawing() {
-        mRootNode->setStagingDisplayList(mCanvas->finishRecording(), nullptr);
-        mProxy->syncAndDrawFrame(nullptr);
+        mRootNode->setStagingDisplayList(mCanvas->finishRecording());
+        mProxy->syncAndDrawFrame();
         // Surprisingly, calling mProxy->fence() here appears to make no difference to
         // the timings we record.
     }
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index d2873e9..aac9727 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -47,7 +47,7 @@
     private double mCarrierPhaseUncertainty;
     private int mMultipathIndicator;
     private double mSnrInDb;
-    private double mAgcLevelDb;
+    private double mAutomaticGainControlLevelInDb;
 
     // The following enumerations must be in sync with the values declared in gps.h
 
@@ -117,14 +117,12 @@
      * This GNSS measurement's tracking state has time-of-week known, possibly not decoded
      * over the air but has been determined from other sources. If TOW decoded is set then TOW Known
      * will also be set.
-     * @hide
      */
     public static final int STATE_TOW_KNOWN = (1<<14);
     /**
      * This Glonass measurement's tracking state has time-of-day known, possibly not decoded
      * over the air but has been determined from other sources. If TOD decoded is set then TOD Known
      * will also be set.
-     * @hide
      */
     public static final int STATE_GLO_TOD_KNOWN = (1<<15);
 
@@ -196,7 +194,7 @@
         mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty;
         mMultipathIndicator = measurement.mMultipathIndicator;
         mSnrInDb = measurement.mSnrInDb;
-        mAgcLevelDb = measurement.mAgcLevelDb;
+        mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb;
     }
 
     /**
@@ -662,8 +660,14 @@
     /**
      * Gets the carrier frequency of the tracked signal.
      *
-     * <p>For example it can be the GPS L1 = 1.57542e9 Hz, or L2, L5, varying GLO channels, etc. If
-     * the field is not set, it is the primary common use frequency, e.g. L1 for GPS.
+     * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+     * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
+     * common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
+     *
+     * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
+     * measurement objects will be reported for this same satellite, in one of the measurement
+     * objects, all the values related to L1 will be filled, and in the other all of the values
+     * related to L5 will be filled.
      *
      * <p>The value is only available if {@link #hasCarrierFrequencyHz()} is {@code true}.
      */
@@ -884,48 +888,50 @@
     }
 
     /**
-     * Returns {@code true} if {@link #getAgcLevelDb()} is available, {@code false} otherwise.
-     * @hide
+     * Returns {@code true} if {@link #getAutomaticGainControlLevelInDb()} is available,
+     * {@code false} otherwise.
      */
-    public boolean hasAgcLevelDb() {
+    public boolean hasAutomaticGainControlLevelInDb() {
         return isFlagSet(HAS_AUTOMATIC_GAIN_CONTROL);
     }
 
     /**
      * Gets the Automatic Gain Control level in dB.
      *
-     * <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal to
-     * minimize the quantization losses. The AGC level may be used to indicate potential
-     * interference. When AGC is at a nominal level, this value must be set as 0.  Higher gain
-     * (and/or lower input power) shall be output as a positive number. Hence in cases of strong
-     * jamming, in the band of this signal, this value will go more negative.
+     * <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal. The AGC
+     * level may be used to indicate potential interference. When AGC is at a nominal level, this
+     * value must be set as 0. Higher gain (and/or lower input power) shall be output as a positive
+     * number. Hence in cases of strong jamming, in the band of this signal, this value will go more
+     * negative.
+     *
      * <p>Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
      * components) may also affect the typical output of of this value on any given hardware design
      * in an open sky test - the important aspect of this output is that changes in this value are
      * indicative of changes on input signal power in the frequency band for this measurement.
-     * <p>The value is only available if {@link #hasAgcLevelDb()} is {@code true}.
-     * @hide
+     * <p>The value is only available if {@link #hasAutomaticGainControlLevelInDb()} is {@code true}
      */
-    public double getAgcLevelDb() {
-        return mAgcLevelDb;
+    public double getAutomaticGainControlLevelInDb() {
+        return mAutomaticGainControlLevelInDb;
     }
 
     /**
      * Sets the Automatic Gain Control level in dB.
      * @hide
      */
-    public void setAgcLevelDb(double agcLevelDb) {
+    @TestApi
+    public void setAutomaticGainControlLevelInDb(double agcLevelDb) {
         setFlag(HAS_AUTOMATIC_GAIN_CONTROL);
-        mAgcLevelDb = agcLevelDb;
+        mAutomaticGainControlLevelInDb = agcLevelDb;
     }
 
     /**
      * Resets the Automatic Gain Control level.
      * @hide
      */
-    public void resetAgcLevel() {
+    @TestApi
+    public void resetAutomaticGainControlLevel() {
         resetFlag(HAS_AUTOMATIC_GAIN_CONTROL);
-        mAgcLevelDb = Double.NaN;
+        mAutomaticGainControlLevelInDb = Double.NaN;
     }
 
     public static final Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
@@ -952,7 +958,7 @@
             gnssMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
             gnssMeasurement.mMultipathIndicator = parcel.readInt();
             gnssMeasurement.mSnrInDb = parcel.readDouble();
-            gnssMeasurement.mAgcLevelDb = parcel.readDouble();
+            gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble();
 
             return gnssMeasurement;
         }
@@ -984,7 +990,7 @@
         parcel.writeDouble(mCarrierPhaseUncertainty);
         parcel.writeInt(mMultipathIndicator);
         parcel.writeDouble(mSnrInDb);
-        parcel.writeDouble(mAgcLevelDb);
+        parcel.writeDouble(mAutomaticGainControlLevelInDb);
     }
 
     @Override
@@ -1058,7 +1064,7 @@
         builder.append(String.format(
             format,
             "AgcLevelDb",
-            hasAgcLevelDb() ? mAgcLevelDb : null));
+            hasAutomaticGainControlLevelInDb() ? mAutomaticGainControlLevelInDb : null));
 
         return builder.toString();
     }
@@ -1082,7 +1088,7 @@
         resetCarrierPhaseUncertainty();
         setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
         resetSnrInDb();
-        resetAgcLevel();
+        resetAutomaticGainControlLevel();
     }
 
     private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 6565042cc..e90a174 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -223,7 +223,6 @@
      * frequency is available for the satellite at the specified index).
      *
      * @param satIndex the index of the satellite in the list.
-     * @hide
      */
     public boolean hasCarrierFrequency(int satIndex) {
         return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) != 0;
@@ -232,11 +231,15 @@
     /**
      * Gets the carrier frequency of the signal tracked.
      *
-     * For example it can be the GPS L1 = 1.57542e9 Hz, or L2, L5, varying GLO channels, etc. If
-     * the field is not set, it is the primary common use frequency, e.g. L1 for GPS.
+     * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+     * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
+     * common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
+     *
+     * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two measurements
+     * will be reported for this same satellite, in one all the values related to L1 will be filled,
+     * and in the other all of the values related to L5 will be filled.
      *
      * <p>The value is only available if {@link #hasCarrierFrequency(int satIndex)} is {@code true}.
-     * @hide
      */
     public float getCarrierFrequencyHz(int satIndex) {
         return mCarrierFrequencies[satIndex];
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index f5f437e..b222a6d 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -798,7 +798,6 @@
 
     /**
      * True if this location has a vertical accuracy.
-     * @hide
      */
     public boolean hasVerticalAccuracy() {
         return (mFieldsMask & HAS_VERTICAL_ACCURACY_MASK) != 0;
@@ -818,7 +817,6 @@
      * errors do not always follow such a simple distribution.
      *
      * <p>If this location does not have a vertical accuracy, then 0.0 is returned.
-     * @hide
      */
     public float getVerticalAccuracyMeters() {
         return mVerticalAccuracyMeters;
@@ -830,7 +828,6 @@
      * <p>See {@link #getVerticalAccuracyMeters} for the definition of vertical accuracy.
      *
      * <p>Following this call {@link #hasVerticalAccuracy} will return true.
-     * @hide
      */
     public void setVerticalAccuracyMeters(float verticalAccuracyMeters) {
         mVerticalAccuracyMeters = verticalAccuracyMeters;
@@ -842,7 +839,6 @@
      *
      * <p>Following this call {@link #hasVerticalAccuracy} will return false, and
      * {@link #getVerticalAccuracyMeters} will return 0.0.
-     * @hide
      */
     public void removeVerticalAccuracy() {
         mVerticalAccuracyMeters = 0.0f;
@@ -851,7 +847,6 @@
 
     /**
      * True if this location has a speed accuracy.
-     * @hide
      */
     public boolean hasSpeedAccuracy() {
         return (mFieldsMask & HAS_SPEED_ACCURACY_MASK) != 0;
@@ -866,7 +861,6 @@
      * inside the circle.
      *
      * <p>If this location does not have a speed accuracy, then 0.0 is returned.
-     * @hide
      */
     public float getSpeedAccuracyMetersPerSecond() {
         return mSpeedAccuracyMetersPerSecond;
@@ -878,7 +872,6 @@
      * <p>See {@link #getSpeedAccuracyMetersPerSecond} for the definition of speed accuracy.
      *
      * <p>Following this call {@link #hasSpeedAccuracy} will return true.
-     * @hide
      */
     public void setSpeedAccuracyMetersPerSecond(float speedAccuracyMeterPerSecond) {
         mSpeedAccuracyMetersPerSecond = speedAccuracyMeterPerSecond;
@@ -890,7 +883,6 @@
      *
      * <p>Following this call {@link #hasSpeedAccuracy} will return false, and
      * {@link #getSpeedAccuracyMetersPerSecond} will return 0.0.
-     * @hide
      */
     public void removeSpeedAccuracy() {
         mSpeedAccuracyMetersPerSecond = 0.0f;
@@ -899,7 +891,6 @@
 
     /**
      * True if this location has a bearing accuracy.
-     * @hide
      */
     public boolean hasBearingAccuracy() {
         return (mFieldsMask & HAS_BEARING_ACCURACY_MASK) != 0;
@@ -914,7 +905,6 @@
      * inside the circle.
      *
      * <p>If this location does not have a bearing accuracy, then 0.0 is returned.
-     * @hide
      */
     public float getBearingAccuracyDegrees() {
         return mBearingAccuracyDegrees;
@@ -926,7 +916,6 @@
      * <p>See {@link #getBearingAccuracyDegrees} for the definition of bearing accuracy.
      *
      * <p>Following this call {@link #hasBearingAccuracy} will return true.
-     * @hide
      */
     public void setBearingAccuracyDegrees(float bearingAccuracyDegrees) {
         mBearingAccuracyDegrees = bearingAccuracyDegrees;
@@ -938,7 +927,6 @@
      *
      * <p>Following this call {@link #hasBearingAccuracy} will return false, and
      * {@link #getBearingAccuracyDegrees} will return 0.0.
-     * @hide
      */
     public void removeBearingAccuracy() {
         mBearingAccuracyDegrees = 0.0f;
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index dc8264a..391a905 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -306,12 +306,26 @@
      * until there are no glitches.
      * This tuning step should be done while playing silence.
      * This technique provides a compromise between latency and glitch rate.
+     *
+     * @deprecated Use {@link AudioTrack.Builder#setPerformanceMode(int)} with
+     * {@link AudioTrack#PERFORMANCE_MODE_LOW_LATENCY} to control performance.
      */
     public final static int FLAG_LOW_LATENCY = 0x1 << 8;
 
+    /**
+     * @hide
+     * Flag requesting a deep buffer path when creating an {@code AudioTrack}.
+     *
+     * A deep buffer path, if available, may consume less power and is
+     * suitable for media playback where latency is not a concern.
+     * Use {@link AudioTrack.Builder#setPerformanceMode(int)} with
+     * {@link AudioTrack#PERFORMANCE_MODE_POWER_SAVING} to enable.
+     */
+    public final static int FLAG_DEEP_BUFFER = 0x1 << 9;
+
     private final static int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO |
             FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY |
-            FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY;
+            FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER;
     private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED |
             FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY;
 
@@ -541,6 +555,8 @@
 
         /**
          * Sets the combination of flags.
+         *
+         * This is a bitwise OR with the existing flags.
          * @param flags a combination of {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED},
          *    {@link AudioAttributes#FLAG_HW_AV_SYNC}.
          * @return the same Builder instance.
@@ -553,6 +569,17 @@
 
         /**
          * @hide
+         * Replaces flags.
+         * @param flags any combination of {@link AudioAttributes#FLAG_ALL}.
+         * @return the same Builder instance.
+         */
+        public Builder replaceFlags(int flags) {
+            mFlags = flags & AudioAttributes.FLAG_ALL;
+            return this;
+        }
+
+        /**
+         * @hide
          * Adds a Bundle of data
          * @param bundle a non-null Bundle
          * @return the same builder instance
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7c60385..fa904cd 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -730,61 +730,6 @@
     }
 
     /**
-     * @hide
-     */
-    public void handleKeyDown(KeyEvent event, int stream) {
-        int keyCode = event.getKeyCode();
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_VOLUME_UP:
-            case KeyEvent.KEYCODE_VOLUME_DOWN:
-                /*
-                 * Adjust the volume in on key down since it is more
-                 * responsive to the user.
-                 */
-                adjustSuggestedStreamVolume(
-                        keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                                ? ADJUST_RAISE
-                                : ADJUST_LOWER,
-                        stream,
-                        FLAG_SHOW_UI | FLAG_VIBRATE);
-                break;
-            case KeyEvent.KEYCODE_VOLUME_MUTE:
-                if (event.getRepeatCount() == 0) {
-                    MediaSessionLegacyHelper.getHelper(getContext())
-                            .sendVolumeKeyEvent(event, false);
-                }
-                break;
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void handleKeyUp(KeyEvent event, int stream) {
-        int keyCode = event.getKeyCode();
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_VOLUME_UP:
-            case KeyEvent.KEYCODE_VOLUME_DOWN:
-                /*
-                 * Play a sound. This is done on key up since we don't want the
-                 * sound to play when a user holds down volume down to mute.
-                 */
-                if (mUseVolumeKeySounds) {
-                    adjustSuggestedStreamVolume(
-                            ADJUST_SAME,
-                            stream,
-                            FLAG_PLAY_SOUND);
-                }
-                mVolumeKeyUpTime = SystemClock.uptimeMillis();
-                break;
-            case KeyEvent.KEYCODE_VOLUME_MUTE:
-                MediaSessionLegacyHelper.getHelper(getContext())
-                        .sendVolumeKeyEvent(event, false);
-                break;
-        }
-    }
-
-    /**
      * Indicates if the device implements a fixed volume policy.
      * <p>Some devices may not have volume control and may operate at a fixed volume,
      * and may not enable muting or changing the volume of audio streams.
@@ -4129,7 +4074,9 @@
      * currently connected to the system and meeting the criteria specified in the
      * <code>flags</code> parameter.
      * @param flags A set of bitflags specifying the criteria to test.
-     * @see {@link GET_DEVICES_OUTPUTS}, {@link GET_DEVICES_INPUTS} and {@link GET_DEVICES_ALL}.
+     * @see #GET_DEVICES_OUTPUTS
+     * @see #GET_DEVICES_INPUTS
+     * @see #GET_DEVICES_ALL
      * @return A (possibly zero-length) array of AudioDeviceInfo objects.
      */
     public AudioDeviceInfo[] getDevices(int flags) {
@@ -4200,7 +4147,9 @@
      * parameter.
      * This is an internal function. The public API front is getDevices(int).
      * @param flags A set of bitflags specifying the criteria to test.
-     * @see {@link GET_DEVICES_OUTPUTS}, {@link GET_DEVICES_INPUTS} and {@link GET_DEVICES_ALL}.
+     * @see #GET_DEVICES_OUTPUTS
+     * @see #GET_DEVICES_INPUTS
+     * @see #GET_DEVICES_ALL
      * @return A (possibly zero-length) array of AudioDeviceInfo objects.
      * @hide
      */
@@ -4246,7 +4195,7 @@
      * Unregisters an {@link AudioDeviceCallback} object which has been previously registered
      * to receive notifications of changes to the set of connected audio devices.
      * @param callback The {@link AudioDeviceCallback} object that was previously registered
-     * with {@link AudioManager#registerAudioDeviceCallback) to be unregistered.
+     * with {@link AudioManager#registerAudioDeviceCallback} to be unregistered.
      */
     public void unregisterAudioDeviceCallback(AudioDeviceCallback callback) {
         synchronized (mDeviceCallbacks) {
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index b38a07f..dd9c6a7 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -283,10 +283,19 @@
 
     /**
      * @hide
-     * FIXME return a player proxy instead, make systemApi
-     * @return
+     * Return a proxy for the player associated with this playback configuration
+     * @return a proxy player
      */
-    public IPlayer getPlayerProxy() {
+    @SystemApi
+    public PlayerProxy getPlayerProxy() {
+        return mIPlayerShell == null ? null : new PlayerProxy(this);
+    }
+
+    /**
+     * @hide
+     * @return the IPlayer interface for the associated player
+     */
+    IPlayer getIPlayer() {
         return mIPlayerShell == null ? null : mIPlayerShell.getIPlayer();
     }
 
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 031ac06..b23f5fd 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -214,6 +214,66 @@
      */
     public final static int WRITE_NON_BLOCKING = 1;
 
+    /** @hide */
+    @IntDef({
+        PERFORMANCE_MODE_NONE,
+        PERFORMANCE_MODE_LOW_LATENCY,
+        PERFORMANCE_MODE_POWER_SAVING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PerformanceMode {}
+
+    /**
+     * Default performance mode for an {@link AudioTrack}.
+     */
+    public static final int PERFORMANCE_MODE_NONE = 0;
+
+    /**
+     * Low latency performance mode for an {@link AudioTrack}.
+     * If the device supports it, this mode
+     * enables a lower latency path through to the audio output sink.
+     * Effects may no longer work with such an {@code AudioTrack} and
+     * the sample rate must match that of the output sink.
+     * <p>
+     * Applications should be aware that low latency requires careful
+     * buffer management, with smaller chunks of audio data written by each
+     * {@code write()} call.
+     * <p>
+     * If this flag is used without specifying a {@code bufferSizeInBytes} then the
+     * {@code AudioTrack}'s actual buffer size may be too small.
+     * It is recommended that a fairly
+     * large buffer should be specified when the {@code AudioTrack} is created.
+     * Then the actual size can be reduced by calling
+     * {@link #setBufferSizeInFrames(int)}. The buffer size can be optimized
+     * by lowering it after each {@code write()} call until the audio glitches,
+     * which is detected by calling
+     * {@link #getUnderrunCount()}. Then the buffer size can be increased
+     * until there are no glitches.
+     * This tuning step should be done while playing silence.
+     * This technique provides a compromise between latency and glitch rate.
+     */
+    public static final int PERFORMANCE_MODE_LOW_LATENCY = 1;
+
+    /**
+     * Power saving performance mode for an {@link AudioTrack}.
+     * If the device supports it, this
+     * mode will enable a lower power path to the audio output sink.
+     * In addition, this lower power path typically will have
+     * deeper internal buffers and better underrun resistance,
+     * with a tradeoff of higher latency.
+     * <p>
+     * In this mode, applications should attempt to use a larger buffer size
+     * and deliver larger chunks of audio data per {@code write()} call.
+     * Use {@link #getBufferSizeInFrames()} to determine
+     * the actual buffer size of the {@code AudioTrack} as it may have increased
+     * to accommodate a deeper buffer.
+     */
+    public static final int PERFORMANCE_MODE_POWER_SAVING = 2;
+
+    // keep in sync with system/media/audio/include/system/audio-base.h
+    private static final int AUDIO_OUTPUT_FLAG_FAST = 0x4;
+    private static final int AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8;
+
     //--------------------------------------------------------------------------
     // Member variables
     //--------------------
@@ -648,6 +708,7 @@
         private int mBufferSizeInBytes;
         private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
         private int mMode = MODE_STREAM;
+        private int mPerformanceMode = PERFORMANCE_MODE_NONE;
 
         /**
          * Constructs a new Builder with the default values as described above.
@@ -752,6 +813,32 @@
         }
 
         /**
+         * Sets the {@link AudioTrack} performance mode.  This is an advisory request which
+         * may not be supported by the particular device, and the framework is free
+         * to ignore such request if it is incompatible with other requests or hardware.
+         *
+         * @param performanceMode one of
+         * {@link AudioTrack#PERFORMANCE_MODE_NONE},
+         * {@link AudioTrack#PERFORMANCE_MODE_LOW_LATENCY},
+         * or {@link AudioTrack#PERFORMANCE_MODE_POWER_SAVING}.
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException if {@code performanceMode} is not valid.
+         */
+        public @NonNull Builder setPerformanceMode(@PerformanceMode int performanceMode) {
+            switch (performanceMode) {
+                case PERFORMANCE_MODE_NONE:
+                case PERFORMANCE_MODE_LOW_LATENCY:
+                case PERFORMANCE_MODE_POWER_SAVING:
+                    mPerformanceMode = performanceMode;
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                            "Invalid performance mode " + performanceMode);
+            }
+            return this;
+        }
+
+        /**
          * Builds an {@link AudioTrack} instance initialized with all the parameters set
          * on this <code>Builder</code>.
          * @return a new successfully initialized {@link AudioTrack} instance.
@@ -765,6 +852,25 @@
                         .setUsage(AudioAttributes.USAGE_MEDIA)
                         .build();
             }
+            switch (mPerformanceMode) {
+            case PERFORMANCE_MODE_LOW_LATENCY:
+                mAttributes = new AudioAttributes.Builder(mAttributes)
+                    .replaceFlags((mAttributes.getAllFlags()
+                            | AudioAttributes.FLAG_LOW_LATENCY)
+                            & ~AudioAttributes.FLAG_DEEP_BUFFER)
+                    .build();
+                break;
+            case PERFORMANCE_MODE_NONE:
+                break;
+            case PERFORMANCE_MODE_POWER_SAVING:
+                mAttributes = new AudioAttributes.Builder(mAttributes)
+                .replaceFlags((mAttributes.getAllFlags()
+                        | AudioAttributes.FLAG_DEEP_BUFFER)
+                        & ~AudioAttributes.FLAG_LOW_LATENCY)
+                .build();
+                break;
+            }
+
             if (mFormat == null) {
                 mFormat = new AudioFormat.Builder()
                         .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
@@ -1278,6 +1384,27 @@
     }
 
     /**
+     * Returns the current performance mode of the {@link AudioTrack}.
+     *
+     * @return one of {@link AudioTrack#PERFORMANCE_MODE_NONE},
+     * {@link AudioTrack#PERFORMANCE_MODE_LOW_LATENCY},
+     * or {@link AudioTrack#PERFORMANCE_MODE_POWER_SAVING}.
+     * Use {@link AudioTrack.Builder#setPerformanceMode}
+     * in the {@link AudioTrack.Builder} to enable a performance mode.
+     * @throws IllegalStateException if track is not initialized.
+     */
+    public @PerformanceMode int getPerformanceMode() {
+        final int flags = native_get_flags();
+        if ((flags & AUDIO_OUTPUT_FLAG_FAST) != 0) {
+            return PERFORMANCE_MODE_LOW_LATENCY;
+        } else if ((flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) {
+            return PERFORMANCE_MODE_POWER_SAVING;
+        } else {
+            return PERFORMANCE_MODE_NONE;
+        }
+    }
+
+    /**
      *  Returns the output sample rate in Hz for the specified stream type.
      */
     static public int getNativeOutputSampleRate(int streamType) {
@@ -2855,6 +2982,8 @@
 
     private native final int native_get_underrun_count();
 
+    private native final int native_get_flags();
+
     // longArray must be a non-null array of length >= 2
     // [0] is assigned the frame position
     // [1] is assigned the time in CLOCK_MONOTONIC nanoseconds
diff --git a/media/java/android/media/BufferingParams.aidl b/media/java/android/media/BufferingParams.aidl
new file mode 100644
index 0000000..d156d44
--- /dev/null
+++ b/media/java/android/media/BufferingParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable BufferingParams;
diff --git a/media/java/android/media/BufferingParams.java b/media/java/android/media/BufferingParams.java
new file mode 100644
index 0000000..fdcd6ba
--- /dev/null
+++ b/media/java/android/media/BufferingParams.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Structure for source buffering management params.
+ *
+ * Used by {@link MediaPlayer#getDefaultBufferingParams()},
+ * {@link MediaPlayer#getBufferingParams()} and
+ * {@link MediaPlayer#setBufferingParams(BufferingParams)}
+ * to control source buffering behavior.
+ *
+ * <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering
+ * (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer}
+ * is playing back source). {@link BufferingParams} includes mode and corresponding
+ * watermarks for each stage of source buffering. The watermarks could be either size
+ * based (in milliseconds), or time based (in kilobytes) or both, depending on the mode.
+ *
+ * <p>There are 4 buffering modes: {@link #BUFFERING_MODE_NONE},
+ * {@link #BUFFERING_MODE_TIME_ONLY}, {@link #BUFFERING_MODE_SIZE_ONLY} and
+ * {@link #BUFFERING_MODE_TIME_THEN_SIZE}.
+ * {@link MediaPlayer} source component has default buffering modes which can be queried
+ * by calling {@link MediaPlayer#getDefaultBufferingParams()}.
+ * Users should always use those default modes or their downsized version when trying to
+ * change buffering params. For example, {@link #BUFFERING_MODE_TIME_THEN_SIZE} can be
+ * downsized to {@link #BUFFERING_MODE_NONE}, {@link #BUFFERING_MODE_TIME_ONLY} or
+ * {@link #BUFFERING_MODE_SIZE_ONLY}. But {@link #BUFFERING_MODE_TIME_ONLY} can not be
+ * downsized to {@link #BUFFERING_MODE_SIZE_ONLY}.
+ * <ul>
+ * <li><strong>initial buffering stage:</strong> has one watermark which is used when
+ * {@link MediaPlayer} is being prepared. When cached data amount exceeds this watermark,
+ * {@link MediaPlayer} is prepared.</li>
+ * <li><strong>rebuffering stage:</strong> has two watermarks, low and high, which are
+ * used when {@link MediaPlayer} is playing back content.
+ * <ul>
+ * <li> When cached data amount exceeds high watermark, {@link MediaPlayer} will pause
+ * buffering. Buffering will resume when cache runs below some limit which could be low
+ * watermark or some intermediate value decided by the source component.</li>
+ * <li> When cached data amount runs below low watermark, {@link MediaPlayer} will paused
+ * playback. Playback will resume when cached data amount exceeds high watermark
+ * or reaches end of stream.</li>
+ * </ul>
+ * </ul>
+ * <p>Users should use {@link Builder} to change {@link BufferingParams}.
+ */
+public final class BufferingParams implements Parcelable {
+    /**
+     * This mode indicates that source buffering is not supported.
+     */
+    public static final int BUFFERING_MODE_NONE = 0;
+    /**
+     * This mode indicates that only time based source buffering is supported. This means
+     * the watermark(s) are time based.
+     */
+    public static final int BUFFERING_MODE_TIME_ONLY = 1;
+    /**
+     * This mode indicates that only size based source buffering is supported. This means
+     * the watermark(s) are size based.
+     */
+    public static final int BUFFERING_MODE_SIZE_ONLY = 2;
+    /**
+     * This mode indicates that both time and size based source buffering are supported,
+     * and time based calculation precedes size based. Size based calculation will be used
+     * only when time information is not available from the source.
+     */
+    public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3;
+
+    /** @hide */
+    @IntDef(
+        value = {
+                BUFFERING_MODE_NONE,
+                BUFFERING_MODE_TIME_ONLY,
+                BUFFERING_MODE_SIZE_ONLY,
+                BUFFERING_MODE_TIME_THEN_SIZE,
+        }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BufferingMode {}
+
+    private static final int BUFFERING_NO_WATERMARK = -1;
+
+    // params
+    private int mInitialBufferingMode = BUFFERING_MODE_NONE;
+    private int mRebufferingMode = BUFFERING_MODE_NONE;
+
+    private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
+    private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
+
+    private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
+    private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
+    private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
+    private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+
+    private BufferingParams() {
+    }
+
+    /**
+     * Return the initial buffering mode used when {@link MediaPlayer} is being prepared.
+     * @return one of the values that can be set in {@link Builder#setInitialBufferingMode(int)}
+     */
+    public int getInitialBufferingMode() {
+        return mInitialBufferingMode;
+    }
+
+    /**
+     * Return the rebuffering mode used when {@link MediaPlayer} is playing back source.
+     * @return one of the values that can be set in {@link Builder#setRebufferingMode(int)}
+     */
+    public int getRebufferingMode() {
+        return mRebufferingMode;
+    }
+
+    /**
+     * Return the time based initial buffering watermark in milliseconds.
+     * It is meaningful only when initial buffering mode obatined from
+     * {@link #getInitialBufferingMode()} is time based.
+     * @return time based initial buffering watermark in milliseconds
+     */
+    public int getInitialBufferingWatermarkMs() {
+        return mInitialWatermarkMs;
+    }
+
+    /**
+     * Return the size based initial buffering watermark in kilobytes.
+     * It is meaningful only when initial buffering mode obatined from
+     * {@link #getInitialBufferingMode()} is size based.
+     * @return size based initial buffering watermark in kilobytes
+     */
+    public int getInitialBufferingWatermarkKB() {
+        return mInitialWatermarkKB;
+    }
+
+    /**
+     * Return the time based low watermark in milliseconds for rebuffering.
+     * It is meaningful only when rebuffering mode obatined from
+     * {@link #getRebufferingMode()} is time based.
+     * @return time based low watermark for rebuffering in milliseconds
+     */
+    public int getRebufferingWatermarkLowMs() {
+        return mRebufferingWatermarkLowMs;
+    }
+
+    /**
+     * Return the time based high watermark in milliseconds for rebuffering.
+     * It is meaningful only when rebuffering mode obatined from
+     * {@link #getRebufferingMode()} is time based.
+     * @return time based high watermark for rebuffering in milliseconds
+     */
+    public int getRebufferingWatermarkHighMs() {
+        return mRebufferingWatermarkHighMs;
+    }
+
+    /**
+     * Return the size based low watermark in kilobytes for rebuffering.
+     * It is meaningful only when rebuffering mode obatined from
+     * {@link #getRebufferingMode()} is size based.
+     * @return size based low watermark for rebuffering in kilobytes
+     */
+    public int getRebufferingWatermarkLowKB() {
+        return mRebufferingWatermarkLowKB;
+    }
+
+    /**
+     * Return the size based high watermark in kilobytes for rebuffering.
+     * It is meaningful only when rebuffering mode obatined from
+     * {@link #getRebufferingMode()} is size based.
+     * @return size based high watermark for rebuffering in kilobytes
+     */
+    public int getRebufferingWatermarkHighKB() {
+        return mRebufferingWatermarkHighKB;
+    }
+
+    /**
+     * Builder class for {@link BufferingParams} objects.
+     * <p> Here is an example where <code>Builder</code> is used to define the
+     * {@link BufferingParams} to be used by a {@link MediaPlayer} instance:
+     *
+     * <pre class="prettyprint">
+     * BufferingParams myParams = mediaplayer.getDefaultBufferingParams();
+     * myParams = new BufferingParams.Builder(myParams)
+     *             .setInitialBufferingWatermarkMs(10000)
+     *             .build();
+     * mediaplayer.setBufferingParams(myParams);
+     * </pre>
+     */
+    public static class Builder {
+        private int mInitialBufferingMode = BUFFERING_MODE_NONE;
+        private int mRebufferingMode = BUFFERING_MODE_NONE;
+
+        private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
+        private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
+
+        private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
+        private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
+        private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
+        private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+
+        /**
+         * Constructs a new Builder with the defaults.
+         * By default, both initial buffering mode and rebuffering mode are
+         * {@link BufferingParams#BUFFERING_MODE_NONE}, and all watermarks are -1.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given {@link BufferingParams} instance
+         * @param bp the {@link BufferingParams} object whose data will be reused
+         * in the new Builder.
+         */
+        public Builder(BufferingParams bp) {
+            mInitialBufferingMode = bp.mInitialBufferingMode;
+            mRebufferingMode = bp.mRebufferingMode;
+
+            mInitialWatermarkMs = bp.mInitialWatermarkMs;
+            mInitialWatermarkKB = bp.mInitialWatermarkKB;
+
+            mRebufferingWatermarkLowMs = bp.mRebufferingWatermarkLowMs;
+            mRebufferingWatermarkHighMs = bp.mRebufferingWatermarkHighMs;
+            mRebufferingWatermarkLowKB = bp.mRebufferingWatermarkLowKB;
+            mRebufferingWatermarkHighKB = bp.mRebufferingWatermarkHighKB;
+        }
+
+        /**
+         * Combines all of the fields that have been set and return a new
+         * {@link BufferingParams} object. <code>IllegalStateException</code> will be
+         * thrown if there is conflict between fields.
+         * @return a new {@link BufferingParams} object
+         */
+        public BufferingParams build() {
+            if (isTimeBasedMode(mRebufferingMode)
+                    && mRebufferingWatermarkLowMs > mRebufferingWatermarkHighMs) {
+                throw new IllegalStateException("Illegal watermark:"
+                        + mRebufferingWatermarkLowMs + " : " + mRebufferingWatermarkHighMs);
+            }
+            if (isSizeBasedMode(mRebufferingMode)
+                    && mRebufferingWatermarkLowKB > mRebufferingWatermarkHighKB) {
+                throw new IllegalStateException("Illegal watermark:"
+                        + mRebufferingWatermarkLowKB + " : " + mRebufferingWatermarkHighKB);
+            }
+
+            BufferingParams bp = new BufferingParams();
+            bp.mInitialBufferingMode = mInitialBufferingMode;
+            bp.mRebufferingMode = mRebufferingMode;
+
+            bp.mInitialWatermarkMs = mInitialWatermarkMs;
+            bp.mInitialWatermarkKB = mInitialWatermarkKB;
+
+            bp.mRebufferingWatermarkLowMs = mRebufferingWatermarkLowMs;
+            bp.mRebufferingWatermarkHighMs = mRebufferingWatermarkHighMs;
+            bp.mRebufferingWatermarkLowKB = mRebufferingWatermarkLowKB;
+            bp.mRebufferingWatermarkHighKB = mRebufferingWatermarkHighKB;
+            return bp;
+        }
+
+        private boolean isTimeBasedMode(int mode) {
+            return (mode == BUFFERING_MODE_TIME_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
+        }
+
+        private boolean isSizeBasedMode(int mode) {
+            return (mode == BUFFERING_MODE_SIZE_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
+        }
+
+        /**
+         * Sets the initial buffering mode.
+         * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
+         *     {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
+         *     {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
+         *     {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+         * @return the same Builder instance.
+         */
+        public Builder setInitialBufferingMode(@BufferingMode int mode) {
+            switch (mode) {
+                case BUFFERING_MODE_NONE:
+                case BUFFERING_MODE_TIME_ONLY:
+                case BUFFERING_MODE_SIZE_ONLY:
+                case BUFFERING_MODE_TIME_THEN_SIZE:
+                     mInitialBufferingMode = mode;
+                     break;
+                default:
+                     throw new IllegalArgumentException("Illegal buffering mode " + mode);
+            }
+            return this;
+        }
+
+        /**
+         * Sets the rebuffering mode.
+         * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
+         *     {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
+         *     {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
+         *     {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingMode(@BufferingMode int mode) {
+            switch (mode) {
+                case BUFFERING_MODE_NONE:
+                case BUFFERING_MODE_TIME_ONLY:
+                case BUFFERING_MODE_SIZE_ONLY:
+                case BUFFERING_MODE_TIME_THEN_SIZE:
+                     mRebufferingMode = mode;
+                     break;
+                default:
+                     throw new IllegalArgumentException("Illegal buffering mode " + mode);
+            }
+            return this;
+        }
+
+        /**
+         * Sets the time based watermark in milliseconds for initial buffering.
+         * @param watermarkMs time based watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setInitialBufferingWatermarkMs(int watermarkMs) {
+            mInitialWatermarkMs = watermarkMs;
+            return this;
+        }
+
+        /**
+         * Sets the size based watermark in kilobytes for initial buffering.
+         * @param watermarkKB size based watermark in kilobytes
+         * @return the same Builder instance.
+         */
+        public Builder setInitialBufferingWatermarkKB(int watermarkKB) {
+            mInitialWatermarkKB = watermarkKB;
+            return this;
+        }
+
+        /**
+         * Sets the time based low watermark in milliseconds for rebuffering.
+         * @param watermarkMs time based low watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarkLowMs(int watermarkMs) {
+            mRebufferingWatermarkLowMs = watermarkMs;
+            return this;
+        }
+
+        /**
+         * Sets the time based high watermark in milliseconds for rebuffering.
+         * @param watermarkMs time based high watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarkHighMs(int watermarkMs) {
+            mRebufferingWatermarkHighMs = watermarkMs;
+            return this;
+        }
+
+        /**
+         * Sets the size based low watermark in milliseconds for rebuffering.
+         * @param watermarkKB size based low watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarkLowKB(int watermarkKB) {
+            mRebufferingWatermarkLowKB = watermarkKB;
+            return this;
+        }
+
+        /**
+         * Sets the size based high watermark in milliseconds for rebuffering.
+         * @param watermarkKB size based high watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarkHighKB(int watermarkKB) {
+            mRebufferingWatermarkHighKB = watermarkKB;
+            return this;
+        }
+
+        /**
+         * Sets the time based low and high watermarks in milliseconds for rebuffering.
+         * @param lowWatermarkMs time based low watermark in milliseconds
+         * @param highWatermarkMs time based high watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarksMs(int lowWatermarkMs, int highWatermarkMs) {
+            mRebufferingWatermarkLowMs = lowWatermarkMs;
+            mRebufferingWatermarkHighMs = highWatermarkMs;
+            return this;
+        }
+
+        /**
+         * Sets the size based low and high watermarks in kilobytes for rebuffering.
+         * @param lowWatermarkKB size based low watermark in kilobytes
+         * @param highWatermarkKB size based high watermark in kilobytes
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarksKB(int lowWatermarkKB, int highWatermarkKB) {
+            mRebufferingWatermarkLowKB = lowWatermarkKB;
+            mRebufferingWatermarkHighKB = highWatermarkKB;
+            return this;
+        }
+    }
+
+    private BufferingParams(Parcel in) {
+        mInitialBufferingMode = in.readInt();
+        mRebufferingMode = in.readInt();
+
+        mInitialWatermarkMs = in.readInt();
+        mInitialWatermarkKB = in.readInt();
+
+        mRebufferingWatermarkLowMs = in.readInt();
+        mRebufferingWatermarkHighMs = in.readInt();
+        mRebufferingWatermarkLowKB = in.readInt();
+        mRebufferingWatermarkHighKB = in.readInt();
+    }
+
+    public static final Parcelable.Creator<BufferingParams> CREATOR =
+            new Parcelable.Creator<BufferingParams>() {
+                @Override
+                public BufferingParams createFromParcel(Parcel in) {
+                    return new BufferingParams(in);
+                }
+
+                @Override
+                public BufferingParams[] newArray(int size) {
+                    return new BufferingParams[size];
+                }
+            };
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mInitialBufferingMode);
+        dest.writeInt(mRebufferingMode);
+
+        dest.writeInt(mInitialWatermarkMs);
+        dest.writeInt(mInitialWatermarkKB);
+
+        dest.writeInt(mRebufferingWatermarkLowMs);
+        dest.writeInt(mRebufferingWatermarkHighMs);
+        dest.writeInt(mRebufferingWatermarkLowKB);
+        dest.writeInt(mRebufferingWatermarkHighKB);
+    }
+}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 52d5b7ca..71f7790 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1597,8 +1597,8 @@
                         final Rational[] rationalArray = new Rational[values.length];
                         for (int j = 0; j < values.length; ++j) {
                             final String[] numbers = values[j].split("/");
-                            rationalArray[j] = new Rational(Long.parseLong(numbers[0]),
-                                    Long.parseLong(numbers[1]));
+                            rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
+                                    (long) Double.parseDouble(numbers[1]));
                         }
                         mAttributes[i].put(tag,
                                 ExifAttribute.createURational(rationalArray, mExifByteOrder));
@@ -1609,8 +1609,8 @@
                         final Rational[] rationalArray = new Rational[values.length];
                         for (int j = 0; j < values.length; ++j) {
                             final String[] numbers = values[j].split("/");
-                            rationalArray[j] = new Rational(Long.parseLong(numbers[0]),
-                                    Long.parseLong(numbers[1]));
+                            rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
+                                    (long) Double.parseDouble(numbers[1]));
                         }
                         mAttributes[i].put(tag,
                                 ExifAttribute.createSRational(rationalArray, mExifByteOrder));
@@ -3442,8 +3442,8 @@
             String[] rationalNumber = entryValue.split("/");
             if (rationalNumber.length == 2) {
                 try {
-                    long numerator = Long.parseLong(rationalNumber[0]);
-                    long denominator = Long.parseLong(rationalNumber[1]);
+                    long numerator = (long) Double.parseDouble(rationalNumber[0]);
+                    long denominator = (long) Double.parseDouble(rationalNumber[1]);
                     if (numerator < 0L || denominator < 0L) {
                         return new Pair<>(IFD_FORMAT_SRATIONAL, -1);
                     }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 432e77c..a76a328 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -192,5 +192,7 @@
 
     oneway void releasePlayer(in int piid);
 
+    void disableRingtoneSync();
+
     // WARNING: read warning at top of file, it is recommended to add new methods at the end
 }
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index e481aa1..a1f816b 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -95,7 +95,8 @@
     public @interface Format {}
 
     // All the native functions are listed here.
-    private static native long nativeSetup(@NonNull FileDescriptor fd, int format);
+    private static native long nativeSetup(@NonNull FileDescriptor fd, int format)
+            throws IllegalArgumentException, IOException;
     private static native void nativeRelease(long nativeObject);
     private static native void nativeStart(long nativeObject);
     private static native void nativeStop(long nativeObject);
@@ -134,19 +135,13 @@
         if (path == null) {
             throw new IllegalArgumentException("path must not be null");
         }
-        if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 &&
-                format != OutputFormat.MUXER_OUTPUT_WEBM) {
-            throw new IllegalArgumentException("format is invalid");
-        }
         // Use RandomAccessFile so we can open the file with RW access;
         // RW access allows the native writer to memory map the output file.
         RandomAccessFile file = null;
         try {
             file = new RandomAccessFile(path, "rws");
             FileDescriptor fd = file.getFD();
-            mNativeObject = nativeSetup(fd, format);
-            mState = MUXER_STATE_INITIALIZED;
-            mCloseGuard.open("release");
+            setUpMediaMuxer(fd, format);
         } finally {
             if (file != null) {
                 file.close();
@@ -155,6 +150,32 @@
     }
 
     /**
+     * Constructor.
+     * Creates a media muxer that writes to the specified FileDescriptor. File descriptor
+     * must be seekable and writable. Application should not use the file referenced
+     * by this file descriptor until {@link #stop}. It is the application's responsibility
+     * to close the file descriptor. It is safe to do so as soon as this call returns.
+     * @param fd The FileDescriptor of the output media file.
+     * @param format The format of the output media file.
+     * @see android.media.MediaMuxer.OutputFormat
+     * @throws IllegalArgumentException if fd is invalid or format is not supported.
+     * @throws IOException if failed to open the file for write.
+     */
+    public MediaMuxer(@NonNull FileDescriptor fd, @Format int format) throws IOException {
+        setUpMediaMuxer(fd, format);
+    }
+
+    private void setUpMediaMuxer(@NonNull FileDescriptor fd, @Format int format) throws IOException {
+        if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 &&
+                format != OutputFormat.MUXER_OUTPUT_WEBM) {
+            throw new IllegalArgumentException("format: " + format + " is invalid");
+        }
+        mNativeObject = nativeSetup(fd, format);
+        mState = MUXER_STATE_INITIALIZED;
+        mCloseGuard.open("release");
+    }
+
+    /**
      * Sets the orientation hint for output video playback.
      * <p>This method should be called before {@link #start}. Calling this
      * method will not rotate the video frame when muxer is generating the file,
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e3a0f25..4023400 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -45,6 +45,7 @@
 import android.widget.VideoView;
 import android.graphics.SurfaceTexture;
 import android.media.AudioManager;
+import android.media.BufferingParams;
 import android.media.MediaFormat;
 import android.media.MediaTimeProvider;
 import android.media.PlaybackParams;
@@ -479,6 +480,11 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
+ * <tr><td>setBufferingParams</p></td>
+ *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}</p></td>
+ *     <td>{Idle} </p></td>
+ *     <td>This method does not change the object state.
+ *         </p></td></tr>
  * <tr><td>setPlaybackParams</p></td>
  *     <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td>
  *     <td>{Idle, Stopped} </p></td>
@@ -1390,6 +1396,45 @@
     public native boolean isPlaying();
 
     /**
+     * Gets the default buffering management params.
+     * Calling it only after {@code setDataSource} has been called.
+     * Each type of data source might have different set of default params.
+     *
+     * @return the default buffering management params supported by the source component.
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized, or {@code setDataSource} has not been called.
+     */
+    @NonNull
+    public native BufferingParams getDefaultBufferingParams();
+
+    /**
+     * Gets the current buffering management params used by the source component.
+     * Calling it only after {@code setDataSource} has been called.
+     *
+     * @return the current buffering management params used by the source component.
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized, or {@code setDataSource} has not been called.
+     */
+    @NonNull
+    public native BufferingParams getBufferingParams();
+
+    /**
+     * Sets buffering management params.
+     * The object sets its internal BufferingParams to the input, except that the input is
+     * invalid or not supported.
+     * Call it only after {@code setDataSource} has been called.
+     * Users should only use supported mode returned by {@link #getDefaultBufferingParams()}
+     * or its downsized version as described in {@link BufferingParams}.
+     *
+     * @param params the buffering management params.
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized or has been released, or {@code setDataSource} has not been called.
+     * @throws IllegalArgumentException if params is invalid or not supported.
+     */
+    public native void setBufferingParams(@NonNull BufferingParams params);
+
+    /**
      * Change playback speed of audio by resampling the audio.
      * <p>
      * Specifies resampling as audio mode for variable rate playback, i.e.,
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 64576ec..3e88450 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -801,6 +801,30 @@
     }
 
     /**
+     * Sets the next output file descriptor to be used when the maximum filesize is reached
+     * on the prior output {@link #setOutputFile} or {@link #setNextOutputFile}). File descriptor
+     * must be seekable and writable. After setting the next output file, application should not
+     * use the file referenced by this file descriptor until {@link #stop}. It is the application's
+     * responsibility to close the file descriptor. It is safe to do so as soon as this call returns.
+     * Application must call this after receiving on the
+     * {@link android.media.MediaRecorder.OnInfoListener} a "what" code of
+     * {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING} and before receiving a "what" code of
+     * {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}. The file is not used until switching to
+     * that output. Application will receive{@link #MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED}
+     * when the next output file is used. Application will not be able to set a new output file if
+     * the previous one has not been used. Application is responsible for cleaning up unused files
+     * after {@link #stop} is called.
+     *
+     * @param fd an open file descriptor to be written into.
+     * @throws IllegalStateException if it is called before prepare().
+     * @throws IOException if setNextOutputFile fails otherwise.
+     */
+    public void setNextOutputFile(FileDescriptor fd) throws IllegalStateException, IOException
+    {
+        _setNextOutputFile(fd);
+    }
+
+    /**
      * Sets the path of the output file to be produced. Call this after
      * setOutputFormat() but before prepare().
      *
@@ -814,9 +838,38 @@
         mPath = path;
     }
 
+    /**
+     * Sets the next output file path to be used when the maximum filesize is reached
+     * on the prior output {@link #setOutputFile} or {@link #setNextOutputFile}). File should
+     * be seekable. After setting the next output file, application should not use the file
+     * referenced by this file descriptor until {@link #stop}. Application must call this
+     * after receiving on the {@link android.media.MediaRecorder.OnInfoListener} a "what" code of
+     * {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING} and before receiving a "what" code of
+     * {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}. The file is not used until switching to
+     * that output. Application will receive {@link #MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED}
+     * when the next output file is used. Application will not be able to set a new output file if
+     * the previous one has not been used. Application is responsible for cleaning up unused files
+     * after {@link #stop} is called.
+     *
+     * @param  path The pathname to use.
+     * @throws IllegalStateException if it is called before prepare().
+     * @throws IOException if setNextOutputFile fails otherwise.
+     */
+    public void setNextOutputFile(String path) throws IllegalStateException, IOException
+    {
+        if (path != null) {
+            RandomAccessFile file = new RandomAccessFile(path, "rws");
+            try {
+                _setNextOutputFile(file.getFD());
+            } finally {
+                file.close();
+            }
+        }
+    }
+
     // native implementation
-    private native void _setOutputFile(FileDescriptor fd, long offset, long length)
-        throws IllegalStateException, IOException;
+    private native void _setOutputFile(FileDescriptor fd) throws IllegalStateException, IOException;
+    private native void _setNextOutputFile(FileDescriptor fd) throws IllegalStateException, IOException;
     private native void _prepare() throws IllegalStateException, IOException;
 
     /**
@@ -833,12 +886,12 @@
         if (mPath != null) {
             RandomAccessFile file = new RandomAccessFile(mPath, "rws");
             try {
-                _setOutputFile(file.getFD(), 0, 0);
+                _setOutputFile(file.getFD());
             } finally {
                 file.close();
             }
         } else if (mFd != null) {
-            _setOutputFile(mFd, 0, 0);
+            _setOutputFile(mFd);
         } else {
             throw new IOException("No valid output file");
         }
@@ -980,9 +1033,26 @@
      */
     public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800;
     /** A maximum filesize had been setup and has now been reached.
+     * Note: This event will not be sent if application already set
+     * next output file through {@link #setNextOutputFile}.
      * @see android.media.MediaRecorder.OnInfoListener
      */
     public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801;
+    /** A maximum filesize had been setup and current recorded file size
+     * has reached 90% of the limit. This is sent once per file upon
+     * reaching/passing the 90% limit. To continue the recording, applicaiton
+     * should use {@link #setNextOutputFile} to set the next output file.
+     * Otherwise, recording will stop when reaching maximum file size.
+     * @see android.media.MediaRecorder.OnInfoListener
+     */
+    public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING = 802;
+    /** A maximum filesize had been reached and MediaRecorder has switched
+     * output to a new file set by application {@link #setNextOutputFile}.
+     * For best practice, application should use this event to keep track
+     * of whether the file previously set has been used or not.
+     * @see android.media.MediaRecorder.OnInfoListener
+     */
+    public static final int MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED = 803;
 
     /** informational events for individual tracks, for testing purpose.
      * The track informational event usually contains two parts in the ext1
diff --git a/media/java/android/media/MediaSyncEvent.java b/media/java/android/media/MediaSyncEvent.java
index 31af6b5..04448f0 100644
--- a/media/java/android/media/MediaSyncEvent.java
+++ b/media/java/android/media/MediaSyncEvent.java
@@ -36,7 +36,7 @@
      * The corresponding action is triggered only when the presentation is completed
      * (meaning the media has been presented to the user) on the specified session.
      * A synchronization of this type requires a source audio session ID to be set via
-     * {@link #setAudioSessionId(int) method.
+     * {@link #setAudioSessionId(int)} method.
      */
     public static final int SYNC_EVENT_PRESENTATION_COMPLETE =
                                                     AudioSystem.SYNC_EVENT_PRESENTATION_COMPLETE;
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 9819395..bd0a1b4 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -78,6 +78,7 @@
         }
         mAttributes = attr;
         mImplType = implType;
+        mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
     };
 
     /**
@@ -138,7 +139,8 @@
     void baseStart() {
         if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); }
         try {
-            getService().playerEvent(mPlayerIId, AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
+            mState = AudioPlaybackConfiguration.PLAYER_STATE_STARTED;
+            getService().playerEvent(mPlayerIId, mState);
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
         }
@@ -152,7 +154,8 @@
     void basePause() {
         if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); }
         try {
-            getService().playerEvent(mPlayerIId, AudioPlaybackConfiguration.PLAYER_STATE_PAUSED);
+            mState = AudioPlaybackConfiguration.PLAYER_STATE_PAUSED;
+            getService().playerEvent(mPlayerIId, mState);
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to audio service, PAUSED state will not be tracked", e);
         }
@@ -161,7 +164,8 @@
     void baseStop() {
         if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); }
         try {
-            getService().playerEvent(mPlayerIId, AudioPlaybackConfiguration.PLAYER_STATE_STOPPED);
+            mState = AudioPlaybackConfiguration.PLAYER_STATE_STOPPED;
+            getService().playerEvent(mPlayerIId, mState);
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to audio service, STOPPED state will not be tracked", e);
         }
@@ -193,7 +197,7 @@
      * Releases AppOps related resources.
      */
     void baseRelease() {
-        if (DEBUG) { Log.v(TAG, "baseRelease() piid=" + mPlayerIId); }
+        if (DEBUG) { Log.v(TAG, "baseRelease() piid=" + mPlayerIId + " state=" + mState); }
         try {
             if (mState != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) {
                 getService().releasePlayer(mPlayerIId);
diff --git a/media/java/android/media/PlayerProxy.java b/media/java/android/media/PlayerProxy.java
new file mode 100644
index 0000000..171be27
--- /dev/null
+++ b/media/java/android/media/PlayerProxy.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.IllegalArgumentException;
+import java.util.Objects;
+
+/**
+ * Class to remotely control a player.
+ * @hide
+ */
+@SystemApi
+public class PlayerProxy {
+
+    private final static String TAG = "PlayerProxy";
+    private final static boolean DEBUG = false;
+
+    private final AudioPlaybackConfiguration mConf; // never null
+
+    /**
+     * @hide
+     * Constructor. Proxy for this player associated with this AudioPlaybackConfiguration
+     * @param conf the configuration being proxied.
+     */
+    PlayerProxy(@NonNull AudioPlaybackConfiguration apc) {
+        if (apc == null) {
+            throw new IllegalArgumentException("Illegal null AudioPlaybackConfiguration");
+        }
+        mConf = apc;
+    };
+
+    //=====================================================================
+    // Methods matching the IPlayer interface
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void start() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().start();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for start operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void pause() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().pause();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for pause operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void stop() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().stop();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for stop operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void setVolume(float vol) throws IllegalStateException {
+        try {
+            mConf.getIPlayer().setVolume(vol);
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for setVolume operation, player already released?", e);
+        }
+    }
+
+}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 7614999..8a1027b 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -33,8 +33,11 @@
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.Uri;
 import android.os.Environment;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.MediaStore;
@@ -850,6 +853,18 @@
     public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
         final ContentResolver resolver = context.getContentResolver();
 
+        if (Settings.Secure.getString(resolver, Settings.Secure.SYNC_PARENT_SOUNDS).equals("1")) {
+            // Sync is enabled, so we need to disable it
+            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+            IAudioService audioService = IAudioService.Stub.asInterface(b);
+            try {
+                audioService.disableRingtoneSync();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to disable ringtone sync.");
+                return;
+            }
+        }
+
         String setting = getSettingForType(type);
         if (setting == null) return;
         if(!isInternalRingtoneUri(ringtoneUri)) {
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index e197141..5f12742 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -224,7 +224,7 @@
          *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}.
          * @return the same Builder instance.
          * @throws IllegalArgumentException
-         * @see {@link #excludeRule(AudioAttributes, int)}
+         * @see #excludeRule(AudioAttributes, int)
          */
         @SystemApi
         public Builder addRule(AudioAttributes attrToMatch, int rule)
@@ -253,7 +253,7 @@
          *     {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}.
          * @return the same Builder instance.
          * @throws IllegalArgumentException
-         * @see {@link #addRule(AudioAttributes, int)}
+         * @see #addRule(AudioAttributes, int)
          */
         @SystemApi
         public Builder excludeRule(AudioAttributes attrToMatch, int rule)
@@ -275,7 +275,7 @@
          *     {@link AudioAttributes} or an {@link java.lang.Integer}).
          * @return the same Builder instance.
          * @throws IllegalArgumentException
-         * @see {@link #excludeMixRule(int, Object)}
+         * @see #excludeMixRule(int, Object)
          */
         @SystemApi
         public Builder addMixRule(int rule, Object property) throws IllegalArgumentException {
diff --git a/media/java/android/media/session/IOnMediaKeyListener.aidl b/media/java/android/media/session/IOnMediaKeyListener.aidl
new file mode 100644
index 0000000..7752357
--- /dev/null
+++ b/media/java/android/media/session/IOnMediaKeyListener.aidl
@@ -0,0 +1,28 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.os.ResultReceiver;
+import android.view.KeyEvent;
+
+/**
+ * Listener to handle media key.
+ * @hide
+ */
+interface IOnMediaKeyListener {
+    void onMediaKey(in KeyEvent event, in ResultReceiver result);
+}
+
diff --git a/media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl b/media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl
new file mode 100644
index 0000000..07b8347
--- /dev/null
+++ b/media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl
@@ -0,0 +1,27 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.view.KeyEvent;
+
+/**
+ * Listener to handle volume key long-press.
+ * @hide
+ */
+oneway interface IOnVolumeKeyLongPressListener {
+    void onVolumeKeyLongPress(in KeyEvent event);
+}
+
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index bb59e5b..8a98773 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -18,6 +18,8 @@
 import android.content.ComponentName;
 import android.media.IRemoteVolumeController;
 import android.media.session.IActiveSessionsListener;
+import android.media.session.IOnVolumeKeyLongPressListener;
+import android.media.session.IOnMediaKeyListener;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
 import android.os.Bundle;
@@ -31,6 +33,7 @@
     ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
     List<IBinder> getSessions(in ComponentName compName, int userId);
     void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
+    void dispatchVolumeKeyEvent(in KeyEvent keyEvent, int stream, boolean musicOnly);
     void dispatchAdjustVolume(int suggestedStream, int delta, int flags);
     void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
             int userId);
@@ -41,4 +44,8 @@
 
     // For PhoneWindowManager to precheck media keys
     boolean isGlobalPriorityActive();
-}
\ No newline at end of file
+
+    void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
+    void setOnMediaKeyListener(in IOnMediaKeyListener listener);
+}
+
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 95cb8ae..7c3af31 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -176,54 +176,12 @@
         }
     }
 
-    public void sendVolumeKeyEvent(KeyEvent keyEvent, boolean musicOnly) {
+    public void sendVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
         if (keyEvent == null) {
             Log.w(TAG, "Tried to send a null key event. Ignoring.");
             return;
         }
-        boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
-        boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
-        int direction = 0;
-        boolean isMute = false;
-        switch (keyEvent.getKeyCode()) {
-            case KeyEvent.KEYCODE_VOLUME_UP:
-                direction = AudioManager.ADJUST_RAISE;
-                break;
-            case KeyEvent.KEYCODE_VOLUME_DOWN:
-                direction = AudioManager.ADJUST_LOWER;
-                break;
-            case KeyEvent.KEYCODE_VOLUME_MUTE:
-                isMute = true;
-                break;
-        }
-        if (down || up) {
-            int flags = AudioManager.FLAG_FROM_KEY;
-            if (musicOnly) {
-                // This flag is used when the screen is off to only affect
-                // active media
-                flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
-            } else {
-                // These flags are consistent with the home screen
-                if (up) {
-                    flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
-                } else {
-                    flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
-                }
-            }
-            if (direction != 0) {
-                // If this is action up we want to send a beep for non-music events
-                if (up) {
-                    direction = 0;
-                }
-                mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
-                        direction, flags);
-            } else if (isMute) {
-                if (down && keyEvent.getRepeatCount() == 0) {
-                    mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
-                            AudioManager.ADJUST_TOGGLE_MUTE, flags);
-                }
-            }
-        }
+        mSessionManager.dispatchVolumeKeyEvent(keyEvent, stream, musicOnly);
     }
 
     public void sendAdjustVolumeBy(int suggestedStream, int delta, int flags) {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 2364a13..80d2a0c 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioManager;
@@ -26,6 +27,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
@@ -50,6 +52,18 @@
 public final class MediaSessionManager {
     private static final String TAG = "SessionManager";
 
+    /**
+     * Used by IOnMediaKeyListener to indicate that the media key event isn't handled.
+     * @hide
+     */
+    public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0;
+
+    /**
+     * Used by IOnMediaKeyListener to indicate that the media key event is handled.
+     * @hide
+     */
+    public static final int RESULT_MEDIA_KEY_HANDLED = 1;
+
     private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
             = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
     private final Object mLock = new Object();
@@ -57,6 +71,9 @@
 
     private Context mContext;
 
+    private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener;
+    private OnMediaKeyListenerImpl mOnMediaKeyListener;
+
     /**
      * @hide
      */
@@ -278,6 +295,21 @@
     }
 
     /**
+     * Send a volume key event. The receiver will be selected automatically.
+     *
+     * @param keyEvent The volume KeyEvent to send.
+     * @param needWakeLock True if a wake lock should be held while sending the key.
+     * @hide
+     */
+    public void dispatchVolumeKeyEvent(@NonNull KeyEvent keyEvent, int stream, boolean musicOnly) {
+        try {
+            mService.dispatchVolumeKeyEvent(keyEvent, stream, musicOnly);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to send volume key event.", e);
+        }
+    }
+
+    /**
      * Dispatch an adjust volume request to the system. It will be sent to the
      * most relevant audio stream or media session. The direction must be one of
      * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE},
@@ -313,6 +345,72 @@
     }
 
     /**
+     * Set the volume key long-press listener. While the listener is set, the listener
+     * gets the volume key long-presses instead of changing volume.
+     *
+     * <p>System can only have a single volume key long-press listener.
+     *
+     * @param listener The volume key long-press listener. {@code null} to reset.
+     * @param handler The handler on which the listener should be invoked, or {@code null}
+     *            if the listener should be invoked on the calling thread's looper.
+     * @hide
+     */
+    @SystemApi
+    public void setOnVolumeKeyLongPressListener(
+            OnVolumeKeyLongPressListener listener, @Nullable Handler handler) {
+        synchronized (mLock) {
+            try {
+                if (listener == null) {
+                    mOnVolumeKeyLongPressListener = null;
+                    mService.setOnVolumeKeyLongPressListener(null);
+                } else {
+                    if (handler == null) {
+                        handler = new Handler();
+                    }
+                    mOnVolumeKeyLongPressListener =
+                            new OnVolumeKeyLongPressListenerImpl(listener, handler);
+                    mService.setOnVolumeKeyLongPressListener(mOnVolumeKeyLongPressListener);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to set volume key long press listener", e);
+            }
+        }
+    }
+
+    /**
+     * Set the media key listener. While the listener is set, the listener
+     * gets the media key before any other media sessions but after the global priority session.
+     * If the listener handles the key (i.e. returns {@code true}),
+     * other sessions will not get the event.
+     *
+     * <p>System can only have a single media key listener.
+     *
+     * @param listener The media key listener. {@code null} to reset.
+     * @param handler The handler on which the listener should be invoked, or {@code null}
+     *            if the listener should be invoked on the calling thread's looper.
+     * @hide
+     */
+    @SystemApi
+    public void setOnMediaKeyListener(OnMediaKeyListener listener, @Nullable Handler handler) {
+        synchronized (mLock) {
+            try {
+                if (listener == null) {
+                    mOnMediaKeyListener = null;
+                    mService.setOnMediaKeyListener(null);
+                } else {
+                    if (handler == null) {
+                        handler = new Handler();
+                    }
+                    mOnMediaKeyListener = new OnMediaKeyListenerImpl(listener, handler);
+                    mService.setOnMediaKeyListener(mOnMediaKeyListener);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to set media key listener", e);
+            }
+        }
+    }
+
+    /**
      * Listens for changes to the list of active sessions. This can be added
      * using {@link #addOnActiveSessionsChangedListener}.
      */
@@ -320,6 +418,33 @@
         public void onActiveSessionsChanged(@Nullable List<MediaController> controllers);
     }
 
+    /**
+     * Listens the volume key long-presses.
+     * @hide
+     */
+    @SystemApi
+    public interface OnVolumeKeyLongPressListener {
+        /**
+         * Called when the volume key is long-pressed.
+         * <p>This will be called for both down and up events.
+         */
+        void onVolumeKeyLongPress(KeyEvent event);
+    }
+
+    /**
+     * Listens the media key.
+     * @hide
+     */
+    @SystemApi
+    public interface OnMediaKeyListener {
+        /**
+         * Called when the media key is pressed.
+         * <p>If it takes more than 1s to return, the key event will be sent to
+         * other media sessions.
+         */
+        boolean onMediaKey(KeyEvent event);
+    }
+
     private static final class SessionsChangedWrapper {
         private Context mContext;
         private OnActiveSessionsChangedListener mListener;
@@ -360,4 +485,60 @@
             mHandler = null;
         }
     }
+
+    private static final class OnVolumeKeyLongPressListenerImpl
+            extends IOnVolumeKeyLongPressListener.Stub {
+        private OnVolumeKeyLongPressListener mListener;
+        private Handler mHandler;
+
+        public OnVolumeKeyLongPressListenerImpl(
+                OnVolumeKeyLongPressListener listener, Handler handler) {
+            mListener = listener;
+            mHandler = handler;
+        }
+
+        @Override
+        public void onVolumeKeyLongPress(KeyEvent event) {
+            if (mListener == null || mHandler == null) {
+                Log.w(TAG, "Failed to call volume key long-press listener." +
+                        " Either mListener or mHandler is null");
+                return;
+            }
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mListener.onVolumeKeyLongPress(event);
+                }
+            });
+        }
+    }
+
+    private static final class OnMediaKeyListenerImpl extends IOnMediaKeyListener.Stub {
+        private OnMediaKeyListener mListener;
+        private Handler mHandler;
+
+        public OnMediaKeyListenerImpl(OnMediaKeyListener listener, Handler handler) {
+            mListener = listener;
+            mHandler = handler;
+        }
+
+        @Override
+        public void onMediaKey(KeyEvent event, ResultReceiver result) {
+            if (mListener == null || mHandler == null) {
+                Log.w(TAG, "Failed to call media key listener." +
+                        " Either mListener or mHandler is null");
+                return;
+            }
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    boolean handled = mListener.onMediaKey(event);
+                    Log.d(TAG, "The media key listener is returned " + handled);
+                    result.send(
+                            handled ? RESULT_MEDIA_KEY_HANDLED : RESULT_MEDIA_KEY_NOT_HANDLED,
+                            null);
+                }
+            });
+        }
+    }
 }
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 20706fd..44f31526 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -543,6 +543,17 @@
          */
         public static final String TYPE_S_DMB = "TYPE_S_DMB";
 
+        /**
+         * The channel type for preview videos.
+         *
+         * <P>Unlike other broadcast TV channel types, the programs in the preview channel usually
+         * are promotional videos. The UI may treat the preview channels differently from the other
+         * broadcast channels.
+         *
+         * @see #COLUMN_TYPE
+         */
+        public static final String TYPE_PREVIEW = "TYPE_PREVIEW";
+
         /** A generic service type. */
         public static final String SERVICE_TYPE_OTHER = "SERVICE_TYPE_OTHER";
 
@@ -643,7 +654,7 @@
          *
          * <p>This is used to indicate the broadcast standard (e.g. ATSC, DVB or ISDB) the current
          * channel conforms to. Use {@link #TYPE_OTHER} for streaming-based channels, which is the
-         * default channel type. The value should match to one of the followings:
+         * default channel type. The value should match one of the followings:
          * {@link #TYPE_1SEG},
          * {@link #TYPE_ATSC_C},
          * {@link #TYPE_ATSC_M_H},
@@ -1001,6 +1012,20 @@
          */
         public static final String COLUMN_VERSION_NUMBER = "version_number";
 
+        /**
+         * The flag indicating whether this TV channel is transient or not.
+         *
+         * <p>A value of 1 indicates that the channel will be automatically removed by the system on
+         * reboot, and a value of 0 indicates that the channel is persistent across reboot. If not
+         * specified, this value is set to 0 (not transient) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         * @see Programs#COLUMN_TRANSIENT
+         * @hide
+         */
+        @SystemApi
+        public static final String COLUMN_TRANSIENT = "transient";
+
         private Channels() {}
 
         /**
@@ -1063,6 +1088,78 @@
         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
 
         /**
+         * The interaction type for "listens".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        public static final String INTERACTION_TYPE_LISTENS = "INTERACTION_TYPE_LISTENS";
+
+        /**
+         * The interaction type for "followers".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        public static final String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS";
+
+        /**
+         * The interaction type for "fans".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        public static final String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS";
+
+        /**
+         * The interaction type for "likes".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        public static final String INTERACTION_TYPE_LIKES = "INTERACTION_TYPE_LIKES";
+
+        /**
+         * The interaction type for "thumbs".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        public static final String INTERACTION_TYPE_THUMBS = "INTERACTION_TYPE_THUMBS";
+
+        /**
+         * The interaction type for "views".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        public static final String INTERACTION_TYPE_VIEWS = "INTERACTION_TYPE_VIEWS";
+
+        /**
+         * The interaction type for "viewers".
+         *
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        public static final String INTERACTION_TYPE_VIEWERS = "INTERACTION_TYPE_VIEWERS";
+
+        /**
+         * The review rating style for five star rating.
+         *
+         * @see #COLUMN_REVIEW_RATING_STYLE
+         */
+        public static final String REVIEW_RATING_STYLE_STARS = "REVIEW_RATING_STYLE_STARS";
+
+        /**
+         * The review rating style for thumbs-up and thumbs-down rating.
+         *
+         * @see #COLUMN_REVIEW_RATING_STYLE
+         */
+        public static final String REVIEW_RATING_STYLE_THUMPS_UP_DOWN =
+                "REVIEW_RATING_STYLE_THUMPS_UP_DOWN";
+
+        /**
+         * The review rating style for 0 to 100 point system.
+         *
+         * @see #COLUMN_REVIEW_RATING_STYLE
+         */
+        public static final String REVIEW_RATING_STYLE_PERCENTAGE =
+                "REVIEW_RATING_STYLE_PERCENTAGE";
+
+        /**
          * The ID of the TV channel that provides this TV program.
          *
          * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
@@ -1165,6 +1262,8 @@
          * previous program in the same channel. In practice, start time will usually be the end
          * time of the previous program.
          *
+         * <p>Can be empty if this program belongs to a {@link Channels#TYPE_PREVIEW} channel.
+         *
          * <p>Type: INTEGER (long)
          */
         public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
@@ -1176,6 +1275,8 @@
          * next program in the same channel. In practice, end time will usually be the start time of
          * the next program.
          *
+         * <p>Can be empty if this program belongs to a {@link Channels#TYPE_PREVIEW} channel.
+         *
          * <p>Type: INTEGER (long)
          */
         public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
@@ -1410,6 +1511,163 @@
          */
         public static final String COLUMN_VERSION_NUMBER = "version_number";
 
+        /**
+         * The internal ID used by individual TV input services.
+         *
+         * <p>This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
+        /**
+         * The URI for the preview video.
+         *
+         * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}. The data in the column must be
+         * a URL, or a URI in one of the following formats:
+         *
+         * <ul>
+         * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+         * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+         * </li>
+         * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+         * </ul>
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
+
+        /**
+         * The last playback position (in milliseconds) of the preview video.
+         *
+         * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_PREVIEW_LAST_PLAYBACK_POSITION =
+                "preview_last_playback_position";
+
+        /**
+         * The duration (in milliseconds) of the preview video.
+         *
+         * <p>This is only relevant to {@link Channels#TYPE_PREVIEW}.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_PREVIEW_DURATION = "preview_duration";
+
+        /**
+         * The intent URI which is launched when the preview video is selected.
+         *
+         * <p>The URI is created using {@link Intent#toUri} with {@link Intent#URI_INTENT_SCHEME}
+         * and converted back to the original intent with {@link Intent#parseUri}. The intent is
+         * launched when the user selects the preview video item.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_PREVIEW_INTENT_URI =
+                "preview_intent_uri";
+
+        /**
+         * The weight of the preview program within the channel.
+         *
+         * <p>The UI may choose to show this item in a different position in the channel row.
+         * A larger weight value means the program is more important than other programs having
+         * smaller weight values. The value is relevant for the preview programs in the same
+         * channel. This is only relevant to {@link Channels#TYPE_PREVIEW}.
+         *
+         * <p>Can be empty.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String COLUMN_PREVIEW_WEIGHT = "preview_weight";
+
+        /**
+         * The flag indicating whether this program is transient or not.
+         *
+         * <p>A value of 1 indicates that the channel will be automatically removed by the system on
+         * reboot, and a value of 0 indicates that the channel is persistent across reboot. If not
+         * specified, this value is set to 0 (not transient) by default.
+         *
+         * <p>Type: INTEGER (boolean)
+         * @see Channels#COLUMN_TRANSIENT
+         * @hide
+         */
+        @SystemApi
+        public static final String COLUMN_TRANSIENT = "transient";
+
+        /**
+         * The type of interaction for this TV program.
+         *
+         * <p> The value should match one of the followings:
+         * {@link #INTERACTION_TYPE_LISTENS},
+         * {@link #INTERACTION_TYPE_FOLLOWERS},
+         * {@link #INTERACTION_TYPE_FANS},
+         * {@link #INTERACTION_TYPE_LIKES},
+         * {@link #INTERACTION_TYPE_THUMBS},
+         * {@link #INTERACTION_TYPE_VIEWS}, and
+         * {@link #INTERACTION_TYPE_VIEWERS}.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_INTERACTION_COUNT
+         */
+        public static final String COLUMN_INTERACTION_TYPE = "interaction_type";
+
+        /**
+         * The interaction count for this program.
+         *
+         * <p>This indicates the number of times interaction has happened.
+         *
+         * <p>Type: INTEGER
+         * @see #COLUMN_INTERACTION_TYPE
+         */
+        public static final String COLUMN_INTERACTION_COUNT = "interaction_count";
+
+        /**
+         * The author or artist of this content.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_AUTHOR = "author";
+
+        /**
+         * The review rating score style used for {@link #COLUMN_REVIEW_RATING}.
+         *
+         * <p> The value should match one of the followings: {@link #REVIEW_RATING_STYLE_STARS},
+         * {@link #REVIEW_RATING_STYLE_THUMPS_UP_DOWN}, and {@link #REVIEW_RATING_STYLE_PERCENTAGE}.
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_REVIEW_RATING
+         */
+        public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+
+        /**
+         * The review rating score for this program.
+         *
+         * <p>The format of the value is dependent on {@link #COLUMN_REVIEW_RATING_STYLE}. If the
+         * style is {@link #REVIEW_RATING_STYLE_STARS}, the value should be a real number between
+         * 0.0 and 5.0. (e.g. "4.5") If the style is {@link #REVIEW_RATING_STYLE_THUMPS_UP_DOWN},
+         * the value should be two integers, one for thumbs-up count and the other for thumbs-down
+         * count, with a comma between them. (e.g. "200,40") If the style is
+         * {@link #REVIEW_RATING_STYLE_PERCENTAGE}, the value shoule be a real number between 0 and
+         * 100. (e.g. "99.9")
+         *
+         * <p>Type: TEXT
+         * @see #COLUMN_REVIEW_RATING_STYLE
+         */
+        public static final String COLUMN_REVIEW_RATING = "review_rating";
+
         private Programs() {}
 
         /** Canonical genres for TV programs. */
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index dfddaa5..b630270 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -317,6 +317,31 @@
      */
     public static final String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
 
+    /**
+     * Activity action to display the recording schedules. When invoked, the system will display an
+     * appropriate UI to browse the schedules.
+     */
+    public static final String ACTION_VIEW_RECORDING_SCHEDULES =
+            "android.media.tv.action.VIEW_RECORDING_SCHEDULES";
+
+    /**
+     * Action sent by an application telling the system to set the given channel as browsable.
+     *
+     * <p>The intent must contain the following bundle parameters:
+     * <ul>
+     *     <li>{@link #EXTRA_CHANNEL_ID} then channel ID as an integer.
+     *     <li>{@link #EXTRA_PACKAGE_NAME} the package name of the requesting application.
+     * </ul>
+     */
+    public static final String ACTION_MAKE_CHANNEL_BROWSABLE
+            = "android.media.tv.action.MAKE_CHANNEL_BROWSABLE";
+
+    /** The key for a bundle parameter containing a channel ID as an integer */
+    public static final String EXTRA_CHANNEL_ID = "android.media.tv.extra.CHANNEL_ID";
+
+    /** The key for a bundle parameter containing a package name as a string. */
+    public static final String EXTRA_PACKAGE_NAME = "android.media.tv.extra.PACKAGE_NAME";
+
     private final ITvInputManager mService;
 
     private final Object mLock = new Object();
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 16847c1..eaec493 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -387,7 +387,6 @@
      * @see BrowserRoot#EXTRA_RECENT
      * @see BrowserRoot#EXTRA_OFFLINE
      * @see BrowserRoot#EXTRA_SUGGESTED
-     * @see BrowserRoot#EXTRA_SUGGESTION_KEYWORDS
      */
     public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
             int clientUid, @Nullable Bundle rootHints);
@@ -550,7 +549,6 @@
      * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
      * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
      * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
-     * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTION_KEYWORDS
      */
     public final Bundle getBrowserRootHints() {
         if (mCurConnection == null) {
@@ -818,7 +816,6 @@
          *
          * @see #EXTRA_OFFLINE
          * @see #EXTRA_SUGGESTED
-         * @see #EXTRA_SUGGESTION_KEYWORDS
          */
         public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
 
@@ -836,7 +833,6 @@
          *
          * @see #EXTRA_RECENT
          * @see #EXTRA_SUGGESTED
-         * @see #EXTRA_SUGGESTION_KEYWORDS
          */
         public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
 
@@ -855,31 +851,9 @@
          *
          * @see #EXTRA_RECENT
          * @see #EXTRA_OFFLINE
-         * @see #EXTRA_SUGGESTION_KEYWORDS
          */
         public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
 
-        /**
-         * The lookup key for a string that indicates specific keywords which will be considered
-         * when the browser service suggests media items.
-         *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint together with {@link #EXTRA_SUGGESTED} for retrieving suggested
-         * media items related with the keywords. The list of media items passed in
-         * {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
-         * is considered ordered by relevance, first being the top suggestion.
-         * If the media browser service can provide such media items, the implementation must return
-         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
-         *
-         * <p>The root hint may contain multiple keys.
-         *
-         * @see #EXTRA_RECENT
-         * @see #EXTRA_OFFLINE
-         * @see #EXTRA_SUGGESTED
-         */
-        public static final String EXTRA_SUGGESTION_KEYWORDS
-                = "android.service.media.extra.SUGGESTION_KEYWORDS";
-
         final private String mRootId;
         final private Bundle mExtras;
 
diff --git a/media/jni/android_media_BufferingParams.h b/media/jni/android_media_BufferingParams.h
new file mode 100644
index 0000000..24c51f5
--- /dev/null
+++ b/media/jni/android_media_BufferingParams.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_BUFFERING_PARAMS_H_
+#define _ANDROID_MEDIA_BUFFERING_PARAMS_H_
+
+#include <media/BufferingSettings.h>
+
+namespace android {
+
+// This entire class is inline
+struct BufferingParams {
+    BufferingSettings settings;
+
+    struct fields_t {
+        jclass      clazz;
+        jmethodID   constructID;
+
+        jfieldID    initial_buffering_mode;
+        jfieldID    rebuffering_mode;
+        jfieldID    initial_watermark_ms;
+        jfieldID    initial_watermark_kb;
+        jfieldID    rebuffering_watermark_low_ms;
+        jfieldID    rebuffering_watermark_high_ms;
+        jfieldID    rebuffering_watermark_low_kb;
+        jfieldID    rebuffering_watermark_high_kb;
+
+        void init(JNIEnv *env) {
+            jclass lclazz = env->FindClass("android/media/BufferingParams");
+            if (lclazz == NULL) {
+                return;
+            }
+
+            clazz = (jclass)env->NewGlobalRef(lclazz);
+            if (clazz == NULL) {
+                return;
+            }
+
+            constructID = env->GetMethodID(clazz, "<init>", "()V");
+
+            initial_buffering_mode = env->GetFieldID(clazz, "mInitialBufferingMode", "I");
+            rebuffering_mode = env->GetFieldID(clazz, "mRebufferingMode", "I");
+            initial_watermark_ms = env->GetFieldID(clazz, "mInitialWatermarkMs", "I");
+            initial_watermark_kb = env->GetFieldID(clazz, "mInitialWatermarkKB", "I");
+            rebuffering_watermark_low_ms = env->GetFieldID(clazz, "mRebufferingWatermarkLowMs", "I");
+            rebuffering_watermark_high_ms = env->GetFieldID(clazz, "mRebufferingWatermarkHighMs", "I");
+            rebuffering_watermark_low_kb = env->GetFieldID(clazz, "mRebufferingWatermarkLowKB", "I");
+            rebuffering_watermark_high_kb = env->GetFieldID(clazz, "mRebufferingWatermarkHighKB", "I");
+
+            env->DeleteLocalRef(lclazz);
+        }
+
+        void exit(JNIEnv *env) {
+            env->DeleteGlobalRef(clazz);
+            clazz = NULL;
+        }
+    };
+
+    void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject params) {
+        settings.mInitialBufferingMode =
+            (BufferingMode)env->GetIntField(params, fields.initial_buffering_mode);
+        settings.mRebufferingMode =
+            (BufferingMode)env->GetIntField(params, fields.rebuffering_mode);
+        settings.mInitialWatermarkMs =
+            env->GetIntField(params, fields.initial_watermark_ms);
+        settings.mInitialWatermarkKB =
+            env->GetIntField(params, fields.initial_watermark_kb);
+        settings.mRebufferingWatermarkLowMs =
+            env->GetIntField(params, fields.rebuffering_watermark_low_ms);
+        settings.mRebufferingWatermarkHighMs =
+            env->GetIntField(params, fields.rebuffering_watermark_high_ms);
+        settings.mRebufferingWatermarkLowKB =
+            env->GetIntField(params, fields.rebuffering_watermark_low_kb);
+        settings.mRebufferingWatermarkHighKB =
+            env->GetIntField(params, fields.rebuffering_watermark_high_kb);
+    }
+
+    jobject asJobject(JNIEnv *env, const fields_t& fields) {
+        jobject params = env->NewObject(fields.clazz, fields.constructID);
+        if (params == NULL) {
+            return NULL;
+        }
+        env->SetIntField(params, fields.initial_buffering_mode, (jint)settings.mInitialBufferingMode);
+        env->SetIntField(params, fields.rebuffering_mode, (jint)settings.mRebufferingMode);
+        env->SetIntField(params, fields.initial_watermark_ms, (jint)settings.mInitialWatermarkMs);
+        env->SetIntField(params, fields.initial_watermark_kb, (jint)settings.mInitialWatermarkKB);
+        env->SetIntField(params, fields.rebuffering_watermark_low_ms, (jint)settings.mRebufferingWatermarkLowMs);
+        env->SetIntField(params, fields.rebuffering_watermark_high_ms, (jint)settings.mRebufferingWatermarkHighMs);
+        env->SetIntField(params, fields.rebuffering_watermark_low_kb, (jint)settings.mRebufferingWatermarkLowKB);
+        env->SetIntField(params, fields.rebuffering_watermark_high_kb, (jint)settings.mRebufferingWatermarkHighKB);
+
+        return params;
+    }
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_BUFFERING_PARAMS_H_
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index 216624e..c3461f0 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -23,6 +23,9 @@
 #include "jni.h"
 #include "JNIHelp.h"
 
+#include <unistd.h>
+#include <fcntl.h>
+
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -138,6 +141,28 @@
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     ALOGV("native_setup: fd %d", fd);
 
+    // It appears that if an invalid file descriptor is passed through
+    // binder calls, the server-side of the inter-process function call
+    // is skipped. As a result, the check at the server-side to catch
+    // the invalid file descritpor never gets invoked. This is to workaround
+    // this issue by checking the file descriptor first before passing
+    // it through binder call.
+    int flags = fcntl(fd, F_GETFL);
+    if (flags == -1) {
+        ALOGE("Fail to get File Status Flags err: %s", strerror(errno));
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                "Invalid file descriptor");
+        return 0;
+    }
+
+    // fd must be in read-write mode or write-only mode.
+    if ((flags & (O_RDWR | O_WRONLY)) == 0) {
+        ALOGE("File descriptor is not in read-write mode or write-only mode");
+        jniThrowException(env, "java/io/IOException",
+                "File descriptor is not in read-write mode or write-only mode");
+        return 0;
+    }
+
     MediaMuxer::OutputFormat fileFormat =
         static_cast<MediaMuxer::OutputFormat>(format);
     sp<MediaMuxer> muxer = new MediaMuxer(fd, fileFormat);
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index c52ed94..8225052 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -37,6 +37,7 @@
 #include "utils/Errors.h"  // for status_t
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
+#include "android_media_BufferingParams.h"
 #include "android_media_MediaDataSource.h"
 #include "android_media_PlaybackParams.h"
 #include "android_media_SyncParams.h"
@@ -69,6 +70,7 @@
 };
 static fields_t fields;
 
+static BufferingParams::fields_t gBufferingParamsFields;
 static PlaybackParams::fields_t gPlaybackParamsFields;
 static SyncParams::fields_t gSyncParamsFields;
 
@@ -343,6 +345,66 @@
     setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
 }
 
+static jobject
+android_media_MediaPlayer_getDefaultBufferingParams(JNIEnv *env, jobject thiz)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    BufferingParams bp;
+    BufferingSettings &settings = bp.settings;
+    process_media_player_call(
+            env, thiz, mp->getDefaultBufferingSettings(&settings),
+            "java/lang/IllegalStateException", "unexpected error");
+    ALOGV("getDefaultBufferingSettings:{%s}", settings.toString().string());
+
+    return bp.asJobject(env, gBufferingParamsFields);
+}
+
+static jobject
+android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    BufferingParams bp;
+    BufferingSettings &settings = bp.settings;
+    process_media_player_call(
+            env, thiz, mp->getBufferingSettings(&settings),
+            "java/lang/IllegalStateException", "unexpected error");
+    ALOGV("getBufferingSettings:{%s}", settings.toString().string());
+
+    return bp.asJobject(env, gBufferingParamsFields);
+}
+
+static void
+android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params)
+{
+    if (params == NULL) {
+        return;
+    }
+
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    BufferingParams bp;
+    bp.fillFromJobject(env, gBufferingParamsFields, params);
+    ALOGV("setBufferingParams:{%s}", bp.settings.toString().string());
+
+    process_media_player_call(
+            env, thiz, mp->setBufferingSettings(bp.settings),
+            "java/lang/IllegalStateException", "unexpected error");
+}
+
 static void
 android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
 {
@@ -860,6 +922,7 @@
 
     env->DeleteLocalRef(clazz);
 
+    gBufferingParamsFields.init(env);
     gPlaybackParamsFields.init(env);
     gSyncParamsFields.init(env);
 }
@@ -1046,6 +1109,9 @@
     {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
     {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
+    {"getDefaultBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getDefaultBufferingParams},
+    {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
+    {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
     {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
     {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 6c79ab7..7c509d2 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -290,7 +290,7 @@
 }
 
 static void
-android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
+android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor)
 {
     ALOGV("setOutputFile");
     if (fileDescriptor == NULL) {
@@ -303,7 +303,25 @@
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
         return;
     }
-    status_t opStatus = mr->setOutputFile(fd, offset, length);
+    status_t opStatus = mr->setOutputFile(fd);
+    process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
+}
+
+static void
+android_media_MediaRecorder_setNextOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor)
+{
+    ALOGV("setNextOutputFile");
+    if (fileDescriptor == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+    if (mr == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+    status_t opStatus = mr->setNextOutputFile(fd);
     process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
 }
 
@@ -617,7 +635,8 @@
     {"setVideoEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setVideoEncoder},
     {"setAudioEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setAudioEncoder},
     {"setParameter",         "(Ljava/lang/String;)V",           (void *)android_media_MediaRecorder_setParameter},
-    {"_setOutputFile",       "(Ljava/io/FileDescriptor;JJ)V",   (void *)android_media_MediaRecorder_setOutputFileFD},
+    {"_setOutputFile",       "(Ljava/io/FileDescriptor;)V",     (void *)android_media_MediaRecorder_setOutputFileFD},
+    {"_setNextOutputFile",   "(Ljava/io/FileDescriptor;)V",     (void *)android_media_MediaRecorder_setNextOutputFileFD},
     {"setVideoSize",         "(II)V",                           (void *)android_media_MediaRecorder_setVideoSize},
     {"setVideoFrameRate",    "(I)V",                            (void *)android_media_MediaRecorder_setVideoFrameRate},
     {"setMaxDuration",       "(I)V",                            (void *)android_media_MediaRecorder_setMaxDuration},
diff --git a/media/mca/effect/java/android/media/effect/package.html b/media/mca/effect/java/android/media/effect/package.html
index 8a210fd..1d297ef 100644
--- a/media/mca/effect/java/android/media/effect/package.html
+++ b/media/mca/effect/java/android/media/effect/package.html
@@ -24,7 +24,7 @@
 android.media.effect.EffectContext#getFactory EffectContext.getFactory()}, which returns an instance
 of {@link android.media.effect.EffectFactory}.</li>
 <li>Call {@link android.media.effect.EffectFactory#createEffect createEffect()}, passing it an
-effect name from @link android.media.effect.EffectFactory}, such as {@link
+effect name from {@link android.media.effect.EffectFactory}, such as {@link
 android.media.effect.EffectFactory#EFFECT_FISHEYE} or {@link
 android.media.effect.EffectFactory#EFFECT_VIGNETTE}.</li>
 </ol>
diff --git a/media/mca/tests/Android.mk b/media/mca/tests/Android.mk
index 2abd7f6..eb451f7 100644
--- a/media/mca/tests/Android.mk
+++ b/media/mca/tests/Android.mk
@@ -5,6 +5,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 308c665..0d9f42b 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -10,7 +10,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     mockito-target-minus-junit4 \
     android-support-test \
-    android-ex-camera2
+    android-ex-camera2 \
+    legacy-android-test
 
 LOCAL_PACKAGE_NAME := mediaframeworktest
 
diff --git a/native/android/Android.mk b/native/android/Android.mk
index da4e4ba..355f52e 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -9,6 +9,7 @@
     asset_manager.cpp \
     choreographer.cpp \
     configuration.cpp \
+    hardware_buffer.cpp \
     input.cpp \
     looper.cpp \
     native_activity.cpp \
diff --git a/native/android/hardware_buffer.cpp b/native/android/hardware_buffer.cpp
new file mode 100644
index 0000000..2f75c10
--- /dev/null
+++ b/native/android/hardware_buffer.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHardwareBuffer"
+
+#include <android/hardware_buffer_jni.h>
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <memory>
+
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
+#include <cutils/native_handle.h>
+#include <binder/IServiceManager.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/IGraphicBufferAlloc.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Flattenable.h>
+#include <utils/Log.h>
+
+static constexpr int kDataBufferSize = 64 * sizeof(int);  // 64 ints
+
+using namespace android;
+
+static inline const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(
+        const AHardwareBuffer* buffer) {
+    return reinterpret_cast<const GraphicBuffer*>(buffer);
+}
+
+static inline GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(
+        AHardwareBuffer* buffer) {
+    return reinterpret_cast<GraphicBuffer*>(buffer);
+}
+
+static inline AHardwareBuffer* GraphicBuffer_to_AHardwareBuffer(
+        GraphicBuffer* buffer) {
+    return reinterpret_cast<AHardwareBuffer*>(buffer);
+}
+
+// ----------------------------------------------------------------------------
+// Public functions
+// ----------------------------------------------------------------------------
+
+int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
+        AHardwareBuffer** outBuffer) {
+    if (!outBuffer || !desc) return BAD_VALUE;
+
+    // The holder is used to destroy the buffer if an error occurs.
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == nullptr) {
+        ALOGE("Unable to connect to ServiceManager");
+        return PERMISSION_DENIED;
+    }
+
+    // Get the SurfaceFlingerService.
+    sp<ISurfaceComposer> composer = interface_cast<ISurfaceComposer>(
+            sm->getService(String16("SurfaceFlinger")));
+    if (composer == nullptr) {
+        ALOGE("Unable to connect to surface composer");
+        return PERMISSION_DENIED;
+    }
+    // Get an IGraphicBufferAlloc to create the buffer.
+    sp<IGraphicBufferAlloc> allocator = composer->createGraphicBufferAlloc();
+    if (allocator == nullptr) {
+        ALOGE("Unable to obtain a buffer allocator");
+        return PERMISSION_DENIED;
+    }
+
+    int format = android_hardware_HardwareBuffer_convertToPixelFormat(
+            desc->format);
+    if (format == 0) {
+        ALOGE("Invalid pixel format");
+        return BAD_VALUE;
+    }
+
+    if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
+        ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB "
+                "format");
+        return BAD_VALUE;
+    }
+
+    status_t err;
+    uint32_t usage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(
+            desc->usage0, desc->usage1);
+    sp<GraphicBuffer> gbuffer = allocator->createGraphicBuffer(desc->width,
+            desc->height, format, desc->layers, usage, &err);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    *outBuffer = GraphicBuffer_to_AHardwareBuffer(gbuffer.get());
+    // Ensure the buffer doesn't get destroyed with the sp<> goes away.
+    AHardwareBuffer_acquire(*outBuffer);
+    return NO_ERROR;
+}
+
+void AHardwareBuffer_acquire(AHardwareBuffer* buffer) {
+    AHardwareBuffer_to_GraphicBuffer(buffer)->incStrong(
+            (void*)AHardwareBuffer_acquire);
+}
+
+void AHardwareBuffer_release(AHardwareBuffer* buffer) {
+    AHardwareBuffer_to_GraphicBuffer(buffer)->decStrong(
+            (void*)AHardwareBuffer_acquire);
+}
+
+void AHardwareBuffer_describe(const AHardwareBuffer* buffer,
+        AHardwareBuffer_Desc* outDesc) {
+    if (!buffer || !outDesc) return;
+
+    const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+    outDesc->width = gbuffer->getWidth();
+    outDesc->height = gbuffer->getHeight();
+    outDesc->layers = gbuffer->getLayerCount();
+    outDesc->usage0 =
+            android_hardware_HardwareBuffer_convertFromGrallocUsageBits(
+                    gbuffer->getUsage());
+    outDesc->usage1 = 0;
+    outDesc->format = android_hardware_HardwareBuffer_convertFromPixelFormat(
+            static_cast<uint32_t>(gbuffer->getPixelFormat()));
+}
+
+int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage0,
+        int32_t fence, const ARect* rect, void** outVirtualAddress) {
+    if (!buffer) return BAD_VALUE;
+
+    if (usage0 & ~(AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN |
+            AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN)) {
+        ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
+                " AHARDWAREBUFFER_USAGE0_CPU_* flags are allowed");
+        return BAD_VALUE;
+    }
+
+    uint32_t usage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(
+            usage0, 0);
+    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    if (!rect) {
+        return gBuffer->lockAsync(usage, outVirtualAddress, fence);
+    } else {
+        Rect bounds(rect->left, rect->top, rect->right, rect->bottom);
+        return gBuffer->lockAsync(usage, bounds, outVirtualAddress, fence);
+    }
+}
+
+int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
+    if (!buffer) return BAD_VALUE;
+
+    GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    return gBuffer->unlockAsync(fence);
+}
+
+int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer,
+        int socketFd) {
+    if (!buffer) return BAD_VALUE;
+    const GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+    size_t flattenedSize = gBuffer->getFlattenedSize();
+    size_t fdCount = gBuffer->getFdCount();
+
+    std::unique_ptr<uint8_t[]> data(new uint8_t[flattenedSize]);
+    std::unique_ptr<int[]> fds(new int[fdCount]);
+
+    // Make copies of needed items since flatten modifies them, and we don't
+    // want to send anything if there's an error during flatten.
+    size_t flattenedSizeCopy = flattenedSize;
+    size_t fdCountCopy = fdCount;
+    void* dataStart = data.get();
+    int* fdsStart = fds.get();
+    status_t err = gBuffer->flatten(dataStart, flattenedSizeCopy, fdsStart,
+                fdCountCopy);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    struct iovec iov[1];
+    iov[0].iov_base = data.get();
+    iov[0].iov_len = flattenedSize;
+
+    char buf[CMSG_SPACE(kDataBufferSize)];
+    struct msghdr msg = {
+        .msg_control = buf,
+        .msg_controllen = sizeof(buf),
+        .msg_iov = &iov[0],
+        .msg_iovlen = 1,
+    };
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fdCount);
+    int* fdData = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    memcpy(fdData, fds.get(), sizeof(int) * fdCount);
+    msg.msg_controllen = cmsg->cmsg_len;
+
+    int result = sendmsg(socketFd, &msg, 0);
+    if (result <= 0) {
+        ALOGE("Error writing AHardwareBuffer to socket: error %#x (%s)",
+                result, strerror(errno));
+        return result;
+    }
+    return NO_ERROR;
+}
+
+int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd,
+        AHardwareBuffer** outBuffer) {
+    if (!outBuffer) return BAD_VALUE;
+
+    char dataBuf[CMSG_SPACE(kDataBufferSize)];
+    char fdBuf[CMSG_SPACE(kDataBufferSize)];
+    struct iovec iov[1];
+    iov[0].iov_base = dataBuf;
+    iov[0].iov_len = sizeof(dataBuf);
+
+    struct msghdr msg = {
+        .msg_control = fdBuf,
+        .msg_controllen = sizeof(fdBuf),
+        .msg_iov = &iov[0],
+        .msg_iovlen = 1,
+    };
+
+    int result = recvmsg(socketFd, &msg, 0);
+    if (result <= 0) {
+        ALOGE("Error reading AHardwareBuffer from socket: error %#x (%s)",
+                result, strerror(errno));
+        return result;
+    }
+
+    if (msg.msg_iovlen != 1) {
+        ALOGE("Error reading AHardwareBuffer from socket: bad data length");
+        return INVALID_OPERATION;
+    }
+
+    if (msg.msg_controllen % sizeof(int) != 0) {
+        ALOGE("Error reading AHardwareBuffer from socket: bad fd length");
+        return INVALID_OPERATION;
+    }
+
+    size_t dataLen = msg.msg_iov[0].iov_len;
+    const void* data = static_cast<const void*>(msg.msg_iov[0].iov_base);
+    if (!data) {
+        ALOGE("Error reading AHardwareBuffer from socket: no buffer data");
+        return INVALID_OPERATION;
+    }
+
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    if (!cmsg) {
+        ALOGE("Error reading AHardwareBuffer from socket: no fd header");
+        return INVALID_OPERATION;
+    }
+
+    size_t fdCount = msg.msg_controllen >> 2;
+    const int* fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+    if (!fdData) {
+        ALOGE("Error reading AHardwareBuffer from socket: no fd data");
+        return INVALID_OPERATION;
+    }
+
+    GraphicBuffer* gBuffer = new GraphicBuffer();
+    status_t err = gBuffer->unflatten(data, dataLen, fdData, fdCount);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    *outBuffer = GraphicBuffer_to_AHardwareBuffer(gBuffer);
+    // Ensure the buffer has a positive ref-count.
+    AHardwareBuffer_acquire(*outBuffer);
+
+    return NO_ERROR;
+}
+
+const struct native_handle* AHardwareBuffer_getNativeHandle(
+        const AHardwareBuffer* buffer) {
+    if (!buffer) return nullptr;
+    const GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+    return gbuffer->handle;
+}
+
+// ----------------------------------------------------------------------------
+// JNI functions
+// ----------------------------------------------------------------------------
+
+AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env,
+        jobject hardwareBufferObj) {
+    return android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
+            hardwareBufferObj);
+}
+
+jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env,
+        AHardwareBuffer* hardwareBuffer) {
+    return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env,
+            hardwareBuffer);
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 5758a3c..f9e8fda 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -139,6 +139,17 @@
     ANativeActivity_setWindowFlags;
     ANativeActivity_setWindowFormat;
     ANativeActivity_showSoftInput;
+    AHardwareBuffer_acquire; # introduced=26
+    AHardwareBuffer_allocate; # introduced=26
+    AHardwareBuffer_describe; # introduced=26
+    AHardwareBuffer_fromHardwareBuffer; # introduced=26
+    AHardwareBuffer_getNativeHandle; # introduced=26
+    AHardwareBuffer_lock; # introduced=26
+    AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26
+    AHardwareBuffer_release; # introduced=26
+    AHardwareBuffer_sendHandleToUnixSocket; # introduced=26
+    AHardwareBuffer_toHardwareBuffer; # introduced=26
+    AHardwareBuffer_unlock; # introduced=26
     ANativeWindow_acquire;
     ANativeWindow_fromSurface;
     ANativeWindow_fromSurfaceTexture; # introduced-arm=13 introduced-mips=13 introduced-x86=13
diff --git a/nfc-extras/Android.mk b/nfc-extras/Android.mk
index 330e2d4..cd7a45b 100644
--- a/nfc-extras/Android.mk
+++ b/nfc-extras/Android.mk
@@ -6,6 +6,8 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+
 LOCAL_MODULE:= com.android.nfc_extras
 
 include $(BUILD_JAVA_LIBRARY)
diff --git a/packages/BackupRestoreConfirmation/res/values-it/strings.xml b/packages/BackupRestoreConfirmation/res/values-it/strings.xml
index b84edbc..1cbb770 100644
--- a/packages/BackupRestoreConfirmation/res/values-it/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-it/strings.xml
@@ -30,7 +30,7 @@
     <string name="backup_enc_password_text" msgid="4981585714795233099">"Inserisci una password da utilizzare per la crittografia dei dati di backup completi. Se non ne inserisci una, verrà utilizzata la tua password di backup corrente:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se desideri crittografare tutti i dati di backup, inserisci una password qui di seguito:"</string>
     <string name="backup_enc_password_required" msgid="7889652203371654149">"Il dispositivo è criptato, quindi devi criptare il backup. Inserisci una password di seguito:"</string>
-    <string name="restore_enc_password_text" msgid="6140898525580710823">"Se i dati di ripristino sono crittografati, inserisci la password qui di seguito:"</string>
+    <string name="restore_enc_password_text" msgid="6140898525580710823">"Se i dati di ripristino sono criptati, inserisci la password qui di seguito:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Avvio del backup..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Backup terminato"</string>
     <string name="toast_restore_started" msgid="7881679218971277385">"Avvio del ripristino..."</string>
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 28d9e5c..e2080b0 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -37,5 +37,7 @@
         <activity android:name="com.android.carrierdefaultapp.CaptivePortalLaunchActivity"
             android:theme="@android:style/Theme.Translucent.NoTitleBar"
             android:excludeFromRecents="true"/>
+        <service android:name="com.android.carrierdefaultapp.ProvisionObserver"
+                 android:permission="android.permission.BIND_JOB_SERVICE"/>
     </application>
 </manifest>
diff --git a/packages/CarrierDefaultApp/res/values-am/strings.xml b/packages/CarrierDefaultApp/res/values-am/strings.xml
new file mode 100644
index 0000000..fc62e8d
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-am/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"አገልግሎትዎን ያግብሩ"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ምንም የውሂብ አገልግሎት የለም"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"አገልግሎትዎን ለማግበር መታ ያድርጉ"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"ምንም አገልግሎት የለም፣ እባክዎ የአገልግሎት አቅራቢዎን ያነጋግሩ"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"ከተያዥ መግቢያ ጋር በመገናኘት ላይ..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"አውታረ መረብ የእረፍት ጊዜ ወስዷል፣ እንደገና መሞከር ይፈልጋሉ?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"አውታረ መረብ አይገኝም"</string>
+    <string name="quit" msgid="4392968039488794590">"አቁም"</string>
+    <string name="wait" msgid="7902715035629500128">"ይጠብቁ"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ar/strings.xml b/packages/CarrierDefaultApp/res/values-ar/strings.xml
new file mode 100644
index 0000000..3903c35
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ar/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"تنشيط الخدمة"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ليست هناك خدمة بيانات"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"انقر لتنشيط الخدمة"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"ليست هناك خدمة، يرجى الاتصال بمقدم الخدمة"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"جارٍ الاتصال بالمدخل المقيد الوصول..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"انتهت مهلة الشبكة، هل ترغب في إعادة المحاولة؟"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"الشبكة غير متاحة"</string>
+    <string name="quit" msgid="4392968039488794590">"إنهاء"</string>
+    <string name="wait" msgid="7902715035629500128">"انتظار"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml b/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..57f8bf5
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktivirajte uslugu"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Nema usluge prenosa podataka"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Dodirnite da biste aktivirali uslugu"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Nema usluge. Kontaktirajte dobavljača usluge"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Povezuje se sa ulaznim portalom…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Vremensko ograničenje mreže je isteklo. Želite li da probate ponovo?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Mreža nije dostupna"</string>
+    <string name="quit" msgid="4392968039488794590">"Zatvori"</string>
+    <string name="wait" msgid="7902715035629500128">"Čekaj"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-be/strings.xml b/packages/CarrierDefaultApp/res/values-be/strings.xml
new file mode 100644
index 0000000..e6d1cd9
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-be/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Актывуйце сэрвіс"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Няма сэрвісу перадачы даных"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Дакраніцеся, каб актываваць сэрвіс"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Няма абслугоўвання, звярніцеся да свайго пастаўшчыка паслуг"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Ідзе падключэнне да партала ўзаемадзеяння..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Час чакання сеткі скончыўся, хочаце паўтарыць спробу?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Сетка недаступная"</string>
+    <string name="quit" msgid="4392968039488794590">"Выйсці"</string>
+    <string name="wait" msgid="7902715035629500128">"Пачакайце"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-bg/strings.xml b/packages/CarrierDefaultApp/res/values-bg/strings.xml
new file mode 100644
index 0000000..b6bdd5d
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-bg/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Активирайте услугата си"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Няма услуга за данни"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Докоснете, за да активирате услугата си"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Няма покритие. Моля, свържете се с доставчика си"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Установява се връзка с портал за удостоверяване..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Времето за изчакване на мрежата изтече. Искате ли да опитате отново?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Мрежата не е налице"</string>
+    <string name="quit" msgid="4392968039488794590">"Изход"</string>
+    <string name="wait" msgid="7902715035629500128">"Изчакване"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-bn/strings.xml b/packages/CarrierDefaultApp/res/values-bn/strings.xml
new file mode 100644
index 0000000..138a2b3
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-bn/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"আপনার পরিষেবা সক্রিয় করুন"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ডেটা পরিষেবা উপলব্ধ নয়"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"আপনার পরিষেবা সক্রিয় করতে আলতো চাপুন"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"পরিষেবা উপলব্ধ নয়, অনুগ্রহ করে আপনার পরিষেবা প্রদানকারীর সঙ্গে যোগাযোগ করুন"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"অন্তরীণ পোর্টালের সঙ্গে সংযুক্ত করা হচ্ছে..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"নেটওয়ার্কের সময় সমাপ্ত হয়েছে, আপনি কি পুনরায় চেষ্টা করতে চান?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"নেটওয়ার্ক অনুপলব্ধ"</string>
+    <string name="quit" msgid="4392968039488794590">"প্রস্থান করুন"</string>
+    <string name="wait" msgid="7902715035629500128">"অপেক্ষা করুন"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-bs/strings.xml b/packages/CarrierDefaultApp/res/values-bs/strings.xml
new file mode 100644
index 0000000..b43e766
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-bs/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktivirajte uslugu"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Nema mobilnog interneta"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Dodirnite da aktivirate uslugu"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Nema usluge. Obratite se pružaocu usluge."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Povezivanje na zaštitni portal..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Isteklo je vrijeme za odziv mreže. Želite li ponoviti?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Mreža nije dostupna"</string>
+    <string name="quit" msgid="4392968039488794590">"Odustani"</string>
+    <string name="wait" msgid="7902715035629500128">"Sačekaj"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ca/strings.xml b/packages/CarrierDefaultApp/res/values-ca/strings.xml
new file mode 100644
index 0000000..0730a01
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ca/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Activa el servei"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"No hi ha servei de dades"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Toca per activar el servei"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"No hi ha servei. Contacta amb el proveïdor del servei."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"S\'està connectant al portal captiu…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"S\'ha esgotat el temps d\'espera de la xarxa. Vols tornar-ho a provar?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Xarxa no disponible"</string>
+    <string name="quit" msgid="4392968039488794590">"Surt"</string>
+    <string name="wait" msgid="7902715035629500128">"Espera"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-de/strings.xml b/packages/CarrierDefaultApp/res/values-de/strings.xml
new file mode 100644
index 0000000..2713560
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-de/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Dienst aktivieren"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Kein Datendienst"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Zum Aktivieren des Dienstes tippen"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Kein Dienst, kontaktiere bitte deinen Internetanbieter"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Verbindung mit Captive Portal wird hergestellt…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Zeitüberschreitung für Netzwerk – möchtest du es noch einmal versuchen?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Netzwerk nicht verfügbar"</string>
+    <string name="quit" msgid="4392968039488794590">"Beenden"</string>
+    <string name="wait" msgid="7902715035629500128">"Warten"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-el/strings.xml b/packages/CarrierDefaultApp/res/values-el/strings.xml
new file mode 100644
index 0000000..6e9b2a4
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-el/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Ενεργοποιήστε την υπηρεσία σας"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Δεν υπάρχει υπηρεσία δεδομένων"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Πατήστε για να ενεργοποιήσετε την υπηρεσία σας"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Η υπηρεσία δεν είναι διαθέσιμη. Επικοινωνήστε με τον παροχέα υπηρεσιών που χρησιμοποιείτε."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Σύνδεση στην πύλη υποδοχής…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Λήξη χρονικού ορίου δικτύου, θέλετε να δοκιμάσετε ξανά;"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Το δίκτυο δεν είναι διαθέσιμο"</string>
+    <string name="quit" msgid="4392968039488794590">"Έξοδος"</string>
+    <string name="wait" msgid="7902715035629500128">"Αναμονή"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml b/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..735b255
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-es-rUS/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Activa tu servicio"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Sin servicio de datos"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Presiona para activar tu servicio"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"No hay servicio, por lo que debes comunicarte con tu proveedor de servicios"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Conectando al portal cautivo…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Se agotó el tiempo de espera de la red, ¿quieres volver a intentarlo?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Red no disponible"</string>
+    <string name="quit" msgid="4392968039488794590">"Salir"</string>
+    <string name="wait" msgid="7902715035629500128">"Esperar"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-es/strings.xml b/packages/CarrierDefaultApp/res/values-es/strings.xml
new file mode 100644
index 0000000..49be628
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-es/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Activar el servicio"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Sin servicio de datos"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Toca aquí para activar tu servicio"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Sin servicio. Ponte en contacto con el proveedor de servicios"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Conectando al portal cautivo…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Se ha agotado el tiempo de espera de la red. ¿Quieres volver a intentarlo?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Red no disponible"</string>
+    <string name="quit" msgid="4392968039488794590">"Salir"</string>
+    <string name="wait" msgid="7902715035629500128">"Esperar"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-et/strings.xml b/packages/CarrierDefaultApp/res/values-et/strings.xml
new file mode 100644
index 0000000..0cdf5bb
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-et/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktiveerige teenus"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Andmesideteenus puudub"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Puudutage teenuse aktiveerimiseks"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Teenus puudub. Võtke ühendust oma teenusepakkujaga"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Hõiveportaaliga ühendamine …"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Võrgu ajalõpp. Kas soovite uuesti proovida?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Võrk ei ole saadaval"</string>
+    <string name="quit" msgid="4392968039488794590">"Välju"</string>
+    <string name="wait" msgid="7902715035629500128">"Oodake"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-eu/strings.xml b/packages/CarrierDefaultApp/res/values-eu/strings.xml
new file mode 100644
index 0000000..c1f7fa9
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-eu/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktibatu zerbitzua"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Datu-zerbitzua ez dago erabilgarri"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Sakatu zerbitzua aktibatzeko"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Zerbitzua ez dago erabilgarri. Jarri operadorearekin harremanetan."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Sare-zerbitzuaren atarira konektatzen…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Denbora-muga gainditu du sareak. Berriro saiatu nahi duzu?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Sarea ez dago erabilgarri"</string>
+    <string name="quit" msgid="4392968039488794590">"Irten"</string>
+    <string name="wait" msgid="7902715035629500128">"Itxaron"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-gu/strings.xml b/packages/CarrierDefaultApp/res/values-gu/strings.xml
new file mode 100644
index 0000000..c6d7f96
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-gu/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"કૅરિઅર ડિફૉલ્ટ ઍપ્લિકેશન"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"તમારી સેવા સક્રિય કરો"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"કોઈ ડેટા સેવા ઉપલબ્ધ નથી"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"તમારી સેવા સક્રિય કરવા માટે ટૅપ કરો"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"કોઈ સેવા ઉપલબ્ધ નથી, કૃપા કરીને તમારા સેવા પ્રદાતાનો સંપર્ક કરો"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"કૅપ્ટિવ પોર્ટલ સાથે કનેક્ટ થઈ રહ્યું છે..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"નેટવર્કનો સમય સમાપ્ત થયો, શું તમે ફરીથી પ્રયાસ કરશો?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"નેટવર્ક અનુપલબ્ધ"</string>
+    <string name="quit" msgid="4392968039488794590">"છોડી દો"</string>
+    <string name="wait" msgid="7902715035629500128">"રાહ જુઓ"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-hi/strings.xml b/packages/CarrierDefaultApp/res/values-hi/strings.xml
new file mode 100644
index 0000000..a1de4bc
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-hi/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"अपनी सेवा सक्रिय करें"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"कोई डेटा सेवा नहीं है"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"अपनी सेवा सक्रिय करने के लिए टैप करें"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"कोई सेवा नहीं है, कृपया आपको सेवा प्रदान करने वाले से संपर्क करें"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"कैप्टिव पोर्टल से कनेक्ट हो रहा है..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"नेटवर्क रुक गया है, क्या आप फिर से कोशिश करना चाहेंगे?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"नेटवर्क उपलब्ध नहीं है"</string>
+    <string name="quit" msgid="4392968039488794590">"बाहर निकलें"</string>
+    <string name="wait" msgid="7902715035629500128">"प्रतीक्षा करें"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-hr/strings.xml b/packages/CarrierDefaultApp/res/values-hr/strings.xml
new file mode 100644
index 0000000..d5c05de
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-hr/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"Zadana aplikacija mobilnog operatera"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktivirajte uslugu"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Podatkovna usluga nije dostupna"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Dodirnite da biste aktivirali uslugu"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Usluga nije dostupna. Obratite se davatelju usluga."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Povezivanje sa zaštitnim portalom..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Isteklo je vremensko ograničenje mreže. Želite li pokušati ponovo?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Mreža nije dostupna"</string>
+    <string name="quit" msgid="4392968039488794590">"Izlaz"</string>
+    <string name="wait" msgid="7902715035629500128">"Pričekajte"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-hu/strings.xml b/packages/CarrierDefaultApp/res/values-hu/strings.xml
new file mode 100644
index 0000000..c204ee0
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-hu/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"A szolgáltatás aktiválása"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Az adatszolgáltatás szünetel"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Koppintson a szolgáltatás aktiválásához"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"A szolgáltatás szünetel. Kérjük, vegye fel a kapcsolatot szolgáltatójával."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Kapcsolódás a hitelesítési portálhoz…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Hálózati időtúllépés. Megpróbálja újra?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"A hálózat nem áll rendelkezésre"</string>
+    <string name="quit" msgid="4392968039488794590">"Kilépés"</string>
+    <string name="wait" msgid="7902715035629500128">"Várakozás"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-hy/strings.xml b/packages/CarrierDefaultApp/res/values-hy/strings.xml
new file mode 100644
index 0000000..ac53574
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-hy/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Ակտիվացրեք ձեր ծառայությունը"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Տվյալների ծառայությունն անհասանելի է"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Հպեք՝ ծառայությունն ակտիվացնելու համար"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Ծառայությունն անհասանելի է։ Դիմեք ձեր ծառայություններ մատուցողին"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Միանում է մուտքի էջին…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Ցանցի սպասման ժամանակը սպառվել է։ Փորձե՞լ կրկին։"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Ցանցն անհասանելի է"</string>
+    <string name="quit" msgid="4392968039488794590">"Դուրս գալ"</string>
+    <string name="wait" msgid="7902715035629500128">"Սպասել"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-in/strings.xml b/packages/CarrierDefaultApp/res/values-in/strings.xml
new file mode 100644
index 0000000..15b6849
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-in/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"AplikasiDefaultOperator"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktifkan layanan"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Tidak ada layanan data"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Tap untuk mengaktifkan layanan"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Tidak Ada Layanan, hubungi penyedia layanan"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Menyambungkan ke captive portal..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Waktu tunggu jaringan habis, ingin mencoba lagi?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Jaringan tidak tersedia"</string>
+    <string name="quit" msgid="4392968039488794590">"Keluar"</string>
+    <string name="wait" msgid="7902715035629500128">"Tunggu"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-is/strings.xml b/packages/CarrierDefaultApp/res/values-is/strings.xml
new file mode 100644
index 0000000..fb6ca57
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-is/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Virkja þjónustuna"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Ekkert gagnasamband"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Ýttu til að virkja þjónustuna"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Ekkert samband, hafðu samband við þjónustuaðila"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Tengist innskráningarsíðu..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Tímamörk nettengingar runnu út, viltu reyna aftur?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Ekkert net til staðar"</string>
+    <string name="quit" msgid="4392968039488794590">"Hætta"</string>
+    <string name="wait" msgid="7902715035629500128">"Bíða"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-it/strings.xml b/packages/CarrierDefaultApp/res/values-it/strings.xml
new file mode 100644
index 0000000..fca6eb9
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-it/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Attiva il servizio"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Nessun servizio dati"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Tocca per attivare il servizio"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Nessun servizio. Contatta il provider Internet."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Connessione al captive portal…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Timeout di rete. Vuoi riprovare?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Rete non disponibile"</string>
+    <string name="quit" msgid="4392968039488794590">"Esci"</string>
+    <string name="wait" msgid="7902715035629500128">"Attendi"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-iw/strings.xml b/packages/CarrierDefaultApp/res/values-iw/strings.xml
new file mode 100644
index 0000000..60b9168
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-iw/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"הפעל את השירות"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"אין שירות נתונים"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"הקש כדי להפעיל את השירות"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"שירות הנתונים לא זמין. צור קשר עם ספק השירות."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"מתחבר לפורטל שבוי..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"הזמן הקצוב לתפוגת הרשת עבר. תרצה לנסות שוב?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"הרשת אינה זמינה"</string>
+    <string name="quit" msgid="4392968039488794590">"צא"</string>
+    <string name="wait" msgid="7902715035629500128">"המתן"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ka/strings.xml b/packages/CarrierDefaultApp/res/values-ka/strings.xml
new file mode 100644
index 0000000..c191575
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ka/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"თქვენი სერვისის გააქტიურება"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"მონაცემთა სერვისი არ არის"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"შეეხეთ თქვენი სერვისის გასააქტიურებლად"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"სერვისი არ არის — გთხოვთ, დაუკავშირდეთ სერვისის პროვაიდერს"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"მიმდინარეობს ავტორიზაციის პორტალთან დაკავშირება…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"ქსელის დროის ლიმიტი ამოიწურა. გსურთ ხელახლა ცდა?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"ქსელი მიუწვდომელია"</string>
+    <string name="quit" msgid="4392968039488794590">"გასვლა"</string>
+    <string name="wait" msgid="7902715035629500128">"მოცდა"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-kk/strings.xml b/packages/CarrierDefaultApp/res/values-kk/strings.xml
new file mode 100644
index 0000000..2285fda
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-kk/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Қызметті іске қосу"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Деректер қызметі көрсетілмейді"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Қызметті іске қосу үшін түртіңіз"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Қызмет көрсетілмейді, қызмет провайдеріне хабарласыңыз"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Адаптивті порталға қосылуда…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Желі мерзімі аяқталды, әрекетті қайталайсыз ба?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Желі қолжетімді емес"</string>
+    <string name="quit" msgid="4392968039488794590">"Шығу"</string>
+    <string name="wait" msgid="7902715035629500128">"Күту"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-kn/strings.xml b/packages/CarrierDefaultApp/res/values-kn/strings.xml
new file mode 100644
index 0000000..42fe9a0
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-kn/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"ನಿಮ್ಮ ಸೇವೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ಯಾವುದೇ ಡೇಟಾ ಸೇವೆ ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"ನಿಮ್ಮ ಸೇವೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"ಸೇವೆಯು ಲಭ್ಯವಿಲ್ಲ, ನಿಮಗೆ ಸೇವೆ ಒದಗಿಸುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"ವೈ-ಫೈ ಪ್ರಾರಂಭ ಪೋರ್ಟಲ್‌ಗೆ ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"ನೆಟ್‌ವರ್ಕ್‌ ಕಾಲಾವಧಿ ಮುಗಿದಿದೆ, ನೀವು ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಲು ಬಯಸುವಿರಾ?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"ನೆಟ್‌ವರ್ಕ್ ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="quit" msgid="4392968039488794590">"ತ್ಯಜಿಸಿ"</string>
+    <string name="wait" msgid="7902715035629500128">"ನಿರೀಕ್ಷಿಸಿ"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ky/strings.xml b/packages/CarrierDefaultApp/res/values-ky/strings.xml
new file mode 100644
index 0000000..54c9575
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ky/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"ОператордунДемейкиКолдонмосу"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Кызматты жандырыңыз"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Мобилдик туташуу кызматы жок"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Кызматты жандыруу үчүн таптаңыз"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Байланыш жок, кызмат көрсөтүүчүңүзгө кайрылыңыз"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Кирүү бетине туташууда…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Тармактын күтүү убакыты аяктады, кайра аракет кыласызбы?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Тармак жеткиликтүү эмес"</string>
+    <string name="quit" msgid="4392968039488794590">"Чыгуу"</string>
+    <string name="wait" msgid="7902715035629500128">"Күтүү"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-lo/strings.xml b/packages/CarrierDefaultApp/res/values-lo/strings.xml
new file mode 100644
index 0000000..205d9b6
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-lo/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Activate your service"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"No data service"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Tap to activate your service"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"No Service, please contact your service provider"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Connecting to captive portal..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Network timeout, would you like to retry?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Network unavailable"</string>
+    <string name="quit" msgid="4392968039488794590">"ອອກ"</string>
+    <string name="wait" msgid="7902715035629500128">"ລໍ​ຖ້າ"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-lt/strings.xml b/packages/CarrierDefaultApp/res/values-lt/strings.xml
new file mode 100644
index 0000000..61f6d59
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-lt/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Suaktyvinkite paslaugas"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Duomenų paslaugos neteikiamos"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Palieskite, kad suaktyvintumėte paslaugą"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Paslauga neteikiama. Susisiekite su paslaugos teikėju"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Prisijungiama prie fiksuotojo portalo..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Baigėsi skirtasis tinklo laikas. Ar norite bandyti dar kartą?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Tinklas nepasiekiamas"</string>
+    <string name="quit" msgid="4392968039488794590">"Baigti"</string>
+    <string name="wait" msgid="7902715035629500128">"Laukti"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-mk/strings.xml b/packages/CarrierDefaultApp/res/values-mk/strings.xml
new file mode 100644
index 0000000..066fb31
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-mk/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Активирајте ја услугата"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Нема услуга за подотоци"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Допрете за да ја активирате услугата"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Нема услуга, контактирајте со давателот на услуги"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Се поврзува на порталот за автентикација…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Истече времето на мрежата, дали сакате да се обидете повторно?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Мрежата е недостапна"</string>
+    <string name="quit" msgid="4392968039488794590">"Излези"</string>
+    <string name="wait" msgid="7902715035629500128">"Почекај"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ml/strings.xml b/packages/CarrierDefaultApp/res/values-ml/strings.xml
new file mode 100644
index 0000000..b72e877
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ml/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"നിങ്ങളുടെ സേവനം ‌ആക്റ്റി‌വേറ്റ് ‌ചെയ്യുക"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ഡാറ്റ ‌സേവനം ലഭ്യമല്ല"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"നിങ്ങളുടെ സേവനം ‌ആക്റ്റി‌വേറ്റ് ‌ചെയ്യാൻ ടാപ്പുചെയ്യുക"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"സേവനം ‌ലഭ്യമല്ല, നിങ്ങളുടെ ‌സേവന‌ദാതാവിനെ ‌ബന്ധപ്പെടുക"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"ക്യാപ്റ്റീവ് പോർട്ടലിലേയ്ക്ക് കണക്റ്റുചെയ്യുന്നു..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"നെറ്റ്‌വർക്ക് കാലഹരണപ്പെട്ടു, വീണ്ടും ശ്രമിക്കണോ?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"നെറ്റ്‌വർക്ക് ലഭ്യമല്ല"</string>
+    <string name="quit" msgid="4392968039488794590">"പുറത്തുപോവുക"</string>
+    <string name="wait" msgid="7902715035629500128">"കാത്തിരിക്കുക"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-mn/strings.xml b/packages/CarrierDefaultApp/res/values-mn/strings.xml
new file mode 100644
index 0000000..c262e8f
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-mn/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Үйлчилгээгээ идэвхжүүлнэ үү"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Дата үйлчилгээ алга"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Үйлчилгээгээ идэвхжүүлэхийн тулд товшино уу"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Үйлчилгээ алга. Үйлчилгээ үзүүлэгчтэйгээ холбогдоно уу"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Дамжих порталд холбогдож байна..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Сүлжээний хугацаа дууссан байна. Дахин оролдох уу?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Сүлжээ боломжгүй"</string>
+    <string name="quit" msgid="4392968039488794590">"Гарах"</string>
+    <string name="wait" msgid="7902715035629500128">"Хүлээх"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-mr/strings.xml b/packages/CarrierDefaultApp/res/values-mr/strings.xml
new file mode 100644
index 0000000..beff675
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-mr/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"आपली सेवा सक्रिय करा"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"डेटा सेवा नाही"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"आपली सेवा सक्रिय करण्यासाठी टॅप करा"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"सेवा नाही, कृपया आपल्या सेवा प्रदात्याशी संपर्क साधा"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"कॅप्टिव्ह पोर्टलशी कनेक्ट करत आहे..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"नेटवर्क कालबाह्य झाले, आपण पुन्हा प्रयत्न करू इच्छिता का?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"नेटवर्क अनुपलब्ध"</string>
+    <string name="quit" msgid="4392968039488794590">"बाहेर पडा"</string>
+    <string name="wait" msgid="7902715035629500128">"प्रतीक्षा करा"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-my/strings.xml b/packages/CarrierDefaultApp/res/values-my/strings.xml
new file mode 100644
index 0000000..b0e929e
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-my/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"ဝန်ဆောင်မှုကို စဖွင့်အသုံးပြုရန်"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ဒေတာချိတ်ဆက်မှုမရှိပါ"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"ဝန်ဆောင်မှုကို စဖွင့်အသုံးပြုရန် တို့ပါ"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"လိုင်းမရှိပါ။ သင့်ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"captive portal သို့ ချိတ်ဆက်နေသည်..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"ချိတ်ဆက်မှု ပြတ်တောက်သွားပါသည်။ ထပ်လုပ်လိုပါသလား။"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"ကွန်ရက်ချိတ်ဆက်မှု မရှိပါ"</string>
+    <string name="quit" msgid="4392968039488794590">"ထွက်ရန်"</string>
+    <string name="wait" msgid="7902715035629500128">"စောင့်ရန်"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-nb/strings.xml b/packages/CarrierDefaultApp/res/values-nb/strings.xml
new file mode 100644
index 0000000..6f69e9a
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-nb/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktiver tjenesten"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Ingen datatjeneste"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Trykk for å aktivere tjenesten"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Ingen dekning – ta kontakt med tjenesteleverandøren din."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Kobler til obligatorisk side …"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Tidsavbrudd for nettverk – vil du prøve på nytt?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Nettverket er utilgjengelig"</string>
+    <string name="quit" msgid="4392968039488794590">"Avslutt"</string>
+    <string name="wait" msgid="7902715035629500128">"Vent"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ne/strings.xml b/packages/CarrierDefaultApp/res/values-ne/strings.xml
new file mode 100644
index 0000000..aa5766b
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ne/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"आफ्नो सेवालाई सक्रिय पार्नुहोस्‌"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"डेटा सेवा छैन"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"आफ्नो सेवालाई सक्रिय पार्न ट्याप गर्नुहोस्"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"सेवा उपलब्ध छैन,कृपया आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नुहोस्"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"क्याप्टिभ पोर्टलमा जडान हुँदैछ..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"नेटवर्कमा जडान हुने समय समाप्त भयो, के तपाईँ पुनः प्रयास गर्न चाहानुहुन्छ?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"नेटवर्क उपलब्ध छैन"</string>
+    <string name="quit" msgid="4392968039488794590">"बाहिरिनुहोस्"</string>
+    <string name="wait" msgid="7902715035629500128">"पर्खनुहोस्"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-nl/strings.xml b/packages/CarrierDefaultApp/res/values-nl/strings.xml
new file mode 100644
index 0000000..b7da335
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-nl/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"De service activeren"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Geen gegevensservice"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Tik om de service te activeren"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Geen service. Neem contact op met je serviceprovider."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Verbinding maken met captive portal..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Time-out voor het netwerk. Wil je het opnieuw proberen?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Netwerk niet beschikbaar"</string>
+    <string name="quit" msgid="4392968039488794590">"Stoppen"</string>
+    <string name="wait" msgid="7902715035629500128">"Wachten"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-pa/strings.xml b/packages/CarrierDefaultApp/res/values-pa/strings.xml
new file mode 100644
index 0000000..ea07e88
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-pa/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"ਆਪਣੀ ਸੇਵਾ ਨੂੰ ਸਰਗਰਮ ਕਰੋ"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ਕੋਈ ਡੈਟਾ ਸੇਵਾ ਨਹੀਂ"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"ਆਪਣੀ ਸੇਵਾ ਨੂੰ ਸਰਗਰਮ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ, ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਸੇਵਾ ਪ੍ਰਦਾਨਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"ਕੈਪਟਿਵ ਪੋਰਟਲ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"ਨੈੱਟਵਰਕ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋ ਗਿਆ, ਕੀ ਤੁਸੀਂ ਮੁੜ-ਕੋਸ਼ਿਸ਼ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+    <string name="quit" msgid="4392968039488794590">"ਛੱਡੋ"</string>
+    <string name="wait" msgid="7902715035629500128">"ਉਡੀਕ ਕਰੋ"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml b/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..e730dc6
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-pt-rBR/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Ative seu serviço"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Sem serviço de dados"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Toque para ativar seu serviço"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Sem serviço. Entre em contato com seu provedor de serviços"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Conectando-se ao portal cativo…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Tempo limite de rede. Deseja tentar novamente?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Rede indisponível"</string>
+    <string name="quit" msgid="4392968039488794590">"Sair"</string>
+    <string name="wait" msgid="7902715035629500128">"Esperar"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml b/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..a2c10e9
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-pt-rPT/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Ativar o seu serviço"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Sem serviço de dados"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Toque para ativar o seu serviço"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Sem serviço. Contacte o seu fornecedor de serviços"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"A ligar ao portal cativo…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"O limite de tempo da rede foi excedido. Pretende tentar novamente?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Rede não disponível"</string>
+    <string name="quit" msgid="4392968039488794590">"Sair"</string>
+    <string name="wait" msgid="7902715035629500128">"Aguardar"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-pt/strings.xml b/packages/CarrierDefaultApp/res/values-pt/strings.xml
new file mode 100644
index 0000000..e730dc6
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-pt/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Ative seu serviço"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Sem serviço de dados"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Toque para ativar seu serviço"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Sem serviço. Entre em contato com seu provedor de serviços"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Conectando-se ao portal cativo…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Tempo limite de rede. Deseja tentar novamente?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Rede indisponível"</string>
+    <string name="quit" msgid="4392968039488794590">"Sair"</string>
+    <string name="wait" msgid="7902715035629500128">"Esperar"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ro/strings.xml b/packages/CarrierDefaultApp/res/values-ro/strings.xml
new file mode 100644
index 0000000..234c410
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ro/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"AplicațiePrestabilităOperator"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Activați serviciul"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Fără serviciu de date"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Atingeți pentru a activa serviciul"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Fără serviciu. Contactați furnizorul de servicii."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Se conectează la portalul captiv..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Timpul limită pentru rețea a expirat. Doriți să încercați din nou?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Rețea indisponibilă"</string>
+    <string name="quit" msgid="4392968039488794590">"Ieșiți"</string>
+    <string name="wait" msgid="7902715035629500128">"Așteptați"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ru/strings.xml b/packages/CarrierDefaultApp/res/values-ru/strings.xml
new file mode 100644
index 0000000..b038f0f
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ru/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Активируйте сервис"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Нет подключения"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Нажмите, чтобы активировать сервис"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Обратитесь к своему поставщику услуг Интернета."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Подключение к странице входа…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Время ожидания для сети истекло. Повторить попытку?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Сеть недоступна"</string>
+    <string name="quit" msgid="4392968039488794590">"Закрыть"</string>
+    <string name="wait" msgid="7902715035629500128">"Подождать"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-sk/strings.xml b/packages/CarrierDefaultApp/res/values-sk/strings.xml
new file mode 100644
index 0000000..a73e55c
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-sk/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"Predvolená aplikácia operátora"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktivujte službu"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Žiadna dátová služba"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Klepnutím aktivujete službu"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Žiadna služba, kontaktujte svojho poskytovateľa služieb"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Pripájanie k prihlasovaciemu portálu…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Vypršal časový limit siete. Chcete to skúsiť znova?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Sieť nie je k dispozícii"</string>
+    <string name="quit" msgid="4392968039488794590">"Ukončiť"</string>
+    <string name="wait" msgid="7902715035629500128">"Počkať"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-sq/strings.xml b/packages/CarrierDefaultApp/res/values-sq/strings.xml
new file mode 100644
index 0000000..316fe1d
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-sq/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Aktivizo shërbimin"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Nuk ka shërbim për të dhënat"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Trokit për të aktivizuar shërbimin"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Nuk ka shërbim, kontakto me ofruesin e shërbimit"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Po lidhet me portalin e izoluar..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Koha e pritjes së rrjetit përfundoi. Dëshiron të provosh sërish?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Rrjeti është i padisponueshëm"</string>
+    <string name="quit" msgid="4392968039488794590">"Dil"</string>
+    <string name="wait" msgid="7902715035629500128">"Prit"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-sr/strings.xml b/packages/CarrierDefaultApp/res/values-sr/strings.xml
new file mode 100644
index 0000000..5c381f4
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-sr/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Активирајте услугу"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Нема услуге преноса података"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Додирните да бисте активирали услугу"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Нема услуге. Контактирајте добављача услуге"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Повезује се са улазним порталом…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Временско ограничење мреже је истекло. Желите ли да пробате поново?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Мрежа није доступна"</string>
+    <string name="quit" msgid="4392968039488794590">"Затвори"</string>
+    <string name="wait" msgid="7902715035629500128">"Чекај"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-sw/strings.xml b/packages/CarrierDefaultApp/res/values-sw/strings.xml
new file mode 100644
index 0000000..dfcc5c4
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-sw/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Anzisha huduma yako"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Hakuna huduma ya data"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Gonga ili uanzishe huduma yako"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Hakuna Huduma, tafadhali wasiliana na mtoa huduma wako"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Inaunganisha kwenye ukurasa wa mwanzo..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Muda wa kutumia mtandao umekwisha, ungependa kujaribu tena?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Mtandao haupatikani"</string>
+    <string name="quit" msgid="4392968039488794590">"Ondoka"</string>
+    <string name="wait" msgid="7902715035629500128">"Subiri"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-te/strings.xml b/packages/CarrierDefaultApp/res/values-te/strings.xml
new file mode 100644
index 0000000..70fe499
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-te/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"మీ సేవని సక్రియం చేయండి"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"డేటా సేవ లేదు"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"మీ సేవని సక్రియం చేయడానికి నొక్కండి"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"సేవ లేదు, దయచేసి మీ సేవా ప్రదాతను సంప్రదించండి"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"క్యాప్టివ్ పోర్టల్‌కు కనెక్ట్ చేస్తోంది..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"నెట్‌వర్క్ గడువు సమయం ముగిసింది, మీరు మళ్లీ ప్రయత్నించాలనుకుంటున్నారా?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"నెట్‌వర్క్ అందుబాటులో లేదు"</string>
+    <string name="quit" msgid="4392968039488794590">"నిష్క్రమించు"</string>
+    <string name="wait" msgid="7902715035629500128">"వేచి ఉండండి"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-th/strings.xml b/packages/CarrierDefaultApp/res/values-th/strings.xml
new file mode 100644
index 0000000..66526ea
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-th/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"เปิดใช้งานบริการของคุณ"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ไม่มีบริการข้อมูล"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"แตะเพื่อเปิดใช้งานบริการ"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"ไม่มีบริการ โปรดติดต่อผู้ให้บริการ"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"กำลังเชื่อมต่อแคพทีฟพอร์ทัล..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"หมดเวลาเชื่อมต่อเครือข่ายแล้ว ลองอีกครั้งไหม"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"เครือข่ายใช้ไม่ได้"</string>
+    <string name="quit" msgid="4392968039488794590">"ปิด"</string>
+    <string name="wait" msgid="7902715035629500128">"รอ"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-ur/strings.xml b/packages/CarrierDefaultApp/res/values-ur/strings.xml
new file mode 100644
index 0000000..3002d40
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-ur/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"اپنی سروس فعال کریں"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"ڈیٹا سروس موجود نہیں"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"اپنی سروس فعال کرنے کے لیے تھپتھپائیں"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"سروس موجود نہیں، براہ کرم اپنے خدمت کے فراہم کنندہ سے رابطہ کریں"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"کیپٹو پورٹل سے منسلک ہو رہا ہے..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"نیٹ ورک ٹائم آؤٹ، کیا آپ دوبارہ کوشش کرنا چاہیں گے؟"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"نیٹ ورک غیر دستیاب ہے"</string>
+    <string name="quit" msgid="4392968039488794590">"چھوڑ دیں"</string>
+    <string name="wait" msgid="7902715035629500128">"انتظار کریں"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-uz/strings.xml b/packages/CarrierDefaultApp/res/values-uz/strings.xml
new file mode 100644
index 0000000..92a0af0
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-uz/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Xizmatni faollashtiring"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Aloqa yo‘q"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Xizmatni faollashtirish uchun bosing"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Xizmat yo‘q. Xizmat ta’minotchisi bilan bog‘laning."</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Kirish sahifasida haqiqiylik tekshiruvi bor tarmoqqa ulanilmoqda…"</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Tarmoq uchun kutish vaqti tugadi. Qayta urinilsinmi?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Tarmoq mavjud emas"</string>
+    <string name="quit" msgid="4392968039488794590">"Chiqish"</string>
+    <string name="wait" msgid="7902715035629500128">"Kutish"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-vi/strings.xml b/packages/CarrierDefaultApp/res/values-vi/strings.xml
new file mode 100644
index 0000000..06ea6f0
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-vi/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Kích hoạt dịch vụ của bạn"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Không có dịch vụ dữ liệu"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Nhấn để kích hoạt dịch vụ của bạn"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Không có dịch vụ, vui lòng liên hệ với nhà cung cấp dịch vụ của bạn"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Đang kết nối với cổng cố định..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Hết thời gian chờ mạng, bạn có muốn thử lại không?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Mạng không khả dụng"</string>
+    <string name="quit" msgid="4392968039488794590">"Thoát"</string>
+    <string name="wait" msgid="7902715035629500128">"Đợi"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/res/values-zu/strings.xml b/packages/CarrierDefaultApp/res/values-zu/strings.xml
new file mode 100644
index 0000000..12e8a7d
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/values-zu/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="5247871339820894594">"CarrierDefaultApp"</string>
+    <string name="portal_notification_id" msgid="267536768510843288">"Sebenzisa isevisi yakho"</string>
+    <string name="no_data_notification_id" msgid="5216950045164991172">"Ayikho isevisi yedatha"</string>
+    <string name="portal_notification_detail" msgid="2860620550281695686">"Thepha ukuze usebenzise isevisi yakho"</string>
+    <string name="no_data_notification_detail" msgid="1757413358517680719">"Ayikho isevisi, sicela uxhumane nomhlinzeki wakho wesevisi"</string>
+    <string name="progress_dialogue_network_connection" msgid="4964125154591905581">"Ixhuma kuphothali yabathunjiweyo..."</string>
+    <string name="alert_dialogue_network_timeout" msgid="4515760047815901797">"Ukuphela kwesikhathi senethiwekhi, ungathanda ukuzama futhi?"</string>
+    <string name="alert_dialogue_network_timeout_title" msgid="3117252205484891837">"Inethiwekhi ayitholakali"</string>
+    <string name="quit" msgid="4392968039488794590">"Yeka"</string>
+    <string name="wait" msgid="7902715035629500128">"Linda"</string>
+</resources>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java
index bc0fa02..3fd89d9 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java
@@ -28,6 +28,10 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         Log.d(TAG, "onReceive intent: " + intent.getAction());
+        if (ProvisionObserver.isDeferredForProvision(context, intent)) {
+            Log.d(TAG, "skip carrier actions during provisioning");
+            return;
+        }
         List<Integer> actionList = CustomConfigLoader.loadCarrierActionList(context, intent);
         for (int actionIdx : actionList) {
             Log.d(TAG, "apply carrier action idx: " + actionIdx);
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
new file mode 100644
index 0000000..3e34f0a
--- /dev/null
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.carrierdefaultapp;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.telephony.TelephonyIntents;
+
+/**
+ * Service to run {@link android.app.job.JobScheduler} job.
+ * Service to monitor when there is a change to conent URI
+ * {@link android.provider.Settings.Global#DEVICE_PROVISIONED DEVICE_PROVISIONED}
+ */
+public class ProvisionObserver extends JobService {
+
+    private static final String TAG = ProvisionObserver.class.getSimpleName();
+    public static final int PROVISION_OBSERVER_REEVALUATION_JOB_ID = 1;
+    // minimum & maximum update delay TBD
+    private static final int CONTENT_UPDATE_DELAY_MS = 100;
+    private static final int CONTENT_MAX_DELAY_MS = 200;
+
+    @Override
+    public boolean onStartJob(JobParameters jobParameters) {
+        switch (jobParameters.getJobId()) {
+            case PROVISION_OBSERVER_REEVALUATION_JOB_ID:
+                if (isProvisioned(this)) {
+                    Log.d(TAG, "device provisioned, force network re-evaluation");
+                    final ConnectivityManager connMgr = ConnectivityManager.from(this);
+                    Network[] info = connMgr.getAllNetworks();
+                    for (Network nw : info) {
+                        final NetworkCapabilities nc = connMgr.getNetworkCapabilities(nw);
+                        if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+                                && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+                            // force connectivity re-evaluation to assume skipped carrier actions.
+                            // one of the following calls will match the last evaluation.
+                            connMgr.reportNetworkConnectivity(nw, true);
+                            connMgr.reportNetworkConnectivity(nw, false);
+                            break;
+                        }
+                    }
+                }
+            default:
+                break;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters jobParameters) {
+        return false;
+    }
+
+    // Returns true if the device is not provisioned yet (in setup wizard), false otherwise
+    private static boolean isProvisioned(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) == 1;
+    }
+
+    /**
+     * Static utility function to schedule a job to execute upon the change of content URI
+     * {@link android.provider.Settings.Global#DEVICE_PROVISIONED DEVICE_PROVISIONED}.
+     * @param context The context used to retrieve the {@link ComponentName} and system services
+     * @return true carrier actions are deferred due to phone provisioning process, false otherwise
+     */
+    public static boolean isDeferredForProvision(Context context, Intent intent) {
+        if (isProvisioned(context)) {
+            return false;
+        }
+        int jobId;
+        switch(intent.getAction()) {
+            case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED:
+                jobId = PROVISION_OBSERVER_REEVALUATION_JOB_ID;
+                break;
+            default:
+                return false;
+        }
+        final JobScheduler jobScheduler =  (JobScheduler) context.getSystemService(
+                Context.JOB_SCHEDULER_SERVICE);
+        final JobInfo job = new JobInfo.Builder(jobId,
+                new ComponentName(context, ProvisionObserver.class))
+                .addTriggerContentUri(new JobInfo.TriggerContentUri(
+                        Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), 0))
+                .setTriggerContentUpdateDelay(CONTENT_UPDATE_DELAY_MS)
+                .setTriggerContentMaxDelay(CONTENT_MAX_DELAY_MS)
+                .build();
+        jobScheduler.schedule(job);
+        return true;
+    }
+}
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.mk b/packages/CarrierDefaultApp/tests/unit/Android.mk
index 092df50..63bd0b1 100644
--- a/packages/CarrierDefaultApp/tests/unit/Android.mk
+++ b/packages/CarrierDefaultApp/tests/unit/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target-minus-junit4 legacy-android-test
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 91c943d..8cdb906 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -290,6 +290,7 @@
         monitor.reportFailedStrongAuthUnlockAttempt(userId);
         mLockPatternUtils.reportFailedPasswordAttempt(userId);
         if (timeoutMs > 0) {
+            mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
             showTimeoutDialog(userId, timeoutMs);
         }
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index e1657c7..f8f4f2a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -29,6 +29,7 @@
 import android.util.Slog;
 import android.util.TypedValue;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.GridLayout;
 import android.widget.TextClock;
 import android.widget.TextView;
@@ -48,6 +49,7 @@
     private TextClock mDateView;
     private TextClock mClockView;
     private TextView mOwnerInfo;
+    private ViewGroup mClockContainer;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -105,6 +107,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mClockContainer = (ViewGroup) findViewById(R.id.keyguard_clock_container);
         mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
         mDateView = (TextClock) findViewById(R.id.date_view);
         mClockView = (TextClock) findViewById(R.id.clock_view);
@@ -167,6 +170,11 @@
         }
     }
 
+    public int getClockBottom() {
+        return mClockView.getBottom() +
+                ((MarginLayoutParams) mClockView.getLayoutParams()).bottomMargin;
+    }
+
     public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) {
         if (info == null) {
             return "";
@@ -260,4 +268,15 @@
             cacheKey = key;
         }
     }
+
+    public void setDark(boolean dark) {
+        final int N = mClockContainer.getChildCount();
+        for (int i = 0; i < N; i++) {
+            View child = mClockContainer.getChildAt(i);
+            if (child == mClockView) {
+                continue;
+            }
+            child.setAlpha(dark ? 0 : 1);
+        }
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0a7bdbf..80d4a26 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -26,7 +26,6 @@
 import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
 import static android.os.BatteryManager.EXTRA_PLUGGED;
 import static android.os.BatteryManager.EXTRA_STATUS;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 
 import android.app.ActivityManager;
 import android.app.AlarmManager;
@@ -106,12 +105,6 @@
     private static final String ACTION_FACE_UNLOCK_STOPPED
             = "com.android.facelock.FACE_UNLOCK_STOPPED";
 
-    private static final String ACTION_STRONG_AUTH_TIMEOUT =
-            "com.android.systemui.ACTION_STRONG_AUTH_TIMEOUT";
-    private static final String USER_ID = "com.android.systemui.USER_ID";
-
-    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
-
     // Callback messages
     private static final int MSG_TIME_UPDATE = 301;
     private static final int MSG_BATTERY_UPDATE = 302;
@@ -203,7 +196,6 @@
     private boolean mDeviceInteractive;
     private boolean mScreenOn;
     private SubscriptionManager mSubscriptionManager;
-    private AlarmManager mAlarmManager;
     private List<SubscriptionInfo> mSubscriptionInfo;
     private TrustManager mTrustManager;
     private UserManager mUserManager;
@@ -588,26 +580,12 @@
     }
 
     public void reportSuccessfulStrongAuthUnlockAttempt() {
-        scheduleStrongAuthTimeout();
         if (mFpm != null) {
             byte[] token = null; /* TODO: pass real auth token once fp HAL supports it */
             mFpm.resetTimeout(token);
         }
     }
 
-    private void scheduleStrongAuthTimeout() {
-        final DevicePolicyManager dpm =
-                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        long when = SystemClock.elapsedRealtime() + dpm.getRequiredStrongAuthTimeout(null,
-                sCurrentUser);
-        Intent intent = new Intent(ACTION_STRONG_AUTH_TIMEOUT);
-        intent.putExtra(USER_ID, sCurrentUser);
-        PendingIntent sender = PendingIntent.getBroadcast(mContext,
-                sCurrentUser, intent, PendingIntent.FLAG_CANCEL_CURRENT);
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, sender);
-        notifyStrongAuthStateChanged(sCurrentUser);
-    }
-
     private void notifyStrongAuthStateChanged(int userId) {
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -723,17 +701,6 @@
         }
     };
 
-    private final BroadcastReceiver mStrongAuthTimeoutReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (ACTION_STRONG_AUTH_TIMEOUT.equals(intent.getAction())) {
-                int userId = intent.getIntExtra(USER_ID, -1);
-                mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, userId);
-                notifyStrongAuthStateChanged(userId);
-            }
-        }
-    };
-
     private final FingerprintManager.LockoutResetCallback mLockoutResetCallback
             = new FingerprintManager.LockoutResetCallback() {
         @Override
@@ -1034,7 +1001,6 @@
     private KeyguardUpdateMonitor(Context context) {
         mContext = context;
         mSubscriptionManager = SubscriptionManager.from(context);
-        mAlarmManager = context.getSystemService(AlarmManager.class);
         mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
         mStrongAuthTracker = new StrongAuthTracker(context);
 
@@ -1094,10 +1060,6 @@
             e.rethrowAsRuntimeException();
         }
 
-        IntentFilter strongAuthTimeoutFilter = new IntentFilter();
-        strongAuthTimeoutFilter.addAction(ACTION_STRONG_AUTH_TIMEOUT);
-        context.registerReceiver(mStrongAuthTimeoutReceiver, strongAuthTimeoutFilter,
-                PERMISSION_SELF, null /* handler */);
         mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
         mTrustManager.registerTrustListener(this);
         mLockPatternUtils = new LockPatternUtils(context);
diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk
index c9e7195..a9e9b2e 100644
--- a/packages/MtpDocumentsProvider/Android.mk
+++ b/packages/MtpDocumentsProvider/Android.mk
@@ -6,7 +6,6 @@
 LOCAL_PACKAGE_NAME := MtpDocumentsProvider
 LOCAL_CERTIFICATE := media
 LOCAL_PRIVILEGED_MODULE := true
-LOCAL_JNI_SHARED_LIBRARIES := libappfuse_jni
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
 # Only enable asserts on userdebug/eng builds
diff --git a/packages/MtpDocumentsProvider/jni/Android.mk b/packages/MtpDocumentsProvider/jni/Android.mk
deleted file mode 100644
index d545b14..0000000
--- a/packages/MtpDocumentsProvider/jni/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    com_android_mtp_AppFuse.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libandroid_runtime \
-    libnativehelper \
-    liblog
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-LOCAL_MODULE := libappfuse_jni
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
deleted file mode 100644
index e948cf7..0000000
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "AppFuseJNI"
-#include "utils/Log.h"
-
-#include <assert.h>
-#include <dirent.h>
-#include <inttypes.h>
-
-#include <linux/fuse.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-
-#include <map>
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "nativehelper/ScopedPrimitiveArray.h"
-#include "nativehelper/ScopedLocalRef.h"
-
-namespace {
-
-// The numbers came from sdcard.c.
-// Maximum number of bytes to write/read in one request/one reply.
-constexpr size_t MAX_WRITE = 256 * 1024;
-constexpr size_t MAX_READ = 128 * 1024;
-
-constexpr size_t NUM_MAX_HANDLES = 1024;
-
-// Largest possible request.
-// The request size is bounded by the maximum size of a FUSE_WRITE request
-// because it has the largest possible data payload.
-constexpr size_t MAX_REQUEST_SIZE = sizeof(struct fuse_in_header) +
-        sizeof(struct fuse_write_in) + (MAX_WRITE > MAX_READ ? MAX_WRITE : MAX_READ);
-
-static jclass app_fuse_class;
-static jmethodID app_fuse_get_file_size;
-static jmethodID app_fuse_read_object_bytes;
-static jmethodID app_fuse_write_object_bytes;
-static jmethodID app_fuse_flush_file_handle;
-static jmethodID app_fuse_close_file_handle;
-static jfieldID app_fuse_buffer;
-
-// NOTE:
-// FuseRequest and FuseResponse shares the same buffer to save memory usage, so the handlers must
-// not access input buffer after writing data to output buffer.
-struct FuseRequest {
-    char buffer[MAX_REQUEST_SIZE];
-    FuseRequest() {}
-    const struct fuse_in_header& header() const {
-        return *(const struct fuse_in_header*) buffer;
-    }
-    void* data() {
-        return (buffer + sizeof(struct fuse_in_header));
-    }
-    size_t data_length() const {
-        return header().len - sizeof(struct fuse_in_header);
-    }
-};
-
-template<typename T>
-class FuseResponse {
-   size_t size_;
-   T* const buffer_;
-public:
-   explicit FuseResponse(void* buffer) : size_(0), buffer_(static_cast<T*>(buffer)) {}
-
-   void prepare_buffer(size_t size = sizeof(T)) {
-       memset(buffer_, 0, size);
-       size_ = size;
-   }
-
-   void set_size(size_t size) {
-       size_ = size;
-   }
-
-   size_t size() const { return size_; }
-   T* data() const { return buffer_; }
-};
-
-class ScopedFd {
-    int mFd;
-
-public:
-    explicit ScopedFd(int fd) : mFd(fd) {}
-    ~ScopedFd() {
-        close(mFd);
-    }
-    operator int() {
-        return mFd;
-    }
-};
-
-/**
- * Fuse implementation consists of handlers parsing FUSE commands.
- */
-class AppFuse {
-    JNIEnv* env_;
-    jobject self_;
-
-    // Map between file handle and inode.
-    std::map<uint32_t, uint64_t> handles_;
-    uint32_t handle_counter_;
-
-public:
-    AppFuse(JNIEnv* env, jobject self) :
-        env_(env), self_(self), handle_counter_(0) {}
-
-    void handle_fuse_request(int fd, FuseRequest* req) {
-        ALOGV("Request op=%d", req->header().opcode);
-        switch (req->header().opcode) {
-            // TODO: Handle more operations that are enough to provide seekable
-            // FD.
-            case FUSE_LOOKUP:
-                invoke_handler(fd, req, &AppFuse::handle_fuse_lookup);
-                return;
-            case FUSE_FORGET:
-                // Return without replying.
-                return;
-            case FUSE_INIT:
-                invoke_handler(fd, req, &AppFuse::handle_fuse_init);
-                return;
-            case FUSE_GETATTR:
-                invoke_handler(fd, req, &AppFuse::handle_fuse_getattr);
-                return;
-            case FUSE_OPEN:
-                invoke_handler(fd, req, &AppFuse::handle_fuse_open);
-                return;
-            case FUSE_READ:
-                invoke_handler(fd, req, &AppFuse::handle_fuse_read);
-                return;
-            case FUSE_WRITE:
-                invoke_handler(fd, req, &AppFuse::handle_fuse_write);
-                return;
-            case FUSE_RELEASE:
-                invoke_handler(fd, req, &AppFuse::handle_fuse_release);
-                return;
-            case FUSE_FLUSH:
-                invoke_handler(fd, req, &AppFuse::handle_fuse_flush);
-                return;
-            default: {
-                ALOGV("NOTIMPL op=%d uniq=%" PRIx64 " nid=%" PRIx64 "\n",
-                      req->header().opcode,
-                      req->header().unique,
-                      req->header().nodeid);
-                fuse_reply(fd, req->header().unique, -ENOSYS, NULL, 0);
-                return;
-            }
-        }
-    }
-
-private:
-    int handle_fuse_lookup(const fuse_in_header& header,
-                           const char* name,
-                           FuseResponse<fuse_entry_out>* out) {
-        if (header.nodeid != 1) {
-            return -ENOENT;
-        }
-
-        const int n = atoi(name);
-        if (n == 0) {
-            return -ENOENT;
-        }
-
-        int64_t size = get_file_size(n);
-        if (size < 0) {
-            return -ENOENT;
-        }
-
-        out->prepare_buffer();
-        out->data()->nodeid = n;
-        out->data()->attr_valid = 10;
-        out->data()->entry_valid = 10;
-        out->data()->attr.ino = n;
-        out->data()->attr.mode = S_IFREG | 0777;
-        out->data()->attr.size = size;
-        return 0;
-    }
-
-    int handle_fuse_init(const fuse_in_header&,
-                         const fuse_init_in* in,
-                         FuseResponse<fuse_init_out>* out) {
-        // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
-        // defined (fuse version 7.6). The structure is the same from 7.6 through
-        // 7.22. Beginning with 7.23, the structure increased in size and added
-        // new parameters.
-        if (in->major != FUSE_KERNEL_VERSION || in->minor < 6) {
-            ALOGE("Fuse kernel version mismatch: Kernel version %d.%d, "
-                  "Expected at least %d.6",
-                  in->major, in->minor, FUSE_KERNEL_VERSION);
-            return -1;
-        }
-
-        // Before writing |out|, we need to copy data from |in|.
-        const uint32_t minor = in->minor;
-        const uint32_t max_readahead = in->max_readahead;
-
-        // We limit ourselves to 15 because we don't handle BATCH_FORGET yet
-        size_t response_size = sizeof(fuse_init_out);
-#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
-        // FUSE_KERNEL_VERSION >= 23.
-
-        // If the kernel only works on minor revs older than or equal to 22,
-        // then use the older structure size since this code only uses the 7.22
-        // version of the structure.
-        if (minor <= 22) {
-            response_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
-        }
-#endif
-        out->prepare_buffer(response_size);
-        out->data()->major = FUSE_KERNEL_VERSION;
-        out->data()->minor = std::min(minor, 15u);
-        out->data()->max_readahead = max_readahead;
-        out->data()->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
-        out->data()->max_background = 32;
-        out->data()->congestion_threshold = 32;
-        out->data()->max_write = MAX_WRITE;
-
-        return 0;
-    }
-
-    int handle_fuse_getattr(const fuse_in_header& header,
-                            const fuse_getattr_in* /* in */,
-                            FuseResponse<fuse_attr_out>* out) {
-        out->prepare_buffer();
-        out->data()->attr_valid = 10;
-        out->data()->attr.ino = header.nodeid;
-        if (header.nodeid == 1) {
-            out->data()->attr.mode = S_IFDIR | 0777;
-            out->data()->attr.size = 0;
-        } else {
-            int64_t size = get_file_size(header.nodeid);
-            if (size < 0) {
-                return -ENOENT;
-            }
-            out->data()->attr.mode = S_IFREG | 0777;
-            out->data()->attr.size = size;
-        }
-
-        return 0;
-    }
-
-    int handle_fuse_open(const fuse_in_header& header,
-                         const fuse_open_in* /* in */,
-                         FuseResponse<fuse_open_out>* out) {
-        if (handles_.size() >= NUM_MAX_HANDLES) {
-            // Too many open files.
-            return -EMFILE;
-        }
-        uint32_t handle;
-        do {
-           handle = handle_counter_++;
-        } while (handles_.count(handle) != 0);
-        handles_.insert(std::make_pair(handle, header.nodeid));
-
-        out->prepare_buffer();
-        out->data()->fh = handle;
-        return 0;
-    }
-
-    int handle_fuse_read(const fuse_in_header& /* header */,
-                         const fuse_read_in* in,
-                         FuseResponse<void>* out) {
-        if (in->size > MAX_READ) {
-            return -EINVAL;
-        }
-        const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh);
-        if (it == handles_.end()) {
-            return -EBADF;
-        }
-        uint64_t offset = in->offset;
-        uint32_t size = in->size;
-
-        // Overwrite the size after writing data.
-        out->prepare_buffer(0);
-        const int64_t result = get_object_bytes(it->second, offset, size, out->data());
-        if (result < 0) {
-            return result;
-        }
-        out->set_size(result);
-        return 0;
-    }
-
-    int handle_fuse_write(const fuse_in_header& /* header */,
-                          const fuse_write_in* in,
-                          FuseResponse<fuse_write_out>* out) {
-        if (in->size > MAX_WRITE) {
-            return -EINVAL;
-        }
-        const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh);
-        if (it == handles_.end()) {
-            return -EBADF;
-        }
-        const uint64_t offset = in->offset;
-        const uint32_t size = in->size;
-        const void* const buffer = reinterpret_cast<const uint8_t*>(in) + sizeof(fuse_write_in);
-        uint32_t written_size;
-        const int result = write_object_bytes(
-                in->fh, it->second, offset, size, buffer, &written_size);
-        if (result < 0) {
-            return result;
-        }
-        out->prepare_buffer();
-        out->data()->size = written_size;
-        return 0;
-    }
-
-    int handle_fuse_release(const fuse_in_header& /* header */,
-                            const fuse_release_in* in,
-                            FuseResponse<void>* /* out */) {
-        handles_.erase(in->fh);
-        return env_->CallIntMethod(self_, app_fuse_close_file_handle, file_handle_to_jlong(in->fh));
-    }
-
-    int handle_fuse_flush(const fuse_in_header& /* header */,
-                          const fuse_flush_in* in,
-                          FuseResponse<void>* /* out */) {
-        return env_->CallIntMethod(self_, app_fuse_flush_file_handle, file_handle_to_jlong(in->fh));
-    }
-
-    template <typename T, typename S>
-    void invoke_handler(int fd,
-                        FuseRequest* request,
-                        int (AppFuse::*handler)(const fuse_in_header&,
-                                                const T*,
-                                                FuseResponse<S>*)) {
-        FuseResponse<S> response(request->data());
-        const int reply_code = (this->*handler)(
-                request->header(),
-                static_cast<const T*>(request->data()),
-                &response);
-        fuse_reply(
-                fd,
-                request->header().unique,
-                reply_code,
-                request->data(),
-                response.size());
-    }
-
-    int64_t get_file_size(int inode) {
-        return static_cast<int64_t>(env_->CallLongMethod(
-                self_,
-                app_fuse_get_file_size,
-                static_cast<int>(inode)));
-    }
-
-    int64_t get_object_bytes(
-            int inode,
-            uint64_t offset,
-            uint32_t size,
-            void* buf) {
-        const jlong read_size = env_->CallLongMethod(
-                self_,
-                app_fuse_read_object_bytes,
-                static_cast<jint>(inode),
-                static_cast<jlong>(offset),
-                static_cast<jlong>(size));
-        if (read_size <= 0) {
-            return read_size;
-        }
-        ScopedLocalRef<jbyteArray> array(
-                env_, static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer)));
-        if (array.get() == nullptr) {
-            return -EFAULT;
-        }
-        ScopedByteArrayRO bytes(env_, array.get());
-        if (bytes.get() == nullptr) {
-            return -ENOMEM;
-        }
-        memcpy(buf, bytes.get(), read_size);
-        return read_size;
-    }
-
-    int write_object_bytes(uint64_t handle, int inode, uint64_t offset, uint32_t size,
-                           const void* buffer, uint32_t* written_size) {
-        static_assert(sizeof(uint64_t) <= sizeof(jlong),
-                      "jlong must be able to express any uint64_t values");
-        ScopedLocalRef<jbyteArray> array(
-                env_,
-                static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer)));
-        {
-            ScopedByteArrayRW bytes(env_, array.get());
-            if (bytes.get() == nullptr) {
-                return -EIO;
-            }
-            memcpy(bytes.get(), buffer, size);
-        }
-        const int result = env_->CallIntMethod(
-                self_,
-                app_fuse_write_object_bytes,
-                file_handle_to_jlong(handle),
-                inode,
-                offset,
-                size,
-                array.get());
-        if (result < 0) {
-            return result;
-        }
-        *written_size = result;
-        return 0;
-    }
-
-    static jlong file_handle_to_jlong(uint64_t handle) {
-        static_assert(
-                sizeof(uint64_t) <= sizeof(jlong),
-                "jlong must be able to express any uint64_t values");
-        return static_cast<jlong>(handle);
-    }
-
-    static void fuse_reply(int fd, int unique, int reply_code, void* reply_data,
-                           size_t reply_size) {
-        // Don't send any data for error case.
-        if (reply_code != 0) {
-            reply_size = 0;
-        }
-
-        struct fuse_out_header hdr;
-        hdr.len = reply_size + sizeof(hdr);
-        hdr.error = reply_code;
-        hdr.unique = unique;
-
-        struct iovec vec[2];
-        vec[0].iov_base = &hdr;
-        vec[0].iov_len = sizeof(hdr);
-        vec[1].iov_base = reply_data;
-        vec[1].iov_len = reply_size;
-
-        const int res = writev(fd, vec, reply_size != 0 ? 2 : 1);
-        if (res < 0) {
-            ALOGE("*** REPLY FAILED *** %d\n", errno);
-        }
-    }
-};
-
-void com_android_mtp_AppFuse_start_app_fuse_loop(JNIEnv* env, jobject self, jint jfd) {
-    ScopedFd fd(static_cast<int>(jfd));
-    AppFuse appfuse(env, self);
-
-    ALOGV("Start fuse loop.");
-    while (true) {
-        FuseRequest request;
-
-        const ssize_t result = TEMP_FAILURE_RETRY(
-                read(fd, request.buffer, sizeof(request.buffer)));
-        if (result < 0) {
-            if (errno == ENODEV) {
-                ALOGV("AppFuse was unmounted.\n");
-                return;
-            }
-            ALOGE("Failed to read bytes from FD: errno=%d\n", errno);
-            continue;
-        }
-
-        const size_t length = static_cast<size_t>(result);
-        if (length < sizeof(struct fuse_in_header)) {
-            ALOGE("request too short: len=%zu\n", length);
-            continue;
-        }
-
-        if (request.header().len != length) {
-            ALOGE("malformed header: len=%zu, hdr->len=%u\n",
-                  length, request.header().len);
-            continue;
-        }
-
-        appfuse.handle_fuse_request(fd, &request);
-    }
-}
-
-static const JNINativeMethod gMethods[] = {
-    {
-        "native_start_app_fuse_loop",
-        "(I)V",
-        (void *) com_android_mtp_AppFuse_start_app_fuse_loop
-    }
-};
-
-}
-
-jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
-    JNIEnv* env = nullptr;
-    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
-        ALOGE("ERROR: GetEnv failed\n");
-        return -1;
-
-    }
-    assert(env != nullptr);
-
-    jclass clazz = env->FindClass("com/android/mtp/AppFuse");
-    if (clazz == nullptr) {
-        ALOGE("Can't find com/android/mtp/AppFuse");
-        return -1;
-    }
-
-    app_fuse_class = static_cast<jclass>(env->NewGlobalRef(clazz));
-    if (app_fuse_class == nullptr) {
-        ALOGE("Can't obtain global reference for com/android/mtp/AppFuse");
-        return -1;
-    }
-
-    app_fuse_get_file_size = env->GetMethodID(
-            app_fuse_class, "getFileSize", "(I)J");
-    if (app_fuse_get_file_size == nullptr) {
-        ALOGE("Can't find getFileSize");
-        return -1;
-    }
-
-    app_fuse_read_object_bytes = env->GetMethodID(
-            app_fuse_class, "readObjectBytes", "(IJJ)J");
-    if (app_fuse_read_object_bytes == nullptr) {
-        ALOGE("Can't find readObjectBytes");
-        return -1;
-    }
-
-    app_fuse_write_object_bytes = env->GetMethodID(app_fuse_class, "writeObjectBytes", "(JIJI[B)I");
-    if (app_fuse_write_object_bytes == nullptr) {
-        ALOGE("Can't find writeObjectBytes");
-        return -1;
-    }
-
-    app_fuse_flush_file_handle = env->GetMethodID(app_fuse_class, "flushFileHandle", "(J)I");
-    if (app_fuse_flush_file_handle == nullptr) {
-        ALOGE("Can't find flushFileHandle");
-        return -1;
-    }
-
-    app_fuse_close_file_handle = env->GetMethodID(app_fuse_class, "closeFileHandle", "(J)I");
-    if (app_fuse_close_file_handle == nullptr) {
-        ALOGE("Can't find closeFileHandle");
-        return -1;
-    }
-
-    app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B");
-    if (app_fuse_buffer == nullptr) {
-        ALOGE("Can't find mBuffer");
-        return -1;
-    }
-
-    const jfieldID read_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_READ", "I");
-    if (static_cast<int>(env->GetStaticIntField(app_fuse_class, read_max_fied)) != MAX_READ) {
-        return -1;
-    }
-
-    const jfieldID write_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_WRITE", "I");
-    if (static_cast<int>(env->GetStaticIntField(app_fuse_class, write_max_fied)) != MAX_WRITE) {
-        return -1;
-    }
-
-    const int result = android::AndroidRuntime::registerNativeMethods(
-            env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods));
-    if (result < 0) {
-        return -1;
-    }
-
-    return JNI_VERSION_1_4;
-}
diff --git a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
index 0762571..36f6fe9 100644
--- a/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
+++ b/packages/MtpDocumentsProvider/perf_tests/src/com/android/mtp/AppFusePerfTest.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
 import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -36,38 +37,13 @@
 
 @RunWith(JUnit4.class)
 public class AppFusePerfTest {
+    final static int SIZE = 10 * 1024 * 1024;  // 10MB
+
     @Test
     @LargeTest
     public void testReadWriteFile() throws IOException {
         final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         final StorageManager storageManager = context.getSystemService(StorageManager.class);
-        final int INODE = 10;
-        final int SIZE = 10 * 1024 * 1024;  // 10MB
-        final AppFuse appFuse = new AppFuse(
-                "test",
-                new TestCallback() {
-                    @Override
-                    public long getFileSize(int inode) throws FileNotFoundException {
-                        if (inode != INODE) {
-                            throw new FileNotFoundException();
-                        }
-                        return SIZE;
-                    }
-
-                    @Override
-                    public long readObjectBytes(int inode, long offset, long size, byte[] bytes)
-                            throws IOException {
-                        return size;
-                    }
-
-                    @Override
-                    public int writeObjectBytes(
-                            long fileHandle, int inode, long offset, int size, byte[] bytes) {
-                        return size;
-                    }
-                });
-
-        appFuse.mount(storageManager);
 
         final byte[] bytes = new byte[SIZE];
         final int SAMPLES = 100;
@@ -75,22 +51,20 @@
         final double[] writeTime = new double[SAMPLES];
 
         for (int i = 0; i < SAMPLES; i++) {
-            final ParcelFileDescriptor fd = appFuse.openFile(
-                    INODE,
-                    ParcelFileDescriptor.MODE_READ_ONLY);
+            final ParcelFileDescriptor fd = storageManager.openProxyFileDescriptor(
+                    ParcelFileDescriptor.MODE_READ_ONLY, new TestCallback());
             try (final ParcelFileDescriptor.AutoCloseInputStream stream =
                     new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
                 final long startTime = System.nanoTime();
                 stream.read(bytes);
                 readTime[i] = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
             }
-
         }
 
         for (int i = 0; i < SAMPLES; i++) {
-            final ParcelFileDescriptor fd = appFuse.openFile(
-                    INODE,
-                    ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
+            final ParcelFileDescriptor fd = storageManager.openProxyFileDescriptor(
+                    ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE,
+                    new TestCallback());
             try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
                     new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
                 final long startTime = System.nanoTime();
@@ -99,8 +73,6 @@
             }
         }
 
-        appFuse.close();
-
         double readAverage = 0;
         double writeAverage = 0;
         double readSquaredAverage = 0;
@@ -127,28 +99,26 @@
         InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, results);
     }
 
-    private static class TestCallback implements AppFuse.Callback {
+    private static class TestCallback extends ProxyFileDescriptorCallback {
         @Override
-        public long getFileSize(int inode) throws FileNotFoundException {
-            throw new FileNotFoundException();
+        public long onGetSize() throws ErrnoException {
+            return SIZE;
         }
 
         @Override
-        public long readObjectBytes(int inode, long offset, long size, byte[] bytes)
-                throws IOException {
-            throw new IOException();
+        public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+            return size;
         }
 
         @Override
-        public int writeObjectBytes(long fileHandle, int inode, long offset, int size, byte[] bytes)
-                throws IOException {
-            throw new IOException();
+        public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+            return size;
         }
 
         @Override
-        public void flushFileHandle(long fileHandle) throws IOException {}
+        public void onFsync() throws ErrnoException {}
 
         @Override
-        public void closeFileHandle(long fileHandle) {}
+        public void onRelease() {}
     }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
deleted file mode 100644
index cd78e61..0000000
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mtp;
-
-import android.annotation.WorkerThread;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.storage.StorageManager;
-import android.system.ErrnoException;
-import android.system.OsConstants;
-import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.mtp.annotations.UsedByNative;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-public class AppFuse {
-    static {
-        System.loadLibrary("appfuse_jni");
-    }
-
-    private static final boolean DEBUG = false;
-
-    /**
-     * Max read amount specified at the FUSE kernel implementation.
-     * The value is copied from sdcard.c.
-     */
-    @UsedByNative("com_android_mtp_AppFuse.cpp")
-    static final int MAX_READ = 128 * 1024;
-
-    @UsedByNative("com_android_mtp_AppFuse.cpp")
-    static final int MAX_WRITE = 256 * 1024;
-
-    private final String mName;
-    private final Callback mCallback;
-
-    /**
-     * Buffer for read bytes request.
-     * Don't use the buffer from the out of AppFuseMessageThread.
-     */
-    private byte[] mBuffer = new byte[Math.max(MAX_READ, MAX_WRITE)];
-
-    private Thread mMessageThread;
-    private ParcelFileDescriptor mDeviceFd;
-
-    AppFuse(String name, Callback callback) {
-        mName = name;
-        mCallback = callback;
-    }
-
-    void mount(StorageManager storageManager) throws IOException {
-        Preconditions.checkState(mDeviceFd == null);
-        mDeviceFd = storageManager.mountAppFuse(mName);
-        mMessageThread = new AppFuseMessageThread(mDeviceFd.dup().detachFd());
-        mMessageThread.start();
-    }
-
-    @VisibleForTesting
-    void close() {
-        try {
-            // Remote side of ParcelFileDescriptor is tracking the close of mDeviceFd, and unmount
-            // the corresponding fuse file system. The mMessageThread will receive ENODEV, and
-            // then terminate itself.
-            mDeviceFd.close();
-            mMessageThread.join();
-        } catch (IOException exp) {
-            Log.e(MtpDocumentsProvider.TAG, "Failed to close device FD.", exp);
-        } catch (InterruptedException exp) {
-            Log.e(MtpDocumentsProvider.TAG, "Failed to terminate message thread.", exp);
-        }
-    }
-
-    /**
-     * Opens a file on app fuse and returns ParcelFileDescriptor.
-     *
-     * @param i ID for opened file.
-     * @param mode Mode for opening file.
-     * @see ParcelFileDescriptor#MODE_READ_ONLY
-     * @see ParcelFileDescriptor#MODE_WRITE_ONLY
-     */
-    public ParcelFileDescriptor openFile(int i, int mode) throws FileNotFoundException {
-        Preconditions.checkArgument(
-                mode == ParcelFileDescriptor.MODE_READ_ONLY ||
-                mode == (ParcelFileDescriptor.MODE_WRITE_ONLY |
-                         ParcelFileDescriptor.MODE_TRUNCATE));
-        return ParcelFileDescriptor.open(new File(
-                getMountPoint(),
-                Integer.toString(i)),
-                mode);
-    }
-
-    File getMountPoint() {
-        return new File("/mnt/appfuse/" + Process.myUid() + "_" + mName);
-    }
-
-    static interface Callback {
-        /**
-         * Returns file size for the given inode.
-         * @param inode
-         * @return File size. Must not be negative.
-         * @throws FileNotFoundException
-         */
-        long getFileSize(int inode) throws FileNotFoundException;
-
-        /**
-         * Returns file bytes for the give inode.
-         * @param inode
-         * @param offset Offset for file bytes.
-         * @param size Size for file bytes.
-         * @param bytes Buffer to store file bytes.
-         * @return Number of read bytes. Must not be negative.
-         * @throws IOException
-         */
-        long readObjectBytes(int inode, long offset, long size, byte[] bytes) throws IOException;
-
-        /**
-         * Handles writing bytes for the give inode.
-         * @param fileHandle
-         * @param inode
-         * @param offset Offset for file bytes.
-         * @param size Size for file bytes.
-         * @param bytes Buffer to store file bytes.
-         * @return Number of read bytes. Must not be negative.
-         * @throws IOException
-         */
-        int writeObjectBytes(long fileHandle, int inode, long offset, int size, byte[] bytes)
-                throws IOException, ErrnoException;
-
-        /**
-         * Flushes bytes for file handle.
-         * @param fileHandle
-         * @throws IOException
-         * @throws ErrnoException
-         */
-        void flushFileHandle(long fileHandle) throws IOException, ErrnoException;
-
-        /**
-         * Closes file handle.
-         * @param fileHandle
-         * @throws IOException
-         */
-        void closeFileHandle(long fileHandle) throws IOException, ErrnoException;
-    }
-
-    @UsedByNative("com_android_mtp_AppFuse.cpp")
-    @WorkerThread
-    private long getFileSize(int inode) {
-        try {
-            return mCallback.getFileSize(inode);
-        } catch (Exception error) {
-            return -getErrnoFromException(error);
-        }
-    }
-
-    @UsedByNative("com_android_mtp_AppFuse.cpp")
-    @WorkerThread
-    private long readObjectBytes(int inode, long offset, long size) {
-        if (offset < 0 || size < 0 || size > MAX_READ) {
-            return -OsConstants.EINVAL;
-        }
-        try {
-            // It's OK to share the same mBuffer among requests because the requests are processed
-            // by AppFuseMessageThread sequentially.
-            return mCallback.readObjectBytes(inode, offset, size, mBuffer);
-        } catch (Exception error) {
-            return -getErrnoFromException(error);
-        }
-    }
-
-    @UsedByNative("com_android_mtp_AppFuse.cpp")
-    @WorkerThread
-    private /* unsgined */ int writeObjectBytes(long fileHandler,
-                                                int inode,
-                                                /* unsigned */ long offset,
-                                                /* unsigned */ int size,
-                                                byte[] bytes) {
-        try {
-            return mCallback.writeObjectBytes(fileHandler, inode, offset, size, bytes);
-        } catch (Exception error) {
-            return -getErrnoFromException(error);
-        }
-    }
-
-    @UsedByNative("com_android_mtp_AppFuse.cpp")
-    @WorkerThread
-    private int flushFileHandle(long fileHandle) {
-        try {
-            mCallback.flushFileHandle(fileHandle);
-            return 0;
-        } catch (Exception error) {
-            return -getErrnoFromException(error);
-        }
-    }
-
-    @UsedByNative("com_android_mtp_AppFuse.cpp")
-    @WorkerThread
-    private int closeFileHandle(long fileHandle) {
-        try {
-            mCallback.closeFileHandle(fileHandle);
-            return 0;
-        } catch (Exception error) {
-            return -getErrnoFromException(error);
-        }
-    }
-
-    private static int getErrnoFromException(Exception error) {
-        if (DEBUG) {
-            Log.e(MtpDocumentsProvider.TAG, "AppFuse callbacks", error);
-        }
-        if (error instanceof FileNotFoundException) {
-            return OsConstants.ENOENT;
-        } else if (error instanceof IOException) {
-            return OsConstants.EIO;
-        } else if (error instanceof UnsupportedOperationException) {
-            return OsConstants.ENOTSUP;
-        } else if (error instanceof IllegalArgumentException) {
-            return OsConstants.EINVAL;
-        } else {
-            return OsConstants.EIO;
-        }
-    }
-
-    private native void native_start_app_fuse_loop(int fd);
-
-    private class AppFuseMessageThread extends Thread {
-        /**
-         * File descriptor used by native loop.
-         * It's owned by native loop and does not need to close here.
-         */
-        private final int mRawFd;
-
-        AppFuseMessageThread(int fd) {
-            super("AppFuseMessageThread");
-            mRawFd = fd;
-        }
-
-        @Override
-        public void run() {
-            native_start_app_fuse_loop(mRawFd);
-        }
-    }
-}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 6b2c1ee..8b0e610 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -33,6 +33,7 @@
 import android.os.CancellationSignal;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
 import android.os.storage.StorageManager;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Path;
@@ -41,6 +42,7 @@
 import android.provider.DocumentsProvider;
 import android.provider.Settings;
 import android.system.ErrnoException;
+import android.system.OsConstants;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -54,6 +56,8 @@
 import java.util.Map;
 import java.util.concurrent.TimeoutException;
 
+import libcore.io.IoUtils;
+
 /**
  * DocumentsProvider for MTP devices.
  */
@@ -84,9 +88,9 @@
     private RootScanner mRootScanner;
     private Resources mResources;
     private MtpDatabase mDatabase;
-    private AppFuse mAppFuse;
     private ServiceIntentSender mIntentSender;
     private Context mContext;
+    private StorageManager mStorageManager;
 
     /**
      * Provides singleton instance to MtpDocumentsService.
@@ -105,8 +109,8 @@
         mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
         mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
         mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
-        mAppFuse = new AppFuse(TAG, new AppFuseCallback());
         mIntentSender = new ServiceIntentSender(getContext());
+        mStorageManager = getContext().getSystemService(StorageManager.class);
 
         // Check boot count and cleans database if it's first time to launch MtpDocumentsProvider
         // after booting.
@@ -129,14 +133,6 @@
             return false;
         }
 
-        // TODO: Mount AppFuse on demands.
-        try {
-            mAppFuse.mount(getContext().getSystemService(StorageManager.class));
-        } catch (IOException error) {
-            Log.e(TAG, "Failed to start app fuse.", error);
-            return false;
-        }
-
         resume();
         return true;
     }
@@ -157,16 +153,9 @@
         mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
         mDatabase = database;
         mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
-        mAppFuse = new AppFuse(TAG, new AppFuseCallback());
         mIntentSender = intentSender;
+        mStorageManager = storageManager;
 
-        // TODO: Mount AppFuse on demands.
-        try {
-            mAppFuse.mount(storageManager);
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to start app fuse.", e);
-            return false;
-        }
         resume();
         return true;
     }
@@ -252,7 +241,10 @@
                 }
                 if (MtpDeviceRecord.isPartialReadSupported(
                         device.operationsSupported, fileSize)) {
-                    return mAppFuse.openFile(Integer.parseInt(documentId), modeFlag);
+
+                    return mStorageManager.openProxyFileDescriptor(
+                            modeFlag,
+                            new MtpProxyFileDescriptorCallback(Integer.parseInt(documentId)));
                 } else {
                     // If getPartialObject{|64} are not supported for the device, returns
                     // non-seekable pipe FD instead.
@@ -262,7 +254,9 @@
                 // TODO: Clear the parent document loader task (if exists) and call notify
                 // when writing is completed.
                 if (MtpDeviceRecord.isWritingSupported(device.operationsSupported)) {
-                    return mAppFuse.openFile(Integer.parseInt(documentId), modeFlag);
+                    return mStorageManager.openProxyFileDescriptor(
+                            modeFlag,
+                            new MtpProxyFileDescriptorCallback(Integer.parseInt(documentId)));
                 } else {
                     throw new UnsupportedOperationException(
                             "The device does not support writing operation.");
@@ -586,7 +580,6 @@
                 throw new RuntimeException(e);
             } finally {
                 mDatabase.close();
-                mAppFuse.close();
                 super.shutdown();
             }
         }
@@ -693,72 +686,92 @@
         }
     }
 
-    private class AppFuseCallback implements AppFuse.Callback {
-        private final Map<Long, MtpFileWriter> mWriters = new HashMap<>();
+    private class MtpProxyFileDescriptorCallback extends ProxyFileDescriptorCallback {
+        private final int mInode;
+        private MtpFileWriter mWriter;
 
-        @Override
-        public long getFileSize(int inode) throws FileNotFoundException {
-            return MtpDocumentsProvider.this.getFileSize(String.valueOf(inode));
+        MtpProxyFileDescriptorCallback(int inode) {
+            mInode = inode;
         }
 
         @Override
-        public long readObjectBytes(
-                int inode, long offset, long size, byte[] buffer) throws IOException {
-            final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode));
-            final MtpDeviceRecord record = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord;
-
-            if (MtpDeviceRecord.isSupported(
-                    record.operationsSupported, MtpConstants.OPERATION_GET_PARTIAL_OBJECT_64)) {
-                return mMtpManager.getPartialObject64(
-                        identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
-            }
-
-            if (0 <= offset && offset <= 0xffffffffL && MtpDeviceRecord.isSupported(
-                    record.operationsSupported, MtpConstants.OPERATION_GET_PARTIAL_OBJECT)) {
-                return mMtpManager.getPartialObject(
-                        identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
-            }
-
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public int writeObjectBytes(
-                long fileHandle, int inode, long offset, int size, byte[] bytes)
-                throws IOException, ErrnoException {
-            final MtpFileWriter writer;
-            if (mWriters.containsKey(fileHandle)) {
-                writer = mWriters.get(fileHandle);
-            } else {
-                writer = new MtpFileWriter(mContext, String.valueOf(inode));
-                mWriters.put(fileHandle, writer);
-            }
-            return writer.write(offset, size, bytes);
-        }
-
-        @Override
-        public void flushFileHandle(long fileHandle) throws IOException, ErrnoException {
-            final MtpFileWriter writer = mWriters.get(fileHandle);
-            if (writer == null) {
-                // File handle for reading.
-                return;
-            }
-            final MtpDeviceRecord device = getDeviceToolkit(
-                    mDatabase.createIdentifier(writer.getDocumentId()).mDeviceId).mDeviceRecord;
-            writer.flush(mMtpManager, mDatabase, device.operationsSupported);
-        }
-
-        @Override
-        public void closeFileHandle(long fileHandle) throws IOException, ErrnoException {
-            final MtpFileWriter writer = mWriters.get(fileHandle);
-            if (writer == null) {
-                // File handle for reading.
-                return;
-            }
+        public long onGetSize() throws ErrnoException {
             try {
-                writer.close();
+                return getFileSize(String.valueOf(mInode));
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, e.getMessage(), e);
+                throw new ErrnoException("onGetSize", OsConstants.ENOENT);
+            }
+        }
+
+        @Override
+        public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+            try {
+                final Identifier identifier = mDatabase.createIdentifier(Integer.toString(mInode));
+                final MtpDeviceRecord record = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord;
+                if (MtpDeviceRecord.isSupported(
+                        record.operationsSupported, MtpConstants.OPERATION_GET_PARTIAL_OBJECT_64)) {
+
+                        return (int) mMtpManager.getPartialObject64(
+                                identifier.mDeviceId, identifier.mObjectHandle, offset, size, data);
+
+                }
+                if (0 <= offset && offset <= 0xffffffffL && MtpDeviceRecord.isSupported(
+                        record.operationsSupported, MtpConstants.OPERATION_GET_PARTIAL_OBJECT)) {
+                    return (int) mMtpManager.getPartialObject(
+                            identifier.mDeviceId, identifier.mObjectHandle, offset, size, data);
+                }
+                throw new ErrnoException("onRead", OsConstants.ENOTSUP);
+            } catch (IOException e) {
+                Log.e(TAG, e.getMessage(), e);
+                throw new ErrnoException("onRead", OsConstants.EIO);
+            }
+        }
+
+        @Override
+        public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+            try {
+                if (mWriter == null) {
+                    mWriter = new MtpFileWriter(mContext, String.valueOf(mInode));
+                }
+                return mWriter.write(offset, size, data);
+            } catch (IOException e) {
+                Log.e(TAG, e.getMessage(), e);
+                throw new ErrnoException("onWrite", OsConstants.EIO);
+            }
+        }
+
+        @Override
+        public void onFsync() throws ErrnoException {
+            tryFsync();
+        }
+
+        @Override
+        public void onRelease() {
+            try {
+                tryFsync();
+            } catch (ErrnoException error) {
+                // Cannot recover from the error at onRelease. Client app should use fsync to
+                // ensure the provider writes data correctly.
+                Log.e(TAG, "Cannot recover from the error at onRelease.", error);
             } finally {
-                mWriters.remove(fileHandle);
+                if (mWriter != null) {
+                    IoUtils.closeQuietly(mWriter);
+                }
+            }
+        }
+
+        private void tryFsync() throws ErrnoException {
+            try {
+                if (mWriter != null) {
+                    final MtpDeviceRecord device =
+                            getDeviceToolkit(mDatabase.createIdentifier(
+                                    mWriter.getDocumentId()).mDeviceId).mDeviceRecord;
+                    mWriter.flush(mMtpManager, mDatabase, device.operationsSupported);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, e.getMessage(), e);
+                throw new ErrnoException("onWrite", OsConstants.EIO);
             }
         }
     }
diff --git a/packages/MtpDocumentsProvider/tests/Android.mk b/packages/MtpDocumentsProvider/tests/Android.mk
index 8538379..e50d6fb 100644
--- a/packages/MtpDocumentsProvider/tests/Android.mk
+++ b/packages/MtpDocumentsProvider/tests/Android.mk
@@ -4,6 +4,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 LOCAL_PACKAGE_NAME := MtpDocumentsProviderTests
 LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
 LOCAL_CERTIFICATE := media
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
deleted file mode 100644
index e421de7..0000000
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mtp;
-
-import android.os.ParcelFileDescriptor;
-import android.os.storage.StorageManager;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-
-@MediumTest
-public class AppFuseTest extends AndroidTestCase {
-    public void testMount() throws ErrnoException, IOException {
-        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final AppFuse appFuse = new AppFuse("test", new TestCallback());
-        appFuse.mount(storageManager);
-        final File file = appFuse.getMountPoint();
-        assertTrue(file.isDirectory());
-        assertEquals(1, Os.stat(file.getPath()).st_ino);
-        appFuse.close();
-        assertTrue(1 != Os.stat(file.getPath()).st_ino);
-    }
-
-    public void testOpenFile() throws IOException {
-        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final int INODE = 10;
-        final AppFuse appFuse = new AppFuse(
-                "test",
-                new TestCallback() {
-                    @Override
-                    public long getFileSize(int inode) throws FileNotFoundException {
-                        if (INODE == inode) {
-                            return 1024;
-                        }
-                        throw new FileNotFoundException();
-                    }
-                });
-        appFuse.mount(storageManager);
-        final ParcelFileDescriptor fd = appFuse.openFile(
-                INODE, ParcelFileDescriptor.MODE_READ_ONLY);
-        fd.close();
-        appFuse.close();
-    }
-
-    public void testOpenFile_fileNotFound() throws IOException {
-        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final int INODE = 10;
-        final AppFuse appFuse = new AppFuse("test", new TestCallback());
-        appFuse.mount(storageManager);
-        try {
-            appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_ONLY);
-            fail();
-        } catch (FileNotFoundException exp) {}
-        appFuse.close();
-    }
-
-    public void testOpenFile_illegalMode() throws IOException {
-        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final int INODE = 10;
-        final AppFuse appFuse = new AppFuse("test", new TestCallback());
-        appFuse.mount(storageManager);
-        try {
-            appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_WRITE);
-            fail();
-        } catch (IllegalArgumentException exp) {}
-        appFuse.close();
-    }
-
-    public void testReadFile() throws IOException {
-        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final int fileInode = 10;
-        final byte[] fileBytes = new byte[] { 'a', 'b', 'c', 'd', 'e' };
-        final AppFuse appFuse = new AppFuse(
-                "test",
-                new TestCallback() {
-                    @Override
-                    public long getFileSize(int inode) throws FileNotFoundException {
-                        if (inode == fileInode) {
-                            return fileBytes.length;
-                        }
-                        return super.getFileSize(inode);
-                    }
-
-                    @Override
-                    public long readObjectBytes(int inode, long offset, long size, byte[] bytes)
-                            throws IOException {
-                        if (inode == fileInode) {
-                            int i = 0;
-                            while (i < size && i + offset < fileBytes.length)  {
-                                bytes[i] = fileBytes[(int) (i + offset)];
-                                i++;
-                            }
-                            return i;
-                        }
-                        return super.readObjectBytes(inode, offset, size, bytes);
-                    }
-                });
-        appFuse.mount(storageManager);
-        final ParcelFileDescriptor fd = appFuse.openFile(
-                fileInode, ParcelFileDescriptor.MODE_READ_ONLY);
-        try (final ParcelFileDescriptor.AutoCloseInputStream stream =
-                new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
-            final byte[] buffer = new byte[1024];
-            final int size = stream.read(buffer, 0, buffer.length);
-            assertEquals(5, size);
-        }
-        appFuse.close();
-    }
-
-    public void testWriteFile() throws IOException {
-        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final int INODE = 10;
-        final byte[] resultBytes = new byte[5];
-        final AppFuse appFuse = new AppFuse(
-                "test",
-                new TestCallback() {
-                    @Override
-                    public long getFileSize(int inode) throws FileNotFoundException {
-                        if (inode != INODE) {
-                            throw new FileNotFoundException();
-                        }
-                        return resultBytes.length;
-                    }
-
-                    @Override
-                    public int writeObjectBytes(
-                            long fileHandle, int inode, long offset, int size, byte[] bytes) {
-                        for (int i = 0; i < size; i++) {
-                            resultBytes[(int)(offset + i)] = bytes[i];
-                        }
-                        return size;
-                    }
-                });
-        appFuse.mount(storageManager);
-        final ParcelFileDescriptor fd = appFuse.openFile(
-                INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
-        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
-                new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
-            stream.write('a');
-            stream.write('b');
-            stream.write('c');
-            stream.write('d');
-            stream.write('e');
-        }
-        final byte[] BYTES = new byte[] { 'a', 'b', 'c', 'd', 'e' };
-        assertTrue(Arrays.equals(BYTES, resultBytes));
-        appFuse.close();
-    }
-
-    public void testWriteFile_writeError() throws IOException {
-        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final int INODE = 10;
-        final AppFuse appFuse = new AppFuse(
-                "test",
-                new TestCallback() {
-                    @Override
-                    public long getFileSize(int inode) throws FileNotFoundException {
-                        if (inode != INODE) {
-                            throw new FileNotFoundException();
-                        }
-                        return 5;
-                    }
-                });
-        appFuse.mount(storageManager);
-        final ParcelFileDescriptor fd = appFuse.openFile(
-                INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
-        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
-                new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
-            stream.write('a');
-            fail();
-        } catch (IOException e) {
-        }
-        appFuse.close();
-    }
-
-    public void testWriteFile_flushError() throws IOException {
-        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final int INODE = 10;
-        final AppFuse appFuse = new AppFuse(
-                "test",
-                new TestCallback() {
-                    @Override
-                    public long getFileSize(int inode) throws FileNotFoundException {
-                        if (inode != INODE) {
-                            throw new FileNotFoundException();
-                        }
-                        return 5;
-                    }
-
-                    @Override
-                    public int writeObjectBytes(
-                            long fileHandle, int inode, long offset, int size, byte[] bytes) {
-                        return size;
-                    }
-
-                    @Override
-                    public void flushFileHandle(long fileHandle) throws IOException {
-                        throw new IOException();
-                    }
-                });
-        appFuse.mount(storageManager);
-        final ParcelFileDescriptor fd = appFuse.openFile(
-                INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
-        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
-                new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
-            stream.write('a');
-            try {
-                IoUtils.close(fd.getFileDescriptor());
-                fail();
-            } catch (IOException e) {
-            }
-        }
-        appFuse.close();
-    }
-
-    private static class TestCallback implements AppFuse.Callback {
-        @Override
-        public long getFileSize(int inode) throws FileNotFoundException {
-            throw new FileNotFoundException();
-        }
-
-        @Override
-        public long readObjectBytes(int inode, long offset, long size, byte[] bytes)
-                throws IOException {
-            throw new IOException();
-        }
-
-        @Override
-        public int writeObjectBytes(long fileHandle, int inode, long offset, int size, byte[] bytes)
-                throws IOException {
-            throw new IOException();
-        }
-
-        @Override
-        public void flushFileHandle(long fileHandle) throws IOException {}
-
-        @Override
-        public void closeFileHandle(long fileHandle) {}
-    }
-}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index a9d35e1..491e24d 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -557,6 +557,7 @@
             try (ParcelFileDescriptor.AutoCloseOutputStream stream =
                     new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
                 stream.write("Hello".getBytes());
+                fd.getFileDescriptor().sync();
             }
         }
         {
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index d108c20..f36167e 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -26,10 +26,10 @@
     <string name="label_color" msgid="1108690305218188969">"रंग"</string>
     <string name="label_duplex" msgid="5370037254347072243">"दो-तरफ़ा"</string>
     <string name="label_orientation" msgid="2853142581990496477">"अभिविन्‍यास"</string>
-    <string name="label_pages" msgid="7768589729282182230">"पृष्ठ"</string>
+    <string name="label_pages" msgid="7768589729282182230">"पेज"</string>
     <string name="destination_default_text" msgid="5422708056807065710">"कोई प्रिंटर चुनें"</string>
     <string name="template_all_pages" msgid="3322235982020148762">"सभी <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
-    <string name="template_page_range" msgid="428638530038286328">"पृष्ठ संख्या <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+    <string name="template_page_range" msgid="428638530038286328">"पेज संख्या <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
     <string name="pages_range_example" msgid="8558694453556945172">"उदा. 1—5,8,11—13"</string>
     <string name="print_preview" msgid="8010217796057763343">"प्रिंट पूर्वावलोकन"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"पूर्वावलोकन के लिए PDF व्यूअर इंस्टॉल करें"</string>
@@ -39,7 +39,7 @@
     <string name="all_printers" msgid="5018829726861876202">"सभी प्रिंटर..."</string>
     <string name="print_dialog" msgid="32628687461331979">"प्रिंट डॉयलॉग"</string>
     <string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
-    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g> में से पृष्ठ <xliff:g id="CURRENT_PAGE">%1$d</xliff:g>"</string>
+    <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g> में से पेज <xliff:g id="CURRENT_PAGE">%1$d</xliff:g>"</string>
     <string name="summary_template" msgid="8899734908625669193">"सारांश, प्रतियां <xliff:g id="COPIES">%1$s</xliff:g>, काग़ज़ का आकार <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
     <string name="expand_handle" msgid="7282974448109280522">"हैंडल विस्तृत करें"</string>
     <string name="collapse_handle" msgid="6886637989442507451">"हैंडल संक्षिप्त करें"</string>
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index ad07283..67ef40a 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -12,6 +12,8 @@
     android-support-v7-appcompat \
     android-support-v14-preference
 
+LOCAL_STATIC_JAVA_LIBRARY := legacy-android-test
+
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
 LOCAL_JAR_EXCLUDE_FILES := none
@@ -21,4 +23,4 @@
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # For the test package.
-include $(call all-makefiles-under, $(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml b/packages/SettingsLib/res/layout/access_point_friction_widget.xml
similarity index 62%
copy from packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
copy to packages/SettingsLib/res/layout/access_point_friction_widget.xml
index 85573e0..7409686 100644
--- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
+++ b/packages/SettingsLib/res/layout/access_point_friction_widget.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,11 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
+
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/friction_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:contentDescription="@null" />
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
index 24a5422..b659cee 100644
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -26,12 +26,12 @@
         android:orientation="vertical"
         android:fitsSystemWindows="true">
         <FrameLayout
+            style="?android:attr/actionBarStyle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:theme="?android:attr/actionBarTheme">
             <Toolbar
                 android:id="@+id/action_bar"
-                style="?android:attr/actionBarStyle"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:navigationContentDescription="@*android:string/action_bar_up_description"/>
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index 4cb349c..b120ffe 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Gebruik HDCP-kontrolering net vir DRM-inhoud"</item>
     <item msgid="45075631231212732">"Gebruik altyd HDCP-kontrolering"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Verstek"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Verstek"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Verstek"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Verstek"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Verstek"</item>
-    <item msgid="5618929009984956469">"16 bis per voorbeeld"</item>
-    <item msgid="3412640499234627248">"24 bis per voorbeeld"</item>
-    <item msgid="121583001492929387">"32 bis per voorbeeld"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Verstek"</item>
-    <item msgid="4726688794884191540">"16 bis per voorbeeld"</item>
-    <item msgid="305344756485516870">"24 bis per voorbeeld"</item>
-    <item msgid="244568657919675099">"32 bis per voorbeeld"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Verstek"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Verstek"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Klankgehalte verkies (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Standaard (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Verbinding verkies (330 kbps/303 kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Klankgehalte verkies (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Standaard (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Verbinding verkies (330 kbps/303 kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Af"</item>
     <item msgid="1593289376502312923">"64 K"</item>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 3672fa9..a097b18 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Sellulêre data altyd aktief"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Deaktiveer absolute volume"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth-oudiokodek"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Kies Bluetooth-A2DP-kodek wat verkies word"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth-oudiovoorbeeldkoers"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Kies Bluetooth-A2DP-kodekvoorbeeldkoers wat verkies word"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth-oudiobisse per voorbeeld"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Kies die Bluetooth-A2DP-kodekbisse per voorbeeld wat verkies word"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth-oudiokanaalmodus"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Kies Bluetooth-A2DP-kodekkanaalmodus wat verkies word"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth-oudio-LDAC-speelgehalte"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Kies Bluetooth-A2DP-kodek-LDAC-speelgehalte wat verkies word"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Wys opsies vir draadlose skermsertifisering"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Verhoog Wi-Fi-aantekeningvlak, wys per SSID RSSI in Wi‑Fi-kieser"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Wanneer dit geaktiveer is, sal Wi-Fi meer aggressief wees om die dataverbinding na selfoon oor te dra wanneer die Wi-Fi-sein swak is"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Hulp en terugvoer"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Kieslys"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"MGT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Voer wagwoord in om fabriekterugstelling in demonstrasiemodus uit te voer"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Volgende"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Wagwoord word benodig"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
index 0b424b3..8746cc8 100644
--- a/packages/SettingsLib/res/values-am/arrays.xml
+++ b/packages/SettingsLib/res/values-am/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"ሁልጊዜ የHDCP ምልከታ ተጠቀም"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ነባሪ"</item>
+    <item msgid="7065842274271279580">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ነባሪ"</item>
+    <item msgid="5062108632402595000">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ነባሪ"</item>
+    <item msgid="3093023430402746802">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="8895532488906185219">"44.1 ኪኸ"</item>
     <item msgid="2909915718994807056">"48.0 ኪኸ"</item>
     <item msgid="3347287377354164611">"88.2 ኪኸ"</item>
     <item msgid="1234212100239985373">"96.0 ኪኸ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ነባሪ"</item>
+    <item msgid="3214516120190965356">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="4482862757811638365">"44.1 ኪኸ"</item>
     <item msgid="354495328188724404">"48.0 ኪኸ"</item>
     <item msgid="7329816882213695083">"88.2 ኪኸ"</item>
     <item msgid="6967397666254430476">"96.0 ኪኸ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ነባሪ"</item>
+    <item msgid="2684127272582591429">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="5618929009984956469">"16 ቢት/ናሙና"</item>
     <item msgid="3412640499234627248">"24 ቢት/ናሙና"</item>
     <item msgid="121583001492929387">"32 ቢት/ናሙና"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ነባሪ"</item>
+    <item msgid="1081159789834584363">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="4726688794884191540">"16 ቢት/ናሙና"</item>
     <item msgid="305344756485516870">"24 ቢት/ናሙና"</item>
     <item msgid="244568657919675099">"32 ቢት/ናሙና"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ነባሪ"</item>
+    <item msgid="5226878858503393706">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="4106832974775067314">"ሞኖ"</item>
     <item msgid="5571632958424639155">"ስቲሪዮ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ነባሪ"</item>
+    <item msgid="4118561796005528173">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
     <item msgid="8900559293912978337">"ሞኖ"</item>
     <item msgid="8883739882299884241">"ስቲሪዮ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"የሚመረጠው የድምጽ ጥራት (990ኪቢ/ሴ/909ኪቢ/ሴ)"</item>
-    <item msgid="138837449700903545">"መደበኛ (660ኪቢ/ሴ/606ኪቢ/ሴ)"</item>
-    <item msgid="4777177307869441982">"የሚመረጠው ግንኙነት (330ኪቢ/ሴ/303ኪቢ/ሴ)"</item>
+    <item msgid="3411577996960199959">"ለኦዲዮ ጥራት አትባ (990 ኪቢ/ሴ / 909 ኪቢ/ሴ)"</item>
+    <item msgid="2921767058740704969">"ለኦዲዮ ጥራት አትባ (660 ኪቢ/ሴ / 606 ኪቢ/ሴ)"</item>
+    <item msgid="3682554248829489641">"ለኦዲዮ ጥራት አትባ (330 ኪቢ/ሴ / 303 ኪቢ/ሴ)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"የሚመረጠው የድምጽ ጥራት (990ኪቢ/ሴ/909ኪቢ/ሴ)"</item>
-    <item msgid="9091111147684472529">"መደበኛ (660ኪቢ/ሴ/606ኪቢ/ሴ)"</item>
-    <item msgid="3367904477834831032">"የሚመረጠው ግንኙነት (330ኪቢ/ሴ/303ኪቢ/ሴ)"</item>
+    <item msgid="7668834469173465015">"የኦዲዮ ጥራት አትባ"</item>
+    <item msgid="4327143584633311908">"የተመጣጠነ የኦዲዮ እና ግንኙነት ጥራት"</item>
+    <item msgid="6155648878105378550">"ለግንኙነት ጥራት አትባ"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ጠፍቷል"</item>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 42783ec..7725827 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"የተንቀስቃሽ ስልክ ውሂብ ሁልጊዜ ንቁ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ፍጹማዊ ድምፅን አሰናክል"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"የብሉቱዝ ኦዲዮ ኮዴክ"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"የሚመረጠውን የብሉቱዝ A2DP ኮዴክ ምረጥ"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"የብሉቱዝ ኦዲዮ ኮዴክ ይምረጡ"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"የብሉቱዝ ኦዲዮ ናሙና ፍጥነት"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"የሚመረጠውን የብሉቱዝ A2DP ኮዴክ ናሙና ፍጥነት ምረጥ"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"የብሉቱዝ ኦዲዮ ኮዴክ ይምረጡ፦\nየናሙና ፍጥነት"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"የብሉቱዝ ኦዲዮ ቢት በናሙና"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"የሚመረጠውን የብሉቱዝ A2DP ኮዴክ ቢት በናሙና ምረጥ"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"የብሉቱዝ ኦዲዮ ኮዴክ ይምረጡ፦\nቢት በናሙና"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"የብሉቱዝ ኦዲዮ ሰርጥ ሁነታ"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"የሚመረጠውን የብሉቱዝ A2DP ኮዴክ ሰርጥ ሁነታ ምረጥ"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"የብሉቱዝ ኦዲዮ LDAC መልሶ ማጫወት ጥራት"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"የሚመረጠውን የብሉቱዝ A2DP ኮዴክ LDAC መልሶ ማጫወት ጥራት ይምረጡ"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"የብሉቱዝ ኦዲዮ ኮዴክ ይምረጡ፦\nየሰርጥ ሁነታ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"የብሉቱዝ ኦዲዮ LDAC ኮዴክ ይምረጡ፦ የመልሶ ማጫወት ጥራት"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"የብሉቱዝ ኦዲዮ LDAC ኮዴክ ይምረጡ፦\nየመልሶ ማጫወት ጥራት"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ዥረት፦ <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"የገመድ አልባ ማሳያ እውቅና ማረጋገጫ አማራጮችን አሳይ"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"የWi‑Fi ምዝግብ ማስታወሻ አያያዝ ደረጃ ጨምር፣ በWi‑Fi መምረጫ ውስጥ በአንድ SSID RSSI አሳይ"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"ሲነቃ የWi‑Fi ምልክት ዝቅተኛ ሲሆን Wi‑Fi የውሂብ ግንኙነት ለተንቀሳቃሽ ማስረከብ ላይ ይበልጥ አስገዳጅ ይሆናል"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"እገዛ እና ግብረመልስ"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"ምናሌ"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"ጂኤምቲ"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"የፋብሪካ ዳግም ማስጀመር በማሳያ ሁነታ ውስጥ ለማከናወን የይለፍ ቃል ያስገቡ"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"ቀጣይ"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"የይለፍ ቃል ያስፈልጋል"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index ae61f64..e7b778c 100644
--- a/packages/SettingsLib/res/values-ar/arrays.xml
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"‏استخدام التحقق من HDCP دومًا"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"افتراضي"</item>
+    <item msgid="7065842274271279580">"استخدام اختيار النظام (افتراضي)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"افتراضي"</item>
+    <item msgid="5062108632402595000">"استخدام اختيار النظام (افتراضي)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"افتراضي"</item>
+    <item msgid="3093023430402746802">"استخدام اختيار النظام (افتراضي)"</item>
     <item msgid="8895532488906185219">"44.1 كيلو هرتز"</item>
     <item msgid="2909915718994807056">"48.0 كيلو هرتز"</item>
     <item msgid="3347287377354164611">"88.2 كيلو هرتز"</item>
     <item msgid="1234212100239985373">"96.0 كيلو هرتز"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"افتراضي"</item>
+    <item msgid="3214516120190965356">"استخدام اختيار النظام (افتراضي)"</item>
     <item msgid="4482862757811638365">"44.1 كيلو هرتز"</item>
     <item msgid="354495328188724404">"48.0 كيلو هرتز"</item>
     <item msgid="7329816882213695083">"88.2 كيلو هرتز"</item>
     <item msgid="6967397666254430476">"96.0 كيلو هرتز"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"افتراضي"</item>
+    <item msgid="2684127272582591429">"استخدام اختيار النظام (افتراضي)"</item>
     <item msgid="5618929009984956469">"16 بت لكل عيّنة"</item>
     <item msgid="3412640499234627248">"24 بت لكل عيّنة"</item>
     <item msgid="121583001492929387">"32 بت لكل عيّنة"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"افتراضي"</item>
+    <item msgid="1081159789834584363">"استخدام اختيار النظام (افتراضي)"</item>
     <item msgid="4726688794884191540">"16 بت لكل عيّنة"</item>
     <item msgid="305344756485516870">"24 بت لكل عيّنة"</item>
     <item msgid="244568657919675099">"32 بت لكل عيّنة"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"افتراضي"</item>
+    <item msgid="5226878858503393706">"استخدام اختيار النظام (افتراضي)"</item>
     <item msgid="4106832974775067314">"أحادي"</item>
     <item msgid="5571632958424639155">"استريو"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"افتراضي"</item>
+    <item msgid="4118561796005528173">"استخدام اختيار النظام (افتراضي)"</item>
     <item msgid="8900559293912978337">"أحادي"</item>
     <item msgid="8883739882299884241">"استريو"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"جودة الصوت المفضّلة (990/909 كيلوبت في الثانية)"</item>
-    <item msgid="138837449700903545">"قياسي (660/606 كيلوبت في الثانية)"</item>
-    <item msgid="4777177307869441982">"الاتصال المفضّل (330/303 كيلوبت في الثانية)"</item>
+    <item msgid="3411577996960199959">"تحسين جودة الصوت (990 كيلوبت في الثانية/909 كيلوبت في الثانية)"</item>
+    <item msgid="2921767058740704969">"جودة متوازنة للصوت والاتصال (660 كيلوبت في الثانية/606 كيلوبت في الثانية)"</item>
+    <item msgid="3682554248829489641">"تحسين جودة الاتصال (330 كيلوبت في الثانية/303 كيلوبت في الثانية)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"جودة الصوت المفضّلة (990/909 كيلوبت في الثانية)"</item>
-    <item msgid="9091111147684472529">"قياسي (660/606 كيلوبت في الثانية)"</item>
-    <item msgid="3367904477834831032">"الاتصال المفضّل (330/303 كيلوبت في الثانية)"</item>
+    <item msgid="7668834469173465015">"تحسين جودة الصوت"</item>
+    <item msgid="4327143584633311908">"جودة متوازنة للصوت والاتصال"</item>
+    <item msgid="6155648878105378550">"تحسين جودة الاتصال"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"إيقاف"</item>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e1b39c11..fa7e253 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"بيانات الجوّال نشطة دائمًا"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"تعطيل مستوى الصوت المطلق"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ترميز صوت بلوتوث"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"‏حدّد ترميز بلوتوث A2DP المفضّل"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"اختيار برنامج الترميز لصوت البلوتوث"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"معدّل عيّنة صوت بلوتوث"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"‏حدّد معدّل عيّنة ترميز A2DP بلوتوث المفضّل"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"اختيار برنامج ترميز صوت البلوتوث:\nمعدل العينة"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"وحدات البت لكل عيّنة في صوت بلوتوث"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"‏حدّد عدد وحدات البت لكل عيّنة المفضّل في ترميز بلوتوث A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"اختيار برنامج ترميز صوت البلوتوث:\nوحدات بت لكل عينة"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"وضع قناة صوت بلوتوث"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"‏حدّد وضع القناة المفضّل في ترميز بلوتوث A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"‏جودة تشغيل صوت بلوتوث LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"‏حدّد جودة التشغيل المفضّلة في ترميز بلوتوث A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"اختيار برنامج ترميز صوت البلوتوث:\nوضع القناة"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"‏برنامج ترميز LDAC لصوت البلوتوث: جودة التشغيل"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"‏اختيار برنامج ترميز LDAC لصوت البلوتوث:\nجودة التشغيل"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"البث: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"عرض خيارات شهادة عرض شاشة لاسلكي"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‏زيادة مستوى تسجيل Wi-Fi، وعرض لكل SSID RSSI في منتقي Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"‏عند تمكينه، سيكون Wi-Fi أكثر حدة في تسليم اتصال البيانات إلى الشبكة الخلوية، وذلك عندما تكون إشارة WiFi منخفضة"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"المساعدة والتعليقات"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"القائمة"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"غرينيتش"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"إدخال كلمة المرور لإعادة الضبط بحسب بيانات المصنع في الوضع التجريبي"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"التالي"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"يلزم توفر كلمة مرور"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml
index 38b2dc0..9ad0c28 100644
--- a/packages/SettingsLib/res/values-az/arrays.xml
+++ b/packages/SettingsLib/res/values-az/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Yalnız DRM məzmun oxumaq üçün HDCP istifadə edin"</item>
     <item msgid="45075631231212732">"Həmişə HDCP yoxlama istifadə edin"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Defolt"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Defolt"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Defolt"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Defolt"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Defolt"</item>
-    <item msgid="5618929009984956469">"16 bit/nümunə"</item>
-    <item msgid="3412640499234627248">"24 bit/nümunə"</item>
-    <item msgid="121583001492929387">"32 bit/nümunə"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Defolt"</item>
-    <item msgid="4726688794884191540">"16 bit/nümunə"</item>
-    <item msgid="305344756485516870">"24 bit/nümunə"</item>
-    <item msgid="244568657919675099">"32 bit/nümunə"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Defolt"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Defolt"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Səs keyfiyyəti tərcih edildi (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Standart (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Bağlantı tərcih edildi (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Səs keyfiyyəti tərcih edildi (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Standart (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Bağlantı tərcih edildi (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Deaktiv"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index e625e32..db58c5c 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobil data həmişə aktivdir"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Mütləq səs həcmi deaktiv edin"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio Kodek"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Tərcih edilmiş Bluetooth A2DP Kodek seçin"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth Audio Nümunə Göstəricisi"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Tərcih edilmiş Bluetooth A2DP Kodek Nümunə Göstəricisi seçin"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Hər Nümunə Üçün Bluetooth Audio Bit"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Hər nümunə üçün tərcih edilmiş Bluetooth A2DP Kodek Bit seçin"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth Audio Kanal Rejimi"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Tərcih edilmiş Bluetooth A2DP Kodek Kanal Rejimi seçin"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth Audio LDAC Oxutma Keyfiyyəti"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Tərcih edilmiş Bluetooth A2DP Kodek LDAC Oxutma Keyfiyyəti seçin"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz displey sertifikatlaşması üçün seçimləri göstərir"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi giriş səviyyəsini qaldırın, Wi‑Fi seçəndə hər SSID RSSI üzrə göstərin"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Aktiv olanda, Wi‑Fi sianqlı zəif olan zaman, Mobil şəbəkə data bağlantısına nisbətən, Wi‑Fi daha aqressiv olacaq"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Yardım və rəy"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Demo rejimində sıfırlamaq üçün parol daxil edin"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Növbəti"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Parol tələb olunur"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
index 9f7cf56..ab3b8b9 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Uvek koristi HDCP proveru"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Podrazumevano"</item>
+    <item msgid="7065842274271279580">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Podrazumevano"</item>
+    <item msgid="5062108632402595000">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Podrazumevano"</item>
+    <item msgid="3093023430402746802">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Podrazumevano"</item>
+    <item msgid="3214516120190965356">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Podrazumevano"</item>
+    <item msgid="2684127272582591429">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="5618929009984956469">"16 bitova po uzorku"</item>
     <item msgid="3412640499234627248">"24 bita po uzorku"</item>
     <item msgid="121583001492929387">"32 bita po uzorku"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Podrazumevano"</item>
+    <item msgid="1081159789834584363">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="4726688794884191540">"16 bitova po uzorku"</item>
     <item msgid="305344756485516870">"24 bita po uzorku"</item>
     <item msgid="244568657919675099">"32 bita po uzorku"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Podrazumevano"</item>
+    <item msgid="5226878858503393706">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Podrazumevano"</item>
+    <item msgid="4118561796005528173">"Koristi izbor sistema (podrazumevano)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Željeni kvalitet zvuka (990 kb/s/909 kb/s)"</item>
-    <item msgid="138837449700903545">"Standardno (660 kb/s/606 kb/s)"</item>
-    <item msgid="4777177307869441982">"Željena veza (330 kb/s/303 kb/s)"</item>
+    <item msgid="3411577996960199959">"Optimizuj za kvalitet zvuka (990 kb/s/909 kb/s)"</item>
+    <item msgid="2921767058740704969">"Ujednačen kvalitet zvuka i veze (660 kb/s/606 kb/s)"</item>
+    <item msgid="3682554248829489641">"Optimizuj za kvalitet veze (330 kb/s/303 kb/s)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Željeni kvalitet zvuka (990 kb/s/909 kb/s)"</item>
-    <item msgid="9091111147684472529">"Standardno (660 kb/s/606 kb/s)"</item>
-    <item msgid="3367904477834831032">"Željena veza (330 kb/s/303 kb/s)"</item>
+    <item msgid="7668834469173465015">"Optimizuje se za kvalitet zvuka"</item>
+    <item msgid="4327143584633311908">"Ujednačen kvalitet zvuka i veze"</item>
+    <item msgid="6155648878105378550">"Optimizuje se za kvalitet veze"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Isključeno"</item>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3446a1a..0aa9524 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Podaci za mobilne uređaje su uvek aktivni"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Onemogući glavno podešavanje jačine zvuka"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth audio kodek"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Izaberite željeni Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Izaberite Bluetooth audio kodek"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Brzina uzorkovanja za Bluetooth audio"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Izaberite željenu brzinu uzorkovanja za Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Izaberite Bluetooth audio kodek:\nbrzina uzorkovanja"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bitova po uzorku za Bluetooth audio"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Izaberite željeni broj bitova po uzorku za Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Izaberite Bluetooth audio kodek:\nbitova po uzorku"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Režim kanala za Bluetooth audio"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Izaberite željeni režim kanala za Bluetooth A2DP kodek"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Kvalitet LDAC snimka za Bluetooth audio"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Izaberite željeni kvalitet LDAC snimka za Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Izaberite Bluetooth audio kodek:\nrežim kanala"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth audio kodek LDAC: kvalitet reprodukcije"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Izaberite Bluetooth audio kodek LDAC:\nkvalitet reprodukcije"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Strimovanje: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaz opcija za sertifikaciju bežičnog ekrana"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećava nivo evidentiranja za Wi‑Fi. Prikaz po SSID RSSI-u u biraču Wi‑Fi mreže"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Kada se omogući, Wi‑Fi će biti agresivniji pri prebacivanju mreže za prenos podataka na Mobilnu, kada je Wi‑Fi signal slab"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Unesite lozinku da biste obavili resetovanje na fabrička podešavanja u režimu demonstracije"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Dalje"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Potrebna je lozinka"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml
index 1266d2c..f5b2bb3 100644
--- a/packages/SettingsLib/res/values-be/arrays.xml
+++ b/packages/SettingsLib/res/values-be/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Заўсёды выкарыстоўваць праверку HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Стандартная"</item>
+    <item msgid="7065842274271279580">"Выкарыстоўв. выбар сістэмы (стандартны)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Стандартная"</item>
+    <item msgid="5062108632402595000">"Выкарыстоўв. выбар сістэмы (стандартны)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Стандартная"</item>
+    <item msgid="3093023430402746802">"Выкарыстоўв. выбар сістэмы (стандартны)"</item>
     <item msgid="8895532488906185219">"44,1 кГц"</item>
     <item msgid="2909915718994807056">"48,0 кГц"</item>
     <item msgid="3347287377354164611">"88,2 кГц"</item>
     <item msgid="1234212100239985373">"96,0 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Стандартная"</item>
+    <item msgid="3214516120190965356">"Выкарыстоўв. выбар сістэмы (стандартны)"</item>
     <item msgid="4482862757811638365">"44,1 кГц"</item>
     <item msgid="354495328188724404">"48,0 кГц"</item>
     <item msgid="7329816882213695083">"88,2 кГц"</item>
     <item msgid="6967397666254430476">"96,0 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Стандартная"</item>
+    <item msgid="2684127272582591429">"Выкарыстоўв. выбар сістэмы (стандартны)"</item>
     <item msgid="5618929009984956469">"16 бітаў/сэмпл"</item>
     <item msgid="3412640499234627248">"24 біты/сэмпл"</item>
     <item msgid="121583001492929387">"32 біты/сэмпл"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Стандартная"</item>
+    <item msgid="1081159789834584363">"Выкарыстоўв. выбар сістэмы (стандартны)"</item>
     <item msgid="4726688794884191540">"16 бітаў/сэмпл"</item>
     <item msgid="305344756485516870">"24 біты/сэмпл"</item>
     <item msgid="244568657919675099">"32 біты/сэмпл"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Стандартная"</item>
+    <item msgid="5226878858503393706">"Выкарыстоўв. выбар сістэмы (стандартны)"</item>
     <item msgid="4106832974775067314">"Мона"</item>
     <item msgid="5571632958424639155">"Стэрэа"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Стандартная"</item>
+    <item msgid="4118561796005528173">"Выкарыстоўв. выбар сістэмы (стандартны)"</item>
     <item msgid="8900559293912978337">"Мона"</item>
     <item msgid="8883739882299884241">"Стэрэа"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Прыярытэтная якасць гуч. (990/909кбіт/с)"</item>
-    <item msgid="138837449700903545">"Стандартная (660/606кбіт/с)"</item>
-    <item msgid="4777177307869441982">"Прыярытэтнае падключэнне (330/303кбіт/с)"</item>
+    <item msgid="3411577996960199959">"Аптымізацыя якасці гуку (990 кбіт/c / 909 кбіт/c)"</item>
+    <item msgid="2921767058740704969">"Збалансаваная якасць аўдыя і падключэння (660кбіт/c / 606 кбіт/c)"</item>
+    <item msgid="3682554248829489641">"Аптымізацыя якасці падключэння (330 кбіт/c / 303 кбіт/c)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Прыярытэтная якасць гуч. (990/909кбіт/с)"</item>
-    <item msgid="9091111147684472529">"Стандартная (660/606кбіт/с)"</item>
-    <item msgid="3367904477834831032">"Прыярытэтнае падключэнне (330/303кбіт/с)"</item>
+    <item msgid="7668834469173465015">"Аптымізацыя якасці гуку"</item>
+    <item msgid="4327143584633311908">"Збалансаваная якасць аўдыя і падключэння"</item>
+    <item msgid="6155648878105378550">"Аптымізацыя якасці падключэння"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Выкл."</item>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index f2e1cf6..6fd82eb 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Перадача даных мабільнай сувязі заўсёды актыўна"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Адключыць абсалютны гук"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Кодэк Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Выберыце прыярытэтны кодэк Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Выбраць аўдыякодэк Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Частата дыскрэтызацыі Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Выберыце прыярытэтную частату дыскрэтызацыі для кодэка Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Выбраць аўдыякодэк Bluetooth:\nчастата дыскрэтызацыі"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Біты на сэмпл для Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Выберыце прыярытэтную колькасць бітаў на сэмпл для кодэка Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Выбраць аўдыякодэк Bluetooth:\nбіты на сэмпл"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Канальны рэжым Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Выберыце прыярытэтны рэжым для кодэка Bluetooth A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Якасць прайгравання для Bluetooth Audio LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Выберыце прыярытэтную якасць прайгравання для кодэка Bluetooth A2DP LDAC"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Выбраць аўдыякодэк Bluetooth:\nканальны рэжым"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Аўдыякодэк Bluetooth Audio LDAC: якасць прайгравання"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Выбраць аўдыякодэк Bluetooth Audio LDAC:\nякасць прайгравання"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Перадача плынню: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Паказаць опцыі сертыфікацыі бесправаднога дысплея"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Падвыс. узровень дэтал-цыі журнала Wi‑Fi у залежн. ад SSID RSSI у Wi‑Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Калі ўкл., прылада будзе больш інтэнсіўна імкнуцца перайсці з падлуч. да Wi-Fi на падлуч. да маб. сеткі, калі сігнал Wi‑Fi слабы"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Даведка і водгукі"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Каб выканаць скід да заводскіх налад у дэманстрацыйным рэжыме, увядзіце пароль"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Далей"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Патрабуецца пароль"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index d220a07..4ada334 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Винаги да се използва проверка с HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Стандартно"</item>
+    <item msgid="7065842274271279580">"Използване на сист. избор (стандартно)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Стандартно"</item>
+    <item msgid="5062108632402595000">"Използване на сист. избор (стандартно)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Стандартно"</item>
+    <item msgid="3093023430402746802">"Използване на сист. избор (стандартно)"</item>
     <item msgid="8895532488906185219">"44,1 кХц"</item>
     <item msgid="2909915718994807056">"48 кХц"</item>
     <item msgid="3347287377354164611">"88,2 кХц"</item>
     <item msgid="1234212100239985373">"96 кХц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Стандартно"</item>
+    <item msgid="3214516120190965356">"Използване на сист. избор (стандартно)"</item>
     <item msgid="4482862757811638365">"44,1 кХц"</item>
     <item msgid="354495328188724404">"48 кХц"</item>
     <item msgid="7329816882213695083">"88,2 кХц"</item>
     <item msgid="6967397666254430476">"96 кХц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Стандартно"</item>
+    <item msgid="2684127272582591429">"Използване на сист. избор (стандартно)"</item>
     <item msgid="5618929009984956469">"16 бита/дискрет"</item>
     <item msgid="3412640499234627248">"24 бита/дискрет"</item>
     <item msgid="121583001492929387">"32 бита/дискрет"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Стандартно"</item>
+    <item msgid="1081159789834584363">"Използване на сист. избор (стандартно)"</item>
     <item msgid="4726688794884191540">"16 бита/дискрет"</item>
     <item msgid="305344756485516870">"24 бита/дискрет"</item>
     <item msgid="244568657919675099">"32 бита/дискрет"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Стандартно"</item>
+    <item msgid="5226878858503393706">"Използване на сист. избор (стандартно)"</item>
     <item msgid="4106832974775067314">"Моно"</item>
     <item msgid="5571632958424639155">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Стандартно"</item>
+    <item msgid="4118561796005528173">"Използване на сист. избор (стандартно)"</item>
     <item msgid="8900559293912978337">"Моно"</item>
     <item msgid="8883739882299884241">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Предпоч. кач. на звука (990 или 909 кб/сек)"</item>
-    <item msgid="138837449700903545">"Стандартно (660 или 606 кб/сек)"</item>
-    <item msgid="4777177307869441982">"Предпоч. връзка (330 или 303 кб/сек)"</item>
+    <item msgid="3411577996960199959">"Оптимизиране за качество на звука (990 или 909 кб/сек)"</item>
+    <item msgid="2921767058740704969">"Балансирано качество на звука и връзката (660 или 606 кб/сек)"</item>
+    <item msgid="3682554248829489641">"Оптимизиране за качество на връзката (330 или 303 кб/сек)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Предпоч. кач. на звука (990 или 909 кб/сек)"</item>
-    <item msgid="9091111147684472529">"Стандартно (660 или 606 кб/сек)"</item>
-    <item msgid="3367904477834831032">"Предпоч. връзка (330 или 303 кб/сек)"</item>
+    <item msgid="7668834469173465015">"Оптимизиране за качество на звука"</item>
+    <item msgid="4327143584633311908">"Балансирано качество на звука и връзката"</item>
+    <item msgid="6155648878105378550">"Оптимизиране за качество на връзката"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Изключено"</item>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 21d1a98..351b489 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Винаги активни клетъчни данни"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Деактивиране на пълната сила на звука"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Аудиокодек за Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Изберете предпочитания кодек за Bluetooth с профил A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Изберете аудиокодек за Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Честота на дискретизация за звука през Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Изберете предпочитаната честота на дискретизация за кодека за Bluetooth с профил A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Изберете аудиокодек за Bluetooth:\nЧестота на дискретизация"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Битове на дискрет за звука през Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Изберете предпочитание за битове на дискрет за кодека за Bluetooth с профил A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Изберете аудиокодек за Bluetooth:\nБитове на дискрет"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Режим на канала на звука през Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Изберете предпочитания режим на канала за кодека за Bluetooth с профил A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Качество на възпроизвеждане на звука през Bluetooth с технологията LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Изберете предпочитаното качество на възпроизвеждане с технологията LDAC за кодека за Bluetooth с профил A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Изберете аудиокодек за Bluetooth:\nРежим на канала"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Кодек за звука през Bluetooth с технологията LDAC: Качество на възпроизвеждане"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Изберете кодек за звука през Bluetooth с технологията LDAC:\nКачество на възпроизвеждане"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Поточно предаване: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показване на опциите за сертифициране на безжичния дисплей"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"По-подробно регистр. на Wi‑Fi – данни за RSSI на SSID в инстр. за избор на Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"При активиране предаването на връзката за данни от Wi-Fi към мобилната мрежа ще е по-агресивно, когато Wi-Fi сигналът е слаб"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Помощ и отзиви"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"Средно време по Гринуич (GMT)"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Въведете парола за възст. на фабр. настройки в демонстр. режим"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Напред"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Изисква се парола"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml
index 6ce7b14..d558127 100644
--- a/packages/SettingsLib/res/values-bn/arrays.xml
+++ b/packages/SettingsLib/res/values-bn/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"সর্বদা HDCP পরীক্ষণ ব্যবহার করুন"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ডিফল্ট"</item>
+    <item msgid="7065842274271279580">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ডিফল্ট"</item>
+    <item msgid="5062108632402595000">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ডিফল্ট"</item>
+    <item msgid="3093023430402746802">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="8895532488906185219">"৪৪.১ kHz"</item>
     <item msgid="2909915718994807056">"৪৮.০ kHz"</item>
     <item msgid="3347287377354164611">"৮৮.২ kHz"</item>
     <item msgid="1234212100239985373">"৯৬.০ kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ডিফল্ট"</item>
+    <item msgid="3214516120190965356">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="4482862757811638365">"৪৪.১ kHz"</item>
     <item msgid="354495328188724404">"৪৮.০ kHz"</item>
     <item msgid="7329816882213695083">"৮৮.২ kHz"</item>
     <item msgid="6967397666254430476">"৯৬.০ kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ডিফল্ট"</item>
+    <item msgid="2684127272582591429">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="5618929009984956469">"১৬ বিট/নমুনা"</item>
     <item msgid="3412640499234627248">"২৪ বিট/নমুনা"</item>
     <item msgid="121583001492929387">"৩২ বিট/নমুনা"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ডিফল্ট"</item>
+    <item msgid="1081159789834584363">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="4726688794884191540">"১৬ বিট/নমুনা"</item>
     <item msgid="305344756485516870">"২৪ বিট/নমুনা"</item>
     <item msgid="244568657919675099">"৩২ বিট/নমুনা"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ডিফল্ট"</item>
+    <item msgid="5226878858503393706">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="4106832974775067314">"মোনো"</item>
     <item msgid="5571632958424639155">"স্টিরিও"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ডিফল্ট"</item>
+    <item msgid="4118561796005528173">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
     <item msgid="8900559293912978337">"মোনো"</item>
     <item msgid="8883739882299884241">"স্টিরিও"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"পছন্দের শব্দের মান (৯৯০kbps/৯০৯kbps)"</item>
-    <item msgid="138837449700903545">"মানক (৬৬০kbps/৬০৬kbps)"</item>
-    <item msgid="4777177307869441982">"পছন্দের সংযোগ (৩৩০kbps/৩০৩kbps)"</item>
+    <item msgid="3411577996960199959">"অডিওর গুণমানের জন্য অপটিমাইজ করুন (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"সন্তুলিত গুণমানের অডিও এবং সংযোগ (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"সংযোগের গুণমানের জন্য অপটিমাইজ করুন (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"পছন্দের শব্দের মান (৯৯০kbps/৯০৯kbps)"</item>
-    <item msgid="9091111147684472529">"মানক (৬৬০kbps/৬০৬kbps)"</item>
-    <item msgid="3367904477834831032">"পছন্দের সংযোগ (৩৩০kbps/৩০৩kbps)"</item>
+    <item msgid="7668834469173465015">"অডিওর গুণমানের জন্য অপটিমাইজ করুন"</item>
+    <item msgid="4327143584633311908">"সন্তুলিত গুণমানের অডিও এবং সংযোগ"</item>
+    <item msgid="6155648878105378550">"সংযোগের গুণমানের জন্য অপটিমাইজ করুন"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"বন্ধ আছে"</item>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 66fa0673..f445e8f 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"সেলুলার ডেটা সর্বদাই সক্রিয় থাকে"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"চূড়ান্ত ভলিউম অক্ষম করুন"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ব্লুটুথ অডিও কোডেক"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"পছন্দের ব্লুটুথ A2DP কোডেক বেছে নিন"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"ব্লুটুথ অডিও কোডেক বেছে নিন"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"ব্লুটুথ অডিওর নমুনা হার"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"পছন্দের ব্লুটুথ A2DP কোডেক নমুনা হার বেছে নিন"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"ব্লুটুথ অডিও কোডেক বেছে নিন:\nস্যাম্পল রেট"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"নমুনা প্রতি ব্লুটুথ অডিও বিট"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"নমুনা প্রতি পছন্দের ব্লুটুথ A2DP কোডেক বিট বেছে নিন"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"ব্লুটুথ অডিও কোডেক বেছে নিন:\nবিটস পার স্যাম্পল"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ব্লুটুথ অডিও চ্যানেল মোড"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"পছন্দের ব্লুটুথ A2DP কোডেক চ্যানেল মোড বেছে নিন"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"ব্লুটুথ অডিও LDAC প্লেব্যাকের মান"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"পছন্দের ব্লুটুথ A2DP কোডেক LDAC প্লেব্যাকের মান বেছে নিন"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"ব্লুটুথ অডিও কোডেক বেছে নিন:\nচ্যানেল মোড"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ব্লুটুথ অডিও LDAC কোডেক: প্লেব্যাক গুণমান"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ব্লুটুথ অডিও LDAC কোডেক বেছে নিন:\nপ্লেব্যাক গুণমান"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"স্ট্রিমিং: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ওয়্যারলেস প্রদর্শন সার্টিফিকেশন জন্য বিকল্পগুলি দেখান"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ওয়াই-ফাই লগিং স্তর বাড়ান, ওয়াই-ফাই চয়নকারীতে SSID RSSI অনুযায়ী দেখান"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"সক্ষম করা থাকলে, নিম্নমানের ওয়াই-ফাই সিগন্যালের ক্ষেত্রে, সেলুলার-এ ডেটা সংযোগ প্রদান করতে ওয়াই-ফাই আরো বেশি শক্তিশালীভাবে কাজ করবে"</string>
@@ -352,4 +353,10 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"সহায়তা ও মতামত"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"মেনু"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <!-- no translation found for retail_demo_reset_message (118771671364131297) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_next (8356731459226304963) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_title (696589204029930100) -->
+    <skip />
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 93e0678..d9c10f8 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Uvijek koristi HDCP provjeru"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Podrazumijevano"</item>
+    <item msgid="7065842274271279580">"Koristi odabir sistema (Zadano)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Podrazumijevano"</item>
+    <item msgid="5062108632402595000">"Koristi odabir sistema (Zadano)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Zadano"</item>
+    <item msgid="3093023430402746802">"Koristi odabir sistema (Zadano)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Zadano"</item>
+    <item msgid="3214516120190965356">"Koristi odabir sistema (Zadano)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Podrazumijevano"</item>
+    <item msgid="2684127272582591429">"Koristi odabir sistema (Zadano)"</item>
     <item msgid="5618929009984956469">"16 bitova/uzorak"</item>
     <item msgid="3412640499234627248">"24 bitova/uzorak"</item>
     <item msgid="121583001492929387">"32 bitova/uzorak"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Podrazumijevano"</item>
+    <item msgid="1081159789834584363">"Koristi odabir sistema (Zadano)"</item>
     <item msgid="4726688794884191540">"16 bitova/uzorak"</item>
     <item msgid="305344756485516870">"24 bitova/uzorak"</item>
     <item msgid="244568657919675099">"32 bitova/uzorak"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Podrazumijevano"</item>
+    <item msgid="5226878858503393706">"Koristi odabir sistema (Zadano)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Podrazumijevano"</item>
+    <item msgid="4118561796005528173">"Koristi odabir sistema (Zadano)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Željena kvaliteta zvuka (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Standardni (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Željena veza (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"Optimiziraj za kvalitet zvuka (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"Uravnotežen kvalitet zvuka i veze (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"Optimiziraj za kvalitet veze (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Željena kvaliteta zvuka (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Standardni (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Željena veza (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"Optimiziraj za kvalitet zvuka"</item>
+    <item msgid="4327143584633311908">"Uravnotežen kvalitet zvuka i veze"</item>
+    <item msgid="6155648878105378550">"Optimiziraj za kvalitet veze"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Isključeno"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index f2ae338..aaf9713 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobilni podaci uvijek aktivni"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Onemogućite apsolutnu jačinu zvuka"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio kodek"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Izaberite željeni Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Odaberite Bluetooth Audio kodek"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Brzina uzorkovanja za Bluetooth audio"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Izaberite željenu brzinu uzorkovanja za Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Odaberite Bluetooth Audio kodek:\nBrzina uzorka"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth audio bitovi po uzorku"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Izaberite željeni broj bitova po uzorku za Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Odaberite Bluetooth Audio kodek:\nBita po uzorku"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Način Bluetooth audio kanala"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Izaberite željeni način kanala Bluetooth A2DP kodeka"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Kvaliteta reprodukcije za Bluetooth Audio LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Izaberite željenu kvalitetu LDAC reprodukcije za Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Odaberite Bluetooth Audio kodek:\nNačin rada kanala"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC kodek: Kvalitet reprodukcije"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Odaberite Bluetooth Audio LDAC kodek:\nKvalitet reprodukcije"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Prijenos: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži opcije za certifikaciju Bežičnog prikaza"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećajte nivo Wi-Fi zapisivanja, pokazati po SSID RSSI Wi-Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Kada je omogućeno, Wi-Fi će biti agresivniji u predavanju podatkovne veze mobilnoj, kada je Wi-Fi signal slab"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Unesite lozinku da izvršite vraćanje na fabričke postavke u načinu demonstracije"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Naprijed"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Potrebna je lozinka"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index 6cb4217..5c8c4d7 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Utilitza sempre la comprovació HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Predeterminat"</item>
+    <item msgid="7065842274271279580">"Selecció del sistema (predeterminada)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Predeterminat"</item>
+    <item msgid="5062108632402595000">"Selecció del sistema (predeterminada)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Predeterminat"</item>
+    <item msgid="3093023430402746802">"Selecció del sistema (predeterminada)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Predeterminat"</item>
+    <item msgid="3214516120190965356">"Selecció del sistema (predeterminada)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Predeterminat"</item>
+    <item msgid="2684127272582591429">"Selecció del sistema (predeterminada)"</item>
     <item msgid="5618929009984956469">"16 bits/mostra"</item>
     <item msgid="3412640499234627248">"24 bits/mostra"</item>
     <item msgid="121583001492929387">"32 bits/mostra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Predeterminat"</item>
+    <item msgid="1081159789834584363">"Selecció del sistema (predeterminada)"</item>
     <item msgid="4726688794884191540">"16 bits/mostra"</item>
     <item msgid="305344756485516870">"24 bits/mostra"</item>
     <item msgid="244568657919675099">"32 bits/mostra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Predeterminat"</item>
+    <item msgid="5226878858503393706">"Selecció del sistema (predeterminada)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Estèreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Predeterminat"</item>
+    <item msgid="4118561796005528173">"Selecció del sistema (predeterminada)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Estèreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Qualitat del so preferida (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Estàndard (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Connexió preferida (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Optimitza la qualitat de l\'àudio (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Qualitat equilibrada de l\'àudio i la connexió (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimitza la qualitat de la connexió (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Qualitat del so preferida (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Estàndard (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Connexió preferida (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Optimitza la qualitat de l\'àudio"</item>
+    <item msgid="4327143584633311908">"Qualitat equilibrada de l\'àudio i la connexió"</item>
+    <item msgid="6155648878105378550">"Optimitza la qualitat de la connexió"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"No"</item>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index ad83d6b..391782b 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Dades mòbils sempre actives"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desactiva el volum absolut"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Còdec d\'àudio per Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Selecciona el còdec Bluetooth A2DP preferit"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Selecciona el còdec d\'àudio per Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Velocitat de mostra d’àudio per Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Selecciona la velocitat de mostra preferida del còdec Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Selecciona el còdec d\'àudio per Bluetooth:\nFreqüència de mostratge"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits per mostra de l\'àudio per Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Selecciona els bits per mostra preferits del còdec Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Selecciona el còdec d\'àudio per Bluetooth:\nBits per mostra"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Mode de canal de l\'àudio per Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Selecciona el mode de canal preferit del còdec Bluetooth A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Qualitat de reproducció LDAC de l\'àudio per Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Selecciona la qualitat de reproducció LDAC preferida del còdec Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Selecciona el còdec d\'àudio per Bluetooth:\nMode de canal"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Còdec LDAC d\'àudio per Bluetooth: qualitat de reproducció"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selecciona el còdec LDAC d\'àudio per Bluetooth:\nQualitat de reproducció"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"S\'està reproduint en temps real: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra les opcions de certificació de pantalla sense fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Augmenta nivell de registre Wi‑Fi i mostra\'l per SSID RSSI al Selector de Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Si s\'activa, la Wi-Fi serà més agressiva en transferir la connexió de dades al mòbil, si el senyal de la Wi-Fi no és estable"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda i suggeriments"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Introdueix la contrasenya per restablir les dades de fàbrica en mode de demostració"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Següent"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Contrasenya obligatòria"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index bba5a16..8c96ac3 100644
--- a/packages/SettingsLib/res/values-cs/arrays.xml
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Použít kontrolu HDCP pouze pro obsah DRM"</item>
     <item msgid="45075631231212732">"Vždy používat kontrolu HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Výchozí"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Výchozí"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Výchozí"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Výchozí"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Výchozí"</item>
-    <item msgid="5618929009984956469">"16 bitů / vzorek"</item>
-    <item msgid="3412640499234627248">"24 bitů / vzorek"</item>
-    <item msgid="121583001492929387">"32 bitů / vzorek"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Výchozí"</item>
-    <item msgid="4726688794884191540">"16 bitů / vzorek"</item>
-    <item msgid="305344756485516870">"24 bitů / vzorek"</item>
-    <item msgid="244568657919675099">"32 bitů / vzorek"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Výchozí"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Výchozí"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Přednost kvality zvuku (990 kb/s / 909 kb/s)"</item>
-    <item msgid="138837449700903545">"Standardní (660 kb/s / 606 kB/s)"</item>
-    <item msgid="4777177307869441982">"Přednost připojení (330 kb/s / 303 kb/s)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Přednost kvality zvuku (990 kb/s / 909 kb/s)"</item>
-    <item msgid="9091111147684472529">"Standardní (660 kb/s / 606 kB/s)"</item>
-    <item msgid="3367904477834831032">"Přednost připojení (330 kb/s / 303 kb/s)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Vypnuto"</item>
     <item msgid="1593289376502312923">"64 kB"</item>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 706aa91..ee318ad 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobilní data jsou vždy aktivní"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Zakázat absolutní hlasitost"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio – kodek"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Vyberte preferovaný kodek Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth Audio – vzorkovací frekvence"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Vybrat vzorkovací frekvenci pro preferovaný kodek Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth Audio – počet bitů na vzorek"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Vybrat počet bitů na vzorek pro preferovaný kodek Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth Audio – režim kanálu"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Vybrat režim kanálu pro preferovaný kodek Bluetooth A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth Audio LDAC – kvalita přehrávání"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Vybrat kvalitu přehrávání pro preferovaný kodek Bluetooth A2DP LDAC"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Zobrazit možnosti certifikace bezdrátového displeje"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zvýšit úroveň protokolování Wi‑Fi zobrazenou v SSID a RSSI při výběru sítě Wi‑Fi."</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Pokud je tato možnost zapnuta, bude síť Wi-Fi agresivnější při předávání datového připojení mobilní síti při slabém signálu Wi-Fi."</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Nápověda a zpětná vazba"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Nabídka"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Chcete-li v ukázkovém režimu obnovit zařízení do továrního nastavení, zadejte heslo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Další"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Je třeba zadat heslo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 6ae0aa8..1b54d4a 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Brug kun HDCP-kontrol ved DRM-indhold"</item>
     <item msgid="45075631231212732">"Brug altid HDCP-kontrol"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Standard"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Standard"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Standard"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Standard"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Standard"</item>
-    <item msgid="5618929009984956469">"16 bit pr. eksempel"</item>
-    <item msgid="3412640499234627248">"24 bit pr. eksempel"</item>
-    <item msgid="121583001492929387">"32 bit pr. eksempel"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Standard"</item>
-    <item msgid="4726688794884191540">"16 bit pr. eksempel"</item>
-    <item msgid="305344756485516870">"24 bit pr. eksempel"</item>
-    <item msgid="244568657919675099">"32 bit pr. eksempel"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Standard"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Standard"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Lydkvalitet foretrækkes (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Forbindelse foretrækkes (330 kbps/303 kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Lydkvalitet foretrækkes (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Forbindelse foretrækkes (330 kbps/303 kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Fra"</item>
     <item msgid="1593289376502312923">"64 kB"</item>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index c6e6fb7..3cd0a6b 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobildata altid aktiveret"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Deaktiver absolut lydstyrke"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth-lydcodec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Vælg foretrukken Bluetooth A2DP-codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Eksempelfrekvens for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Vælg eksempelfrekvens for foretrukken Bluetooth A2DP-codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bit pr. eksempel for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Vælg bit pr. eksempel for foretrukken Bluetooth A2DP-codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Kanaltilstand for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Vælg kanaltilstand for foretrukken Bluetooth A2DP-codec"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"LDAC-afspilningskvalitet for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Vælg LDAC-afspilningskvalitet for foretrukken Bluetooth A2DP-codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vis valgmuligheder for certificering af trådløs skærm"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Øg mængden af Wi‑Fi-logføring. Vis opdelt efter SSID RSSI i Wi‑Fi-vælgeren"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Når dette er aktiveret, gennemtvinges en overdragelse af dataforbindelsen fra Wi-Fi til mobilnetværk, når Wi-Fi-signalet er svagt"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Hjælp og feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Angiv adgangskode for at gendanne fabriksdata i demotilstand"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Næste"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Du skal angive en adgangskode"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
index 935c2b4..dec52b2 100644
--- a/packages/SettingsLib/res/values-de/arrays.xml
+++ b/packages/SettingsLib/res/values-de/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"HDCP-Prüfung immer verwenden"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Standard"</item>
+    <item msgid="7065842274271279580">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Standard"</item>
+    <item msgid="5062108632402595000">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Standard"</item>
+    <item msgid="3093023430402746802">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Standard"</item>
+    <item msgid="3214516120190965356">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Standard"</item>
+    <item msgid="2684127272582591429">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="5618929009984956469">"16 Bits pro Sample"</item>
     <item msgid="3412640499234627248">"24 Bits pro Sample"</item>
     <item msgid="121583001492929387">"32 Bits pro Sample"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Standard"</item>
+    <item msgid="1081159789834584363">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="4726688794884191540">"16 Bits pro Sample"</item>
     <item msgid="305344756485516870">"24 Bits pro Sample"</item>
     <item msgid="244568657919675099">"32 Bits pro Sample"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Standard"</item>
+    <item msgid="5226878858503393706">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Standard"</item>
+    <item msgid="4118561796005528173">"Systemauswahl verwenden (Standard)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Bevorzugte Tonqualität (990 kbit/s / 909 kbit/s)"</item>
-    <item msgid="138837449700903545">"Standard (660 kbit/s / 606 kbit/s)"</item>
-    <item msgid="4777177307869441982">"Bevorzugte Verbindung (330 kbit/s / 303 kbit/s)"</item>
+    <item msgid="3411577996960199959">"Für Audioqualität optimieren (990 kbit/s/909 kbit/s)"</item>
+    <item msgid="2921767058740704969">"Ausgeglichene Audio- und Verbindungsqualität (660 kbit/s/606 kbit/s)"</item>
+    <item msgid="3682554248829489641">"Für Verbindungsqualität optimieren (330 kbit/s/303 kbit/s)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Bevorzugte Tonqualität (990 kbit/s / 909 kbit/s)"</item>
-    <item msgid="9091111147684472529">"Standard (660 kbit/s / 606 kbit/s)"</item>
-    <item msgid="3367904477834831032">"Bevorzugte Verbindung (330 kbit/s / 303 kbit/s)"</item>
+    <item msgid="7668834469173465015">"Für Audioqualität optimieren"</item>
+    <item msgid="4327143584633311908">"Ausgeglichene Audio- und Verbindungsqualität"</item>
+    <item msgid="6155648878105378550">"Für Verbindungsqualität optimieren"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Aus"</item>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 0ce1384..8dd4c1db 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobile Datennutzung immer aktiviert"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Maximallautstärke deaktivieren"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth-Audio-Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Bevorzugten Bluetooth-A2DP-Codec auwählen"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth-Audio-Codec auswählen"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth-Audio-Abtastrate"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Bevorzugten Bluetooth-A2DP-Codec auswählen/Abtastrate"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth-Audio-Codec auswählen:\nAbtastrate"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth-Audio/Bits pro Sample"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Bevorzugten Bluetooth-A2DP-Codec auswählen/Bits Pro Sample"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth-Audio-Codec auswählen:\nBits pro Sample"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modus des Bluetooth-Audiokanals"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Bevorzugten Bluetooth-A2DP-Codec auswählen/Modus des Kanals"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"LDAC-Wiedergabequalität von Bluetooth-Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Bevorzugten Bluetooth-A2DP-Codec auswählen/LDAC-Wiedergabequalität"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth-Audio-Codec auswählen:\nKanalmodus"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth-Audio-LDAC-Codec: Wiedergabequalität"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth-Audio-LDAC-Codec auswählen:\nWiedergabequalität"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Optionen zur Zertifizierung für kabellose Übertragung anzeigen"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Level für WLAN-Protokollierung erhöhen, in WiFi Picker pro SSID-RSSI anzeigen"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Wenn diese Option aktiviert ist, ist WLAN bei schwachem Signal bei der Übergabe der Datenverbindung an den Mobilfunk aggressiver."</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Hilfe &amp; Feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Passwort eingeben, um Gerät im Demomodus auf Werkseinstellungen zurückzusetzen"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Weiter"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Passwort erforderlich"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml
index 04e1a8c..12a05b9 100644
--- a/packages/SettingsLib/res/values-el/arrays.xml
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Να χρησιμοποιείται πάντα έλεγχος HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Προεπιλογή"</item>
+    <item msgid="7065842274271279580">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Προεπιλογή"</item>
+    <item msgid="5062108632402595000">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Προεπιλογή"</item>
+    <item msgid="3093023430402746802">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Προεπιλογή"</item>
+    <item msgid="3214516120190965356">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Προεπιλογή"</item>
+    <item msgid="2684127272582591429">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="5618929009984956469">"16 bit/δείγμα"</item>
     <item msgid="3412640499234627248">"24 bit/δείγμα"</item>
     <item msgid="121583001492929387">"32 bit/δείγμα"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Προεπιλογή"</item>
+    <item msgid="1081159789834584363">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="4726688794884191540">"16 bit/δείγμα"</item>
     <item msgid="305344756485516870">"24 bit/δείγμα"</item>
     <item msgid="244568657919675099">"32 bit/δείγμα"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Προεπιλογή"</item>
+    <item msgid="5226878858503393706">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="4106832974775067314">"Μονοφωνικό"</item>
     <item msgid="5571632958424639155">"Στερεοφωνικό"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Προεπιλογή"</item>
+    <item msgid="4118561796005528173">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
     <item msgid="8900559293912978337">"Μονοφωνικό"</item>
     <item msgid="8883739882299884241">"Στερεοφωνικό"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Προτιμ. ποιότητα ήχου (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Τυπική (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Προτιμώμενη σύνδεση (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"Βελτιστοποίηση για ποιότητα ήχου (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"Ισορροπημένος ήχος και ποιότητα σύνδεσης (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"Βελτιστοποίηση για ποιότητα σύνδεσης (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Προτιμ. ποιότητα ήχου (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Τυπική (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Προτιμώμενη σύνδεση (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"Βελτιστοποίηση για ποιότητα ήχου"</item>
+    <item msgid="4327143584633311908">"Ισορροπημένος ήχος και ποιότητα σύνδεσης"</item>
+    <item msgid="6155648878105378550">"Βελτιστοποίηση για ποιότητα σύνδεσης"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Ανενεργό"</item>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 8d3df55..1ee6db8 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Πάντα ενεργά δεδομένα κινητής τηλεφωνίας"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Απενεργοποίηση απόλυτης έντασης"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Κωδικοποιητής ήχου Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Επιλέξτε τον προτιμώμενο κωδικοποιητή A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Επιλογή κωδικοποιητή ήχου Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Ρυθμός δειγματοληψίας ήχου Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Επιλέξτε τον προτιμώμενο ρυθμό δειγματοληψίας του κωδικοποιητή A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Επιλογή κωδικοποιητή ήχου Bluetooth:\nΠοσοστό δείγματος"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bit ανά δείγμα ήχου Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Επιλέξτε τα προτιμώμενα bit ανά δείγμα για τον κωδικοποιητή A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Επιλογή κωδικοποιητή ήχου Bluetooth:\nBit ανά δείγμα"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Λειτουργία καναλιού ήχου Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Επιλέξτε την προτιμώμενη λειτουργία καναλιού κωδικοποιητή A2DP Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Ποιότητα αναπαραγωγής LDAC ήχου Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Επιλέξτε την προτιμώμενη ποιότητα αναπαραγωγής LDAC του κωδικοποιητή A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Επιλογή κωδικοποιητή ήχου Bluetooth:\nΛειτουργία καναλιού"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Κωδικοποιητής LDAC ήχου Bluetooth: Ποιότητα αναπαραγωγής"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Επιλογή κωδικοποιητή LDAC ήχου Bluetooth:\nΠοιότητα αναπαραγωγής"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Ροή: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Εμφάνιση επιλογών για πιστοποίηση ασύρματης οθόνης"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Αύξηση επιπέδου καταγ. Wi-Fi, εμφάνιση ανά SSID RSSI στο εργαλείο επιλογής Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Όταν είναι ενεργό, το Wi-Fi θα μεταβιβάζει πιο επιθετικά τη σύνδ.δεδομένων σε δίκτυο κινητής τηλ., όταν το σήμα Wi-Fi είναι χαμηλό"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Βοήθεια και σχόλια"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Μενού"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Εισαγάγετε κωδικό πρόσβασης για επαναφορά εργοστασιακών ρυθμίσεων στη λειτουργία επίδειξης"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Επόμενο"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Απαιτείται κωδικός πρόσβασης"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
index 5242be3..4758019 100644
--- a/packages/SettingsLib/res/values-en-rAU/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Use HDCP checking for DRM content only"</item>
     <item msgid="45075631231212732">"Always use HDCP checking"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Default"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Default"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Default"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Default"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Default"</item>
-    <item msgid="5618929009984956469">"16 bits/sample"</item>
-    <item msgid="3412640499234627248">"24 bits/sample"</item>
-    <item msgid="121583001492929387">"32 bits/sample"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Default"</item>
-    <item msgid="4726688794884191540">"16 bits/sample"</item>
-    <item msgid="305344756485516870">"24 bits/sample"</item>
-    <item msgid="244568657919675099">"32 bits/sample"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Default"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Default"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Sound quality preferred (990 Kbps/909 Kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660 Kbps/606 Kbps)"</item>
-    <item msgid="4777177307869441982">"Connection preferred (330 Kbps/303 Kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Sound quality preferred (990 Kbps/909 Kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660 Kbps/606 Kbps)"</item>
-    <item msgid="3367904477834831032">"Connection preferred (330 Kbps/303 Kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Off"</item>
     <item msgid="1593289376502312923">"64 K"</item>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index f09206d..af9895e 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobile data always active"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Disable absolute volume"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Select Preferred Bluetooth A2DP Codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth Audio Sample Rate"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Select Preferred Bluetooth A2DP Codec Sample Rate"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth Audio Bits Per Sample"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Select Preferred Bluetooth A2DP Codec Bits Per Sample"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth Audio Channel Mode"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Select Preferred Bluetooth A2DP Codec Channel Mode"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth Audio LDAC Playback Quality"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Select Preferred Bluetooth A2DP Codec LDAC Playback Quality"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to Mobile, when Wi‑Fi signal is low"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Help &amp; feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Next"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Password required"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
index 5242be3..4758019 100644
--- a/packages/SettingsLib/res/values-en-rGB/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Use HDCP checking for DRM content only"</item>
     <item msgid="45075631231212732">"Always use HDCP checking"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Default"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Default"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Default"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Default"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Default"</item>
-    <item msgid="5618929009984956469">"16 bits/sample"</item>
-    <item msgid="3412640499234627248">"24 bits/sample"</item>
-    <item msgid="121583001492929387">"32 bits/sample"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Default"</item>
-    <item msgid="4726688794884191540">"16 bits/sample"</item>
-    <item msgid="305344756485516870">"24 bits/sample"</item>
-    <item msgid="244568657919675099">"32 bits/sample"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Default"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Default"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Sound quality preferred (990 Kbps/909 Kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660 Kbps/606 Kbps)"</item>
-    <item msgid="4777177307869441982">"Connection preferred (330 Kbps/303 Kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Sound quality preferred (990 Kbps/909 Kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660 Kbps/606 Kbps)"</item>
-    <item msgid="3367904477834831032">"Connection preferred (330 Kbps/303 Kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Off"</item>
     <item msgid="1593289376502312923">"64 K"</item>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index f09206d..af9895e 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobile data always active"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Disable absolute volume"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Select Preferred Bluetooth A2DP Codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth Audio Sample Rate"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Select Preferred Bluetooth A2DP Codec Sample Rate"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth Audio Bits Per Sample"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Select Preferred Bluetooth A2DP Codec Bits Per Sample"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth Audio Channel Mode"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Select Preferred Bluetooth A2DP Codec Channel Mode"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth Audio LDAC Playback Quality"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Select Preferred Bluetooth A2DP Codec LDAC Playback Quality"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to Mobile, when Wi‑Fi signal is low"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Help &amp; feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Next"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Password required"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
index 5242be3..4758019 100644
--- a/packages/SettingsLib/res/values-en-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Use HDCP checking for DRM content only"</item>
     <item msgid="45075631231212732">"Always use HDCP checking"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Default"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Default"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Default"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Default"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Default"</item>
-    <item msgid="5618929009984956469">"16 bits/sample"</item>
-    <item msgid="3412640499234627248">"24 bits/sample"</item>
-    <item msgid="121583001492929387">"32 bits/sample"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Default"</item>
-    <item msgid="4726688794884191540">"16 bits/sample"</item>
-    <item msgid="305344756485516870">"24 bits/sample"</item>
-    <item msgid="244568657919675099">"32 bits/sample"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Default"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Default"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Sound quality preferred (990 Kbps/909 Kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660 Kbps/606 Kbps)"</item>
-    <item msgid="4777177307869441982">"Connection preferred (330 Kbps/303 Kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Sound quality preferred (990 Kbps/909 Kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660 Kbps/606 Kbps)"</item>
-    <item msgid="3367904477834831032">"Connection preferred (330 Kbps/303 Kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Off"</item>
     <item msgid="1593289376502312923">"64 K"</item>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index f09206d..af9895e 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobile data always active"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Disable absolute volume"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Select Preferred Bluetooth A2DP Codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth Audio Sample Rate"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Select Preferred Bluetooth A2DP Codec Sample Rate"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth Audio Bits Per Sample"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Select Preferred Bluetooth A2DP Codec Bits Per Sample"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth Audio Channel Mode"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Select Preferred Bluetooth A2DP Codec Channel Mode"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth Audio LDAC Playback Quality"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Select Preferred Bluetooth A2DP Codec LDAC Playback Quality"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to Mobile, when Wi‑Fi signal is low"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Help &amp; feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Next"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Password required"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index d574edd..09480a5 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Siempre utilizar comprobación HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Predeterminado"</item>
+    <item msgid="7065842274271279580">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Predeterminado"</item>
+    <item msgid="5062108632402595000">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Predeterminado"</item>
+    <item msgid="3093023430402746802">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Predeterminado"</item>
+    <item msgid="3214516120190965356">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Predeterminado"</item>
+    <item msgid="2684127272582591429">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="5618929009984956469">"16 bits/muestra"</item>
     <item msgid="3412640499234627248">"24 bits/muestra"</item>
     <item msgid="121583001492929387">"32 bits/muestra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Predeterminado"</item>
+    <item msgid="1081159789834584363">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="4726688794884191540">"16 bits/muestra"</item>
     <item msgid="305344756485516870">"24 bits/muestra"</item>
     <item msgid="244568657919675099">"32 bits/muestra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Predeterminado"</item>
+    <item msgid="5226878858503393706">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Predeterminado"</item>
+    <item msgid="4118561796005528173">"Usar selección del sistema (predeterminado)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Calidad de sonido (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Estándar (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Conexión preferida (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Optimizar para calidad de audio (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Calidad de audio y conexión equilibrada (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimizar para calidad de conexión (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Calidad de sonido (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Estándar (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Conexión preferida (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Optimizar para la calidad de audio"</item>
+    <item msgid="4327143584633311908">"Calidad de audio y conexión equilibrada"</item>
+    <item msgid="6155648878105378550">"Optimizar para calidad de conexión"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Desactivado"</item>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index b8b8ce4..25ee5a8 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Datos móviles siempre activos"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Inhabilitar volumen absoluto"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Códec del audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Selecciona el códec A2DP de Bluetooth preferido"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Frecuencia de la muestra del audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Selecciona la frecuencia de la muestra preferida del códec A2DP de Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Seleccionar códec del audio Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Frecuencia de muestreo del audio Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Seleccionar códec del audio Bluetooth:\nVelocidad de la muestra"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits por muestra del audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Selecciona los bits por muestra del códec A2DP de Bluetooth preferidos"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Seleccionar códec del audio Bluetooth:\nBits por muestra"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modo de canal del audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Selecciona el modo de canal del códec A2DP de Bluetooth preferido"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Calidad de reproducción LDAC del audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Selecciona la calidad de reproducción LDAC preferida del códec A2DP del audio Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Seleccionar códec del audio Bluetooth:\nModo de canal"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Códec del audio Bluetooth LDAC: calidad de reproducción"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Seleccionar códec del audio Bluetooth LDAC:\nCalidad de reproducción"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Transmitiendo: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones de certificación de pantalla inalámbrica"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar nivel de registro Wi-Fi; mostrar por SSID RSSI en el selector de Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Si está habilitada, la conexión Wi‑Fi será más intensa al transferir la conexión de datos al celular (si la señal Wi‑Fi es débil)."</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ayuda y comentarios"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Ingresa contraseña y restablece en demo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Siguiente"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Contraseña obligatoria"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index 1cfbe7e..b5160a4 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Utilizar siempre comprobación de HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Predeterminado"</item>
+    <item msgid="7065842274271279580">"Usar preferencia del sistema (predeter.)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Predeterminado"</item>
+    <item msgid="5062108632402595000">"Usar preferencia del sistema (predeter.)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Predeterminado"</item>
+    <item msgid="3093023430402746802">"Usar preferencia del sistema (predeter.)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Predeterminado"</item>
+    <item msgid="3214516120190965356">"Usar preferencia del sistema (predeter.)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Predeterminado"</item>
+    <item msgid="2684127272582591429">"Usar preferencia del sistema (predeter.)"</item>
     <item msgid="5618929009984956469">"16 bits por muestra"</item>
     <item msgid="3412640499234627248">"24 bits por muestra"</item>
     <item msgid="121583001492929387">"32 bits por muestra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Predeterminado"</item>
+    <item msgid="1081159789834584363">"Usar preferencia del sistema (predeter.)"</item>
     <item msgid="4726688794884191540">"16 bits por muestra"</item>
     <item msgid="305344756485516870">"24 bits por muestra"</item>
     <item msgid="244568657919675099">"32 bits por muestra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Predeterminado"</item>
+    <item msgid="5226878858503393706">"Usar preferencia del sistema (predeter.)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Predeterminado"</item>
+    <item msgid="4118561796005528173">"Usar preferencia del sistema (predeter.)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Calidad son. pref. (990 kbps / 909 kbps)"</item>
-    <item msgid="138837449700903545">"Estándar (660 kbps / 606 kbps)"</item>
-    <item msgid="4777177307869441982">"Conexión preferida (330 kbps / 303 kbps)"</item>
+    <item msgid="3411577996960199959">"Optimizar la calidad del audio (990/909&amp;nbsp;kbps)"</item>
+    <item msgid="2921767058740704969">"Equilibrar la calidad del audio y de la conexión (660/606&amp;nbsp;kbps)"</item>
+    <item msgid="3682554248829489641">"Optimizar la calidad de la conexión (330/303&amp;nbsp;kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Calidad son. pref. (990 kbps / 909 kbps)"</item>
-    <item msgid="9091111147684472529">"Estándar (660 kbps / 606 kbps)"</item>
-    <item msgid="3367904477834831032">"Conexión preferida (330 kbps / 303 kbps)"</item>
+    <item msgid="7668834469173465015">"Optimizar la calidad del audio"</item>
+    <item msgid="4327143584633311908">"Equilibrar la calidad del audio y la de la conexión"</item>
+    <item msgid="6155648878105378550">"Optimizar la calidad de la conexión"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"No"</item>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index e4c9ba9..9d98096 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Datos móviles siempre activos"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Inhabilitar volumen absoluto"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Códec de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Selecciona el códec A2DP de Bluetooth preferido"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Selecciona el códec de audio por Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Porcentaje de muestreo de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Selecciona el porcentaje de muestreo del códec A2DP de Bluetooth preferido"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Selecciona el códec de audio por Bluetooth:\nFrecuencia de muestreo"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits de audio por Bluetooth por muestra"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Selecciona los bits del códec A2DP de Bluetooth preferidos por muestra"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Selecciona el códec de audio por Bluetooth:\nBits por muestra"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modo de canal de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Selecciona el modo de canal del códec A2DP de Bluetooth preferido"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Calidad de la reproducción LDAC de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Selecciona la calidad de la reproducción LDAC del códec A2DP de Bluetooth preferida"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Selecciona el códec de audio por Bluetooth:\nModo de canal"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Selecciona el códec LDAC por Bluetooth: calidad de reproducción"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selecciona el códec LDAC de audio por Bluetooth:\nCalidad de reproducción"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones para la certificación de la pantalla inalámbrica"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar el nivel de logging de Wi-Fi, mostrar por SSID RSSI en el selector Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Si está habilitada, la conexión Wi‑Fi será más agresiva al transferir la conexión de datos al móvil (si la señal Wi‑Fi no es estable)"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ayuda y sugerencias"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Escribe una contraseña para restablecer datos de fábrica en modo demostración"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Siguiente"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Contraseña obligatoria"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml
index ba8e8de..f26543e 100644
--- a/packages/SettingsLib/res/values-et/arrays.xml
+++ b/packages/SettingsLib/res/values-et/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Kasuta alati HDCP-kontrollimist"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Vaikeseade"</item>
+    <item msgid="7065842274271279580">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Vaikeseade"</item>
+    <item msgid="5062108632402595000">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Vaikeseade"</item>
+    <item msgid="3093023430402746802">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Vaikeseade"</item>
+    <item msgid="3214516120190965356">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Vaikeseade"</item>
+    <item msgid="2684127272582591429">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="5618929009984956469">"16 bitti diskreedi kohta"</item>
     <item msgid="3412640499234627248">"24 bitti diskreedi kohta"</item>
     <item msgid="121583001492929387">"32 bitti diskreedi kohta"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Vaikeseade"</item>
+    <item msgid="1081159789834584363">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="4726688794884191540">"16 bitti diskreedi kohta"</item>
     <item msgid="305344756485516870">"24 bitti diskreedi kohta"</item>
     <item msgid="244568657919675099">"32 bitti diskreedi kohta"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Vaikeseade"</item>
+    <item msgid="5226878858503393706">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Vaikeseade"</item>
+    <item msgid="4118561796005528173">"Süsteemi valiku kasutamine (vaikeseade)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Eelist. helikvaliteeti (990/909 kbit/s)"</item>
-    <item msgid="138837449700903545">"Standardne (660/606 kbit/s)"</item>
-    <item msgid="4777177307869441982">"Eelistatakse ühendust (330/303 kbit/s)"</item>
+    <item msgid="3411577996960199959">"Helikvaliteedi jaoks optimeerimine (990/909 kbit/s)"</item>
+    <item msgid="2921767058740704969">"Tasakaalustatud heli- ja ühenduskvaliteet (660/606 kbit/s)"</item>
+    <item msgid="3682554248829489641">"Ühenduskvaliteedi jaoks optimeerimine (330/303 kbit/s)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Eelist. helikvaliteeti (990/909 kbit/s)"</item>
-    <item msgid="9091111147684472529">"Standardne (660/606 kbit/s)"</item>
-    <item msgid="3367904477834831032">"Eelistatakse ühendust (330/303 kbit/s)"</item>
+    <item msgid="7668834469173465015">"Helikvaliteedi jaoks optimeerimine"</item>
+    <item msgid="4327143584633311908">"Tasakaalustatud heli- ja ühenduskvaliteet"</item>
+    <item msgid="6155648878105378550">"Ühenduskvaliteedi jaoks optimeerimine"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Väljas"</item>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index a7b066a..507bd20 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobiilne andmeside on alati aktiivne"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Keela absoluutne helitugevus"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetoothi heli kodek"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Eelistatud Bluetooth A2DP kodeki valimine"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Valige Bluetoothi helikodek"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetoothi heli diskreetimissagedus"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Bluetooth A2DP kodeki eelistatud diskreetimissageduse valimine"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Valige Bluetoothi helikodek:\ndiskreetimissagedus"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetoothi heli bitte diskreedi kohta"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Bluetooth A2DP kodeki eelistatud bittide arvu valimine diskreedi kohta"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Valige Bluetoothi helikodek:\nbitte diskreetimise kohta"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetoothi heli kanalirežiim"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Bluetooth A2DP kodeki eelistatud kanalirežiimi valimine"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetoothi heli LDAC esituskvaliteet"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Bluetooth A2DP kodeki eelistatud LDAC esituskvaliteedi valimine"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Valige Bluetoothi helikodek:\nkanalirežiim"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetoothi LDAC-helikodek: taasesituskvaliteet"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Valige Bluetoothi LDAC-helikodek:\ntaasesituskvaliteet"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Voogesitus: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Juhtmeta ekraaniühenduse sertifitseerimisvalikute kuvamine"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Suurenda WiFi logimistaset, kuva WiFi valijas SSID RSSI järgi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Kui see on lubatud, siis püüab WiFi nõrga WiFi-signaali korral agressiivsemalt anda andmeside ühenduse üle mobiilsele andmesidele"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Abi ja tagasiside"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menüü"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Sisestage parool, et demorežiimis tehaseseadetele lähtestada"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Järgmine"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Parool on kohustuslik"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index 222aba0..4c4e7e7 100644
--- a/packages/SettingsLib/res/values-eu/arrays.xml
+++ b/packages/SettingsLib/res/values-eu/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Erabili beti HDCP egiaztapena"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Lehenetsia"</item>
+    <item msgid="7065842274271279580">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Lehenetsia"</item>
+    <item msgid="5062108632402595000">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Lehenetsia"</item>
+    <item msgid="3093023430402746802">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Lehenetsia"</item>
+    <item msgid="3214516120190965356">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Lehenetsia"</item>
+    <item msgid="2684127272582591429">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="5618929009984956469">"16 bit lagin bakoitzeko"</item>
     <item msgid="3412640499234627248">"24 bit lagin bakoitzeko"</item>
     <item msgid="121583001492929387">"32 bit lagin bakoitzeko"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Lehenetsia"</item>
+    <item msgid="1081159789834584363">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="4726688794884191540">"16 bit lagin bakoitzeko"</item>
     <item msgid="305344756485516870">"24 bit lagin bakoitzeko"</item>
     <item msgid="244568657919675099">"32 bit lagin bakoitzeko"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Lehenetsia"</item>
+    <item msgid="5226878858503393706">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="4106832974775067314">"Monoa"</item>
     <item msgid="5571632958424639155">"Estereoa"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Lehenetsia"</item>
+    <item msgid="4118561796005528173">"Erabili sistema-hautapena (lehenetsia)"</item>
     <item msgid="8900559293912978337">"Monoa"</item>
     <item msgid="8883739882299884241">"Estereoa"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Soinu-kalitate hobetsia (990 kb/s edo 909 kb/s)"</item>
-    <item msgid="138837449700903545">"Arrunta (660 kb/s edo 606 kb/s)"</item>
-    <item msgid="4777177307869441982">"Konexio hobetsia (330 kb/s edo 303 kb/s)"</item>
+    <item msgid="3411577996960199959">"Optimizatu audioaren kalitatea areagotzeko (990 kbps / 909kbps)"</item>
+    <item msgid="2921767058740704969">"Orekatu audioaren eta konexioaren kalitateak (660 kbps / 606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimizatu konexioaren kalitatea areagotzeko (330 kbps / 303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Soinu-kalitate hobetsia (990 kb/s edo 909 kb/s)"</item>
-    <item msgid="9091111147684472529">"Arrunta (660 kb/s edo 606 kb/s)"</item>
-    <item msgid="3367904477834831032">"Konexio hobetsia (330 kb/s edo 303 kb/s)"</item>
+    <item msgid="7668834469173465015">"Optimizatu audioaren kalitatea areagotzeko"</item>
+    <item msgid="4327143584633311908">"Orekatu audioaren eta konexioaren kalitateak"</item>
+    <item msgid="6155648878105378550">"Optimizatu konexioaren kalitatea areagotzeko"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Desaktibatuta"</item>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 514d136..d8b2496 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mugikorreko datuak beti aktibo"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desgaitu bolumen absolutua"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth bidezko audioaren kodeka"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Hautatu Bluetooth A2DP kodek hobetsia"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Hautatu Bluetooth audioaren kodeka"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth bidezko audioaren lagin-abiadura"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Hautatu Bluetooth A2DP kodekaren lagin-abiadura hobetsia"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Hautatu Bluetooth audioaren LDAC kodeka:\nlaginaren abiadura"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth bidezko audioaren lagin bakoitzeko bit kopurua"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Hautatu Bluetooth A2DP kodekaren lagin bakoitzeko bit kopuru hobetsia"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Hautatu Bluetooth audioaren kodeka:\nlagin bakoitzeko bitak"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth bidezko audioaren kanalaren modua"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Hautatu Bluetooth A2DP kodekaren kanalaren modu hobetsia"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth bidezko audioaren LDAC erreprodukzioaren kalitatea"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Hautatu Bluetooth A2DP kodekaren LDAC erreprodukzioaren kalitate hobetsia"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Hautatu Bluetooth audioaren kodeka:\nkanal modua"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth audioaren LDAC kodeka: erreprodukzioaren kalitatea"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Hautatu Bluetooth audioaren LDAC kodeka:\nerreprodukzioaren kalitatea"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Igortzean: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Erakutsi hari gabeko bistaratze-egiaztapenaren aukerak"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Erakutsi datu gehiago Wi-Fi sareetan saioa hasterakoan. Erakutsi sarearen identifikatzailea eta seinalearen indarra Wi‑Fi sareen hautagailuan."</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Aukera hori gaituz gero, gailua errazago aldatuko da datu mugikorren konexiora Wi-Fi seinalea ahultzen dela nabaritutakoan"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Laguntza eta iritziak"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menua"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Idatzi pasahitza jatorrizko ezarpenak demo moduan berrezartzeko"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Hurrengoa"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Pasahitza behar da"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
index 1ff0d25..293639c 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"‏استفاده از بررسی HDCP فقط برای محتوای DRM"</item>
     <item msgid="45075631231212732">"‏همیشه از بررسی HDCP استفاده شود"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"پیش‌فرض"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"پیش‌فرض"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"پیش‌فرض"</item>
-    <item msgid="8895532488906185219">"۴۴٫۱ کیلوهرتز"</item>
-    <item msgid="2909915718994807056">"۴۸٫۰ کیلوهرتز"</item>
-    <item msgid="3347287377354164611">"۸۸٫۲ کیلوهرتز"</item>
-    <item msgid="1234212100239985373">"۹۶٫۰ کیلوهرتز"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"پیش‌فرض"</item>
-    <item msgid="4482862757811638365">"۴۴٫۱ کیلوهرتز"</item>
-    <item msgid="354495328188724404">"۴۸٫۰ کیلوهرتز"</item>
-    <item msgid="7329816882213695083">"۸۸٫۲ کیلوهرتز"</item>
-    <item msgid="6967397666254430476">"۹۶٫۰ کیلوهرتز"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"پیش‌فرض"</item>
-    <item msgid="5618929009984956469">"۱۶ بیت در هر نمونه"</item>
-    <item msgid="3412640499234627248">"۲۴ بیت در هر نمونه"</item>
-    <item msgid="121583001492929387">"۳۲ بیت در هر نمونه"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"پیش‌فرض"</item>
-    <item msgid="4726688794884191540">"۱۶ بیت در هر نمونه"</item>
-    <item msgid="305344756485516870">"۲۴ بیت در هر نمونه"</item>
-    <item msgid="244568657919675099">"۳۲ بیت در هر نمونه"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"پیش‌فرض"</item>
-    <item msgid="4106832974775067314">"مونو"</item>
-    <item msgid="5571632958424639155">"استریو"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"پیش‌فرض"</item>
-    <item msgid="8900559293912978337">"مونو"</item>
-    <item msgid="8883739882299884241">"استریو"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"کیفیت صدای ترجیحی (۹۹۰کیلوبیت در ثانیه/۹۰۹کیلوبیت در ثانیه)"</item>
-    <item msgid="138837449700903545">"استاندارد (۶۶۰کیلوبیت در ثانیه/۶۰۶کیلوبیت در ثانیه)"</item>
-    <item msgid="4777177307869441982">"اتصال ترجیحی (۳۳۰کیلوبیت در ثانیه/۳۰۳کیلوبیت در ثانیه)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"کیفیت صدای ترجیحی (۹۹۰کیلوبیت در ثانیه/۹۰۹کیلوبیت در ثانیه)"</item>
-    <item msgid="9091111147684472529">"استاندارد (۶۶۰کیلوبیت در ثانیه/۶۰۶کیلوبیت در ثانیه)"</item>
-    <item msgid="3367904477834831032">"اتصال ترجیحی (۳۳۰کیلوبیت در ثانیه/۳۰۳کیلوبیت در ثانیه)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"خاموش"</item>
     <item msgid="1593289376502312923">"۶۴ هزار"</item>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index d6518e1..61ec98e 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"داده سلولی همیشه فعال"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"غیرفعال کردن میزان صدای مطلق"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"کدک بلوتوث صوتی"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"‏انتخاب کدک‌ A2DP در بلوتوث ترجیحی"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"سرعت نمونه بلوتوث صوتی"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"‏انتخاب سرعت نمونه کدک‌ A2DP بلوتوث ترجیحی"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"بیت‌های بلوتوث صوتی در هر نمونه"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"‏انتخاب بیت‌های کدک‌ A2DP بلوتوث ترجیحی هر نمونه"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"حالت کانال بلوتوث‌ صوتی"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"‏انتخاب حالت کانال کدک‌ A2DP بلوتوث ترجیحی"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"‏کیفیت پخش LDAC بلوتوث صوتی"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"‏انتخاب کیفیت پخش LDAC کدک‌ A2DP بلوتوث ترجیحی"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"نمایش گزینه‌ها برای گواهینامه نمایش بی‌سیم"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‏افزایش سطح گزارش‌گیری Wi‑Fi، نمایش به ازای SSID RSSI در انتخاب‌کننده Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"‏وقتی فعال است، در شرایط پایین بودن سیگنال، Wi‑Fi برای واگذار کردن اتصال داده به شبکه سلولی فعال‌تر خواهد بود."</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"راهنما و بازخورد"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"منو"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"برای انجام بازنشانی کارخانه‌ای در حالت نمایشی، گذرواژه را وارد کنید"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"بعدی"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"وارد کردن گذرواژه الزامی است"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index 5d842a4..2808bf2 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Käytä HDCP-tarkistusta vain DRM-suojatulle sisällölle"</item>
     <item msgid="45075631231212732">"Käytä aina HDCP-tarkistusta"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Oletus"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Oletus"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Oletus"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Oletus"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Oletus"</item>
-    <item msgid="5618929009984956469">"16 bittiä/näyte"</item>
-    <item msgid="3412640499234627248">"24 bittiä/näyte"</item>
-    <item msgid="121583001492929387">"32 bittiä/näyte"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Oletus"</item>
-    <item msgid="4726688794884191540">"16 bittiä/näyte"</item>
-    <item msgid="305344756485516870">"24 bittiä/näyte"</item>
-    <item msgid="244568657919675099">"32 bittiä/näyte"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Oletus"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Oletus"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Äänenlaatu etusijalla (990 kbps / 909 kbps)"</item>
-    <item msgid="138837449700903545">"Vakio (660 kbps / 606 kbps)"</item>
-    <item msgid="4777177307869441982">"Yhteys etusijalla (330 kbps / 303 kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Äänenlaatu etusijalla (990 kbps / 909 kbps)"</item>
-    <item msgid="9091111147684472529">"Vakio (660 kbps / 606 kbps)"</item>
-    <item msgid="3367904477834831032">"Yhteys etusijalla (330 kbps / 303 kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Ei käytössä"</item>
     <item msgid="1593289376502312923">"64 kt"</item>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 2332fc5..a954b17 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobiilidata on aina käytössä"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Poista yleinen äänenvoimakkuuden säätö käytöstä"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth-äänen koodekki"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Valitse ensisijainen Bluetooth A2DP ‑koodekki"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth-ääninäytteen siirtonopeus"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Valitse ensisijainen Bluetooth A2DP ‑koodekkinäytteen siirtonopeus"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth-äänen bittiä/näyte-arvo"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Valitse ensisijainen Bluetooth A2DP ‑koodekin bittiä/näyte-arvo"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth-äänen kanavatila"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Valitse ensisijainen Bluetooth A2DP ‑koodekin kanavatila"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth-äänen LDAC-toiston laatu"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Valitse ensisijainen Bluetooth A2DP ‑koodekin LDAC-toiston laatu"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Näytä langattoman näytön sertifiointiin liittyvät asetukset"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Lisää Wi‑Fin lokikirjaustasoa, näytä SSID RSSI -kohtaisesti Wi‑Fi-valitsimessa."</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Kun asetus on käytössä, Wi-Fi siirtää datayhteyden aggressiivisemmin matkapuhelinverkolle, jos Wi-Fi-signaali on heikko."</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ohje ja palaute"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Valikko"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Palauta tehdasasetukset antamalla salasana"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Seuraava"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Salasana vaaditaan"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index c50e576..7c403bf 100644
--- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Utiliser la vérification HDCP uniquement pour le contenu GDN"</item>
     <item msgid="45075631231212732">"Toujours utiliser la vérification HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Par défaut"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Par défaut"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Par défaut"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Par défaut"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Par défaut"</item>
-    <item msgid="5618929009984956469">"16 bits par échantillon"</item>
-    <item msgid="3412640499234627248">"24 bits par échantillon"</item>
-    <item msgid="121583001492929387">"32 bits par échantillon"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Par défaut"</item>
-    <item msgid="4726688794884191540">"16 bits par échantillon"</item>
-    <item msgid="305344756485516870">"24 bits par échantillon"</item>
-    <item msgid="244568657919675099">"32 bits par échantillon"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Par défaut"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stéréo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Par défaut"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stéréo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Qualité sonore préférée (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Connexion préférée (330 kbps/303 kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Qualité sonore préférée (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Connexion préférée (330 kbps/303 kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Désactivé"</item>
     <item msgid="1593289376502312923">"64 ko"</item>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 9eeeccb7c..973458d 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Données cellulaires toujours actives"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Désactiver le volume absolu"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Sélectionnez le codec Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Taux d\'échantillonnage pour l\'audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Sélectionnez le taux d\'échantillonnage préféré pour le codec Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits par échantillon pour l\'audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Sélectionnez le nombre de bits par échantillon préféré pour le codec Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Mode de canal pour l\'audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Sélectionnez le mode de canal préféré pour le codec Bluetooth A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Qualité de lecture LDAC pour l\'audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Sélectionnez la qualité de lecture LDAC préférée pour le codec Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification d\'affichage sans fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler davantage les données Wi-Fi, afficher par SSID RSSI dans sélect. Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Si cette option est activée, le passage du Wi-Fi aux données cellulaires est forcé lorsque le signal Wi-Fi est faible"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Aide et commentaires"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Entrez m. passe pour réinit. en mode démo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Suivant"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Mot de passe obligatoire"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml
index cda881c..c63275f 100644
--- a/packages/SettingsLib/res/values-fr/arrays.xml
+++ b/packages/SettingsLib/res/values-fr/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Utiliser la vérification HDCP uniquement pour le contenu DRM"</item>
     <item msgid="45075631231212732">"Toujours utiliser la vérification HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Par défaut"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Par défaut"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Par défaut"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Par défaut"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Par défaut"</item>
-    <item msgid="5618929009984956469">"16 bits par échantillon"</item>
-    <item msgid="3412640499234627248">"24 bits par échantillon"</item>
-    <item msgid="121583001492929387">"32 bits par échantillon"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Par défaut"</item>
-    <item msgid="4726688794884191540">"16 bits par échantillon"</item>
-    <item msgid="305344756485516870">"24 bits par échantillon"</item>
-    <item msgid="244568657919675099">"32 bits par échantillon"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Par défaut"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stéréo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Par défaut"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stéréo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Qualité audio prioritaire (990/909 kbit/s)"</item>
-    <item msgid="138837449700903545">"Standard (660/606 kbit/s)"</item>
-    <item msgid="4777177307869441982">"Connexion prioritaire (330/303 kbits/s)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Qualité audio prioritaire (990/909 kbit/s)"</item>
-    <item msgid="9091111147684472529">"Standard (660/606 kbit/s)"</item>
-    <item msgid="3367904477834831032">"Connexion prioritaire (330/303 kbits/s)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Désactivé"</item>
     <item msgid="1593289376502312923">"64 Ko"</item>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 8ba0b0f..9ce3d5a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Données mobiles toujours actives"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Désactiver le volume absolu"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Sélectionner le codec A2DP Bluetooth prioritaire"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Taux d\'échantillonnage audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Sélectionner le taux d\'échantillonnage pour le codec A2DP Bluetooth prioritaire"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Nombre de bits par échantillon pour l\'audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Sélectionner le nombre de bits par échantillon pour le codec A2DP Bluetooth prioritaire"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Mode de chaîne de l\'audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Sélectionner le mode de chaîne pour le codec A2DP Bluetooth prioritaire"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Qualité de lecture LDAC de l\'audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Sélectionner la qualité de lecture LDAC pour le codec A2DP Bluetooth prioritaire"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options de la certification de l\'affichage sans fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler plus infos Wi-Fi, afficher par RSSI de SSID dans outil sélection Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Si cette option est activée, le passage du Wi-Fi aux données mobiles est forcé en cas de signal Wi-Fi faible."</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Aide et commentaires"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Saisir mot de passe pour rétablir conf. d\'usine en mode démo."</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Suivant"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Veuillez saisir le mot de passe"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml
index e581368..2a89199 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Utiliza a comprobación HDCP só para contido DRM"</item>
     <item msgid="45075631231212732">"Utilizar sempre a comprobación HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Predeterminado"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Predeterminado"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Predeterminado"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Predeterminado"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Predeterminado"</item>
-    <item msgid="5618929009984956469">"16 bits/mostra"</item>
-    <item msgid="3412640499234627248">"24 bits/mostra"</item>
-    <item msgid="121583001492929387">"32 bits/mostra"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Predeterminado"</item>
-    <item msgid="4726688794884191540">"16 bits/mostra"</item>
-    <item msgid="305344756485516870">"24 bits/mostra"</item>
-    <item msgid="244568657919675099">"32 bits/mostra"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Predeterminado"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Estéreo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Predeterminado"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Estéreo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Calidade preferida (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Estándar (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Conexión preferida (330 kbps/303 kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Calidade preferida (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Estándar (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Conexión preferida (330 kbps/303 kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Desactivado"</item>
     <item msgid="1593289376502312923">"64 K"</item>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 6a9a1ec..dfdb851 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Datos móbiles sempre activados"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desactivar volume absoluto"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Códec de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Seleccionar códec A2DP de Bluetooth preferido"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Taxa de mostraxe de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Seleccionar taxa de mostraxe do códec A2DP de Bluetooth preferido"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits por mostra de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Seleccionar bits por mostra do códec A2DP de Bluetooth preferido"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modo de canle de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Seleccionar modo de canle do códec A2DP de Bluetooth preferido"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Calidade de reprodución de LDAC de audio por Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Seleccionar calidade de reprodución de LDAC do códec A2DP de Bluetooth preferido"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opcións para o certificado de visualización sen fíos"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nivel de rexistro da wifi, mostrar por SSID RSSI no selector de wifi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Cando está activada esta función, a wifi será máis agresiva ao entregar a conexión de datos ao móbil, cando o sinal wifi é feble"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Axuda e suxestións"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Insire contrasinal para restablec. en demostración"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Seguinte"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"O contrasinal é obrigatorio"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml
index 742103b..e00c29f 100644
--- a/packages/SettingsLib/res/values-gu/arrays.xml
+++ b/packages/SettingsLib/res/values-gu/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"હંમેશા HDCP તપાસનો ઉપયોગ કરો"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ડિફૉલ્ટ"</item>
+    <item msgid="7065842274271279580">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ડિફૉલ્ટ"</item>
+    <item msgid="5062108632402595000">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ડિફૉલ્ટ"</item>
+    <item msgid="3093023430402746802">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ડિફૉલ્ટ"</item>
+    <item msgid="3214516120190965356">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ડિફૉલ્ટ"</item>
+    <item msgid="2684127272582591429">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="5618929009984956469">"16 બિટ/નમૂનો"</item>
     <item msgid="3412640499234627248">"24 બિટ/નમૂનો"</item>
     <item msgid="121583001492929387">"32 બિટ/નમૂનો"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ડિફૉલ્ટ"</item>
+    <item msgid="1081159789834584363">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="4726688794884191540">"16 બિટ/નમૂનો"</item>
     <item msgid="305344756485516870">"24 બિટ/નમૂનો"</item>
     <item msgid="244568657919675099">"32 બિટ/નમૂનો"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ડિફૉલ્ટ"</item>
+    <item msgid="5226878858503393706">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="4106832974775067314">"મૉનો"</item>
     <item msgid="5571632958424639155">"સ્ટીરિઓ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ડિફૉલ્ટ"</item>
+    <item msgid="4118561796005528173">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
     <item msgid="8900559293912978337">"મૉનો"</item>
     <item msgid="8883739882299884241">"સ્ટીરિઓ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"પસંદગીની અવાજ ગુણવત્તા (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"માનક (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"પસંદગીનું કનેક્શન (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"ઑડિઓની ગુણવત્તા માટે ઑપ્ટિમાઇઝ કરો (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"સંતુલિત ઑડિઓ અને કનેક્શનની ગુણવત્તા (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"કનેક્શનની ગુણવત્તા માટે ઑપ્ટિમાઇઝ કરો (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"પસંદગીની અવાજ ગુણવત્તા (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"માનક (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"પસંદગીનું કનેક્શન (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"ઑડિઓ ગુણવત્તા માટે ઑપ્ટિમાઇઝ કરો"</item>
+    <item msgid="4327143584633311908">"સંતુલિત ઑડિઓ અને કનેક્શનની ગુણવત્તા"</item>
+    <item msgid="6155648878105378550">"કનેક્શનની ગુણવત્તા માટે ઑપ્ટિમાઇઝ કરો"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"બંધ"</item>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index e61e6e2..f6d2327 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"સેલ્યુલર ડેટા હંમેશા સક્રિય"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ચોક્કસ વૉલ્યૂમને અક્ષમ કરો"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth ઑડિઓ કોડેક"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"પસંદગીના Bluetooth A2DP કોડેકને પસંદ કરો"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth ઑડિઓ LDAC કોડેક પસંદ કરો"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth ઑડિઓ નમૂના દર"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"પસંદગીના Bluetooth A2DP કોડેક નમૂના દરને પસંદ કરો"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth ઑડિઓ LDAC કોડેક પસંદ કરો:\nનમૂના દર"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"નમૂના દીઠ Bluetooth ઑડિઓ બિટ"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"નમૂના દીઠ પસંદગીના Bluetooth A2DP કોડેક બિટને પસંદ કરો"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth ઑડિઓ કોડેક પસંદ કરો:\nનમૂના દીઠ બિટ"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth ઑડિઓ ચેનલ મોડ"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"પસંદગીના Bluetooth A2DP કોડેક ચેનલ મોડને પસંદ કરો"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth ઑડિઓ LDAC પ્લેબેક ગુણવત્તા"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"પસંદગીની Bluetooth A2DP કોડેક LDAC પ્લેબેક ગુણવત્તા પસંદ કરો"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth ઑડિઓ કોડેક પસંદ કરો:\nચૅનલ મોડ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth ઑડિઓ LDAC કોડેક: પ્લેબૅક ગુણવત્તા"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth ઑડિઓ LDAC કોડેક પસંદ કરો:\nપ્લેબૅક ગુણવત્તા"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"સ્ટ્રીમિંગ: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"વાયરલેસ ડિસ્પ્લે પ્રમાણપત્ર માટેના વિકલ્પો બતાવો"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi લોગિંગ સ્તર વધારો, Wi‑Fi પીકરમાં SSID RSSI દીઠ બતાવો"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"જ્યારે સક્ષમ હોય, ત્યારે Wi‑Fi સિગ્નલ ઓછા હોવા પર, સેલ્યુલર પર ડેટા કનેક્શન મોકલવામાં વધુ આક્રમક હશે"</string>
@@ -352,4 +353,10 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"સહાય અને પ્રતિસાદ"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"મેનુ"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <!-- no translation found for retail_demo_reset_message (118771671364131297) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_next (8356731459226304963) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_title (696589204029930100) -->
+    <skip />
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 11b83c9..ecf101a 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"हमेशा HDCP जांच का उपयोग करें"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"डिफ़ॉल्ट"</item>
+    <item msgid="7065842274271279580">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"डिफ़ॉल्ट"</item>
+    <item msgid="5062108632402595000">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"डिफ़ॉल्ट"</item>
+    <item msgid="3093023430402746802">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"डिफ़ॉल्ट"</item>
+    <item msgid="3214516120190965356">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"डिफ़ॉल्ट"</item>
+    <item msgid="2684127272582591429">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="5618929009984956469">"16 बिट/नमूना"</item>
     <item msgid="3412640499234627248">"24 बिट/नमूना"</item>
     <item msgid="121583001492929387">"32 बिट/नमूना"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"डिफ़ॉल्ट"</item>
+    <item msgid="1081159789834584363">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="4726688794884191540">"16 बिट/नमूना"</item>
     <item msgid="305344756485516870">"24 बिट/नमूना"</item>
     <item msgid="244568657919675099">"32 बिट/नमूना"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"डिफ़ॉल्ट"</item>
+    <item msgid="5226878858503393706">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="4106832974775067314">"मोनो"</item>
     <item msgid="5571632958424639155">"स्टीरियो"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"डिफ़ॉल्ट"</item>
+    <item msgid="4118561796005528173">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item>
     <item msgid="8900559293912978337">"मोनो"</item>
     <item msgid="8883739882299884241">"स्टीरियो"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"पसंदीदा ध्वनि गुणवत्ता (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"मानक (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"पसंदीदा कनेक्शन (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"ऑडियो गुणवत्ता के अनुसार बदलाव करें (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"संतुलित ऑडियो और कनेक्शन गुणवत्ता (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"कनेक्शन गुणवत्ता के अनुसार बदलाव करें (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"पसंदीदा ध्वनि गुणवत्ता (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"मानक (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"पसंदीदा कनेक्शन (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"ऑडियो गुणवत्ता के अनुसार बदलाव करें"</item>
+    <item msgid="4327143584633311908">"संतुलित ऑडियो और कनेक्शन गुणवत्ता"</item>
+    <item msgid="6155648878105378550">"कनेक्शन गुणवत्ता के अनुसार बदलाव करें"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"बंद"</item>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 7ba3d54..f0cb2e3 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"सेल्युलर डेटा हमेशा सक्रिय"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"पूर्ण वॉल्यूम अक्षम करें"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ब्लूटूथ ऑडियो कोडेक"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"पसंदीदा ब्लूटूथ A2DP कोडेक चुनें"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"ब्लूटूथ ऑडियो कोडेक चुनें"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"ब्लूटूथ ऑडियो नमूना दर"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"पसंदीदा ब्लूटूथ A2DP कोडेक नमूना दर चुनें"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"ब्लूटूथ ऑडियो कोडेक चुनें:\nनमूना दर"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"ब्लूटूथ ऑडियो बिट प्रति नमूना"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"पसंदीदा ब्लूटूथ A2DP कोडेक बिट प्रति नमूना चुनें"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"ब्लूटूथ ऑडियो कोडेक चुनें:\nबिट प्रति नमूना"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ब्लूटूथ ऑडियो चैनल मोड"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"पसंदीदा ब्लूटूथ A2DP कोडेक चैनल मोड चुनें"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"ब्लूटूथ ऑडियो LDAC प्लेबैक गुणवत्ता"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"पसंदीदा ब्लूटूथ A2DP कोडेक LDAC प्लेबैक गुणवत्ता चुनें"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"ब्लूटूथ ऑडियो कोडेक चुनें:\nचैनल मोड"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ब्लूटूथ ऑडियो LDAC कोडेक: प्लेबैक क्वालिटी"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ब्लूटूथ ऑडियो LDAC कोडेक चुनें:\nप्लेबैक क्वालिटी"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"स्ट्रीम हो रहा है: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस दिखाई देने के लिए प्रमाणन विकल्प दिखाएं"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"वाई-फ़ाई प्रवेश स्तर बढ़ाएं, वाई-फ़ाई पिकर में प्रति SSID RSSI दिखाएं"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"इसके सक्षम होने पर, जब वाई-फ़ाई संकेत कमज़ोर हों तो वाई-फ़ाई, डेटा कनेक्शन को सेल्यूलर पर अधिक बलपूर्वक भेजेगा"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"सहायता और फ़ीडबैक"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"मेनू"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"डेमो मोड में फ़ैक्टरी रीसेट के लिए पासवर्ड डालें"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"आगे"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"पासवर्ड आवश्यक"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
index 114337a..2b70c78 100644
--- a/packages/SettingsLib/res/values-hr/arrays.xml
+++ b/packages/SettingsLib/res/values-hr/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Uvijek upotrebljavaj HDCP provjeru"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Zadano"</item>
+    <item msgid="7065842274271279580">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Zadano"</item>
+    <item msgid="5062108632402595000">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Zadano"</item>
+    <item msgid="3093023430402746802">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Zadano"</item>
+    <item msgid="3214516120190965356">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Zadano"</item>
+    <item msgid="2684127272582591429">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="5618929009984956469">"16 bitova po uzorku"</item>
     <item msgid="3412640499234627248">"24 bita po uzorku"</item>
     <item msgid="121583001492929387">"32 bita po uzorku"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Zadano"</item>
+    <item msgid="1081159789834584363">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="4726688794884191540">"16 bitova po uzorku"</item>
     <item msgid="305344756485516870">"24 bita po uzorku"</item>
     <item msgid="244568657919675099">"32 bita po uzorku"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Zadano"</item>
+    <item msgid="5226878858503393706">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Zadano"</item>
+    <item msgid="4118561796005528173">"Upotreba odabira sustava (zadano)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Željena kval. zvuka (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Standardna (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Željeno povezivanje (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Optimizacija za kvalitetu audioreprodukcije (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Uravnotežena kvaliteta audioreprodukcije i veze (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimizacija za kvalitetu veze (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Željena kval. zvuka (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Standardna (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Željeno povezivanje (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Optimizacija za kvalitetu audioreprodukcije"</item>
+    <item msgid="4327143584633311908">"Uravnotežena kvaliteta audioreprodukcije i veze"</item>
+    <item msgid="6155648878105378550">"Optimizacija za kvalitetu veze"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Isključeno"</item>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 598e5cb..4de241a 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobilni podaci uvijek aktivni"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Onemogući apsolutnu glasnoću"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Kodek za Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Odabir željenog Bluetooth A2DP kodeka"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Odaberi kodek za Bluetooth Audio"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Brzina uzorka za Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Odabir željenu brzinu uzorka Bluetooth A2DP kodeka"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Odaberi kodek za Bluetooth Audio:\nbrzina uzorkovanja"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bitovi po uzorku za Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Odabir željenog broja bitova po uzorku za Bluetooth A2DP kodek"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Odaberi kodek za Bluetooth Audio:\nbitovi po uzorku"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Način kanala za Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Odabir željenog načina kanala Bluetooth A2DP kodeka"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"LDAC kvaliteta reprodukcije za Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Odabir željene kvalitete reprodukcije Bluetooth A2DP kodeka LDAC"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Odaberi kodek za Bluetooth Audio:\nnačin kanala"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Kodek za Bluetooth Audio LDAC: kvaliteta reprodukcije"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Odaberi kodek za Bluetooth Audio LDAC:\nkvaliteta reprodukcije"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Strujanje: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaži opcije za certifikaciju bežičnog prikaza"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećana razina prijave na Wi‑Fi, prikaz po SSID RSSI-ju u Biraču Wi‑Fi-ja"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Ako je omogućeno, Wi-Fi će aktivno prebacivati podatkovnu vezu mobilnoj mreži kada je Wi-Fi signal slab."</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Izbornik"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Unesite zaporku za resetiranje u demu"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Dalje"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Potrebna je zaporka"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index 4cc67a3..99727cc 100644
--- a/packages/SettingsLib/res/values-hu/arrays.xml
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Mindig használjon HDCP ellenőrzést"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Alapértelmezett"</item>
+    <item msgid="7065842274271279580">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Alapértelmezett"</item>
+    <item msgid="5062108632402595000">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Alapértelmezett"</item>
+    <item msgid="3093023430402746802">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Alapértelmezett"</item>
+    <item msgid="3214516120190965356">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Alapértelmezett"</item>
+    <item msgid="2684127272582591429">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="5618929009984956469">"16 bit/minta"</item>
     <item msgid="3412640499234627248">"24 bit/minta"</item>
     <item msgid="121583001492929387">"32 bit/minta"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Alapértelmezett"</item>
+    <item msgid="1081159789834584363">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="4726688794884191540">"16 bit/minta"</item>
     <item msgid="305344756485516870">"24 bit/minta"</item>
     <item msgid="244568657919675099">"32 bit/minta"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Alapértelmezett"</item>
+    <item msgid="5226878858503393706">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="4106832974775067314">"Monó"</item>
     <item msgid="5571632958424639155">"Sztereó"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Alapértelmezett"</item>
+    <item msgid="4118561796005528173">"Rendszerérték (alapértelmezett)"</item>
     <item msgid="8900559293912978337">"Monó"</item>
     <item msgid="8883739882299884241">"Sztereó"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Minőség előnyben részesítése (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Alapértelmezett (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Kapcsolat előnyben részesítése (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Optimalizálás hangminőséghez (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Kiegyensúlyozott hang- és kapcsolatminőség (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimalizálás kapcsolatminőséghez (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Minőség előnyben részesítése (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Alapértelmezett (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Kapcsolat előnyben részesítése (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Optimalizálás hangminőséghez"</item>
+    <item msgid="4327143584633311908">"Kiegyensúlyozott hang- és kapcsolatminőség"</item>
+    <item msgid="6155648878105378550">"Optimalizálás kapcsolatminőséghez"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Ki"</item>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index f4be3ec..15417d6 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"A mobilhálózati adatforgalom mindig aktív"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Abszolút hangerő funkció letiltása"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth hang – Kodek"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Előnyben részesített kodek kiválasztása a Bluetooth A2DP profiljához"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth hangkodek kiválasztása"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth hang – mintavételezési gyakoriság"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Kodek előnyben részesített mintavételezési gyakoriságának kiválasztása a Bluetooth A2DP profiljához"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth hangkodek kiválasztása:\nmintavételi gyakoriság"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth hang – bit/minta"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Kodek előnyben részesített bit/minta mennyiségének kiválasztása a Bluetooth A2DP profiljához"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth hangkodek kiválasztása:\nbit/minta"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth hang – Csatornamód"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Kodek előnyben részesített csatornamódjának kiválasztása a Bluetooth A2DP profiljához"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth hang – LDAC lejátszási minőség"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Kodek előnyben részesített LDAC lejátszási minőségének kiválasztása a Bluetooth A2DP profiljához"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth hangkodek kiválasztása:\ncsatornamód"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth LDAC hangkodek: lejátszási minőség"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth LDAC hangkodek kiválasztása:\nlejátszási minőség"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streamelés: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vezeték nélküli kijelző tanúsítványával kapcsolatos lehetőségek megjelenítése"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi-naplózási szint növelése, RSSI/SSID megjelenítése a Wi‑Fi-választóban"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Ha engedélyezi, a Wi-Fi agresszívebben fogja átadni az adatkapcsolatot a mobilhálózatnak gyenge Wi-Fi-jel esetén"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Súgó és visszajelzés"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Írja be a jelszót a visszaállításhoz"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Következő"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Jelszó szükséges"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index ab810bf..e3b553e 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Միշտ օգտագործել HDCP ստուգումը"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Կանխադրված"</item>
+    <item msgid="7065842274271279580">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Կանխադրված"</item>
+    <item msgid="5062108632402595000">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Կանխադրված"</item>
+    <item msgid="3093023430402746802">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="8895532488906185219">"44,1 կՀց"</item>
     <item msgid="2909915718994807056">"48,0 կՀց"</item>
     <item msgid="3347287377354164611">"88,2 կՀց"</item>
     <item msgid="1234212100239985373">"96,0 կՀց"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Կանխադրված"</item>
+    <item msgid="3214516120190965356">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="4482862757811638365">"44,1 կՀց"</item>
     <item msgid="354495328188724404">"48,0 կՀց"</item>
     <item msgid="7329816882213695083">"88,2 կՀց"</item>
     <item msgid="6967397666254430476">"96,0 կՀց"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Կանխադրված"</item>
+    <item msgid="2684127272582591429">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="5618929009984956469">"16 բիթ/նմուշ"</item>
     <item msgid="3412640499234627248">"24 բիթ/նմուշ"</item>
     <item msgid="121583001492929387">"32 բիթ/նմուշ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Կանխադրված"</item>
+    <item msgid="1081159789834584363">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="4726688794884191540">"16 բիթ/նմուշ"</item>
     <item msgid="305344756485516870">"24 բիթ/նմուշ"</item>
     <item msgid="244568657919675099">"32 բիթ/նմուշ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Կանխադրված"</item>
+    <item msgid="5226878858503393706">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="4106832974775067314">"Մոնո"</item>
     <item msgid="5571632958424639155">"Ստերեո"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Կանխադրված"</item>
+    <item msgid="4118561796005528173">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
     <item msgid="8900559293912978337">"Մոնո"</item>
     <item msgid="8883739882299884241">"Ստերեո"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Նախընտրելի է ձայնի որակը (990 կբ/վ / 909 կբ/վ)"</item>
-    <item msgid="138837449700903545">"Ստանդարտ (660 կբ/վ / 606 կբ/վ)"</item>
-    <item msgid="4777177307869441982">"Նախընտրելի է կապը (330 կբ/վ / 303 կբ/վ)"</item>
+    <item msgid="3411577996960199959">"Օպտիմալացնել ձայնի որակը (990 կբ/վ / 909 կբ/վ)"</item>
+    <item msgid="2921767058740704969">"Ձայնի և կապի հավասարակշռված որակ (660 կբ/վ / 606 կբ/վ)"</item>
+    <item msgid="3682554248829489641">"Օպտիմալացնել կապի որակը (330 կբ/վ / 303 կբ/վ)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Նախընտրելի է ձայնի որակը (990 կբ/վ / 909 կբ/վ)"</item>
-    <item msgid="9091111147684472529">"Ստանդարտ (660 կբ/վ / 606 կբ/վ)"</item>
-    <item msgid="3367904477834831032">"Նախընտրելի է կապը (330 կբ/վ / 303 կբ/վ)"</item>
+    <item msgid="7668834469173465015">"Օպտիմալացնել ձայնի որակը"</item>
+    <item msgid="4327143584633311908">"Ձայնի և կապի հավասարակշռված որակ"</item>
+    <item msgid="6155648878105378550">"Օպտիմալացնել կապի որակը"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Անջատված է"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 5ee1570..c75220f 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Բջջային տվյալները՝ միշտ ակտիվացրած"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Անջատել ձայնի բացարձակ ուժգնությունը"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth աուդիո կոդեկ"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Ընտրեք Bluetooth A2DP նախընտրելի կոդեկը"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Ընտրեք Bluetooth աուդիո կոդեկը"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth աուդիոյի Ընդհատավորման հաճախականությունը"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Ընտրեք Bluetooth A2DP կոդեկի Ընդհատավորման նախընտրելի հաճախականությունը"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Ընտրեք Bluetooth աուդիո կոդեկը՝\nընդհատավորման հաճախականություն"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth աուդիո, բիթ / նմուշ"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Ընտրեք Bluetooth A2DP կոդեկի նախընտրելի որակը, բիթ / նմուշ"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Ընտրեք Bluetooth աուդիո կոդեկը՝\nբիթ/նմուշ"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth աուդիո կապուղու ռեժիմը"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Ընտրեք Bluetooth A2DP կոդեկի կապուղու նախընտրելի ռեժիմը"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth աուդիո LDAC նվագարկման որակը"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Ընտրեք Bluetooth A2DP կոդեկի LDAC նվագարկման նախընտրելի որակը"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Ընտրեք Bluetooth աուդիո կոդեկը՝\nկապուղու ռեժիմ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth աուդիո LDAC կոդեկ՝ նվագարկման որակ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Ընտրեք Bluetooth աուդիո LDAC կոդեկը՝\nնվագարկման որակ"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Հեռարձակում՝ <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ցույց տալ անլար էկրանի հավաստագրման ընտրանքները"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Բարձրացնել մակարդակը, Wi‑Fi ընտրիչում ամեն մի SSID-ի համար ցույց տալ RSSI"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Եթե այս գործառույթը միացված է, Wi‑Fi-ի թույլ ազդանշանի դեպքում Wi‑Fi ինտերնետից անցումը բջջային ինտերնետին ավելի կտրուկ կլինի"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Օգնություն և հետադարձ կապ"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Ընտրացանկ"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Մուտքագրեք գաղտնաբառը՝ ցուցադրական ռեժիմում գործարանային վերակայում կատարելու համար"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Հաջորդը"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Պահանջվում է գաղտնաբառ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index be3d89c..9d56ba7 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Selalu gunakan pemeriksaan HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Default"</item>
+    <item msgid="7065842274271279580">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Default"</item>
+    <item msgid="5062108632402595000">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Default"</item>
+    <item msgid="3093023430402746802">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Default"</item>
+    <item msgid="3214516120190965356">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Default"</item>
+    <item msgid="2684127272582591429">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="5618929009984956469">"16 bit/sampel"</item>
     <item msgid="3412640499234627248">"24 bit/sampel"</item>
     <item msgid="121583001492929387">"32 bit/sampel"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Default"</item>
+    <item msgid="1081159789834584363">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="4726688794884191540">"16 bit/sampel"</item>
     <item msgid="305344756485516870">"24 bit/sampel"</item>
     <item msgid="244568657919675099">"32 bit/sampel"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Default"</item>
+    <item msgid="5226878858503393706">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Default"</item>
+    <item msgid="4118561796005528173">"Gunakan Pilihan Sistem (Default)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Kualitas suara yang disukai (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Standar (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Sambungan yang disukai (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"Optimalkan untuk Kualitas Audio (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"Kualitas Audio dan Sambungan Seimbang (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"Optimalkan untuk Kualitas Sambungan (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Kualitas suara yang disukai (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Standar (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Sambungan yang disukai (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"Optimalkan untuk Kualitas Audio"</item>
+    <item msgid="4327143584633311908">"Kualitas Audio dan Sambungan Seimbang"</item>
+    <item msgid="6155648878105378550">"Optimalkan untuk Kualitas Sambungan"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Nonaktif"</item>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 97eca8e..12d61fe 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Data seluler selalu aktif"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Nonaktifkan volume absolut"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Pilih Codec A2DP Bluetooth Yang Disukai"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Pilih Codec Audio Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Frekuensi Sampel Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Pilih Frekuensi Sampel Codec A2DP Bluetooth Yang Disukai"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Pilih Codec Audio Bluetooth:\nFrekuensi Sampel"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bit Per Sampel Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Pilih Bit Per Sampel Codec A2DP Bluetooth Yang Disukai"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Pilih Codec Audio Bluetooth:\nBit Per Sampel"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Mode Channel Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Pilih Mode Channel Codec A2DP Bluetooth Yang Disukai"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Kualitas Pemutaran LDAC Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Pilih Kualitas Pemutaran LDAC Codec A2DP Bluetooth Yang Disukai"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Pilih Codec Audio Bluetooth:\nMode Channel"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec LDAC Audio Bluetooth: Kualitas Pemutaran"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Pilih Codec LDAC Audio Bluetooth:\nKualitas Pemutaran"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Tampilkan opsi untuk sertifikasi layar nirkabel"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tingkatkan level pencatatan log Wi-Fi, tampilkan per SSID RSSI di Pemilih Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Jika diaktifkan, Wi-Fi akan menjadi lebih agresif dalam mengalihkan sambungan data ke Seluler saat sinyal Wi-Fi lemah"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Bantuan &amp; masukan"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Masukkan sandi untuk mengembalikan ke setelan pabrik dalam mode demo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Berikutnya"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Perlu sandi"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml
index dbb1636..e3f2ba3 100644
--- a/packages/SettingsLib/res/values-is/arrays.xml
+++ b/packages/SettingsLib/res/values-is/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Nota alltaf HDCP-eftirlit"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Sjálfgefið"</item>
+    <item msgid="7065842274271279580">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Sjálfgefið"</item>
+    <item msgid="5062108632402595000">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Sjálfgefið"</item>
+    <item msgid="3093023430402746802">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Sjálfgefið"</item>
+    <item msgid="3214516120190965356">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Sjálfgefið"</item>
+    <item msgid="2684127272582591429">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="5618929009984956469">"16 bitar/úrtak"</item>
     <item msgid="3412640499234627248">"24 bitar/úrtak"</item>
     <item msgid="121583001492929387">"32 bitar/úrtak"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Sjálfgefið"</item>
+    <item msgid="1081159789834584363">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="4726688794884191540">"16 bitar/úrtak"</item>
     <item msgid="305344756485516870">"24 bitar/úrtak"</item>
     <item msgid="244568657919675099">"32 bitar/úrtak"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Sjálfgefið"</item>
+    <item msgid="5226878858503393706">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="4106832974775067314">"Einóma"</item>
     <item msgid="5571632958424639155">"Víðóma"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Sjálfgefið"</item>
+    <item msgid="4118561796005528173">"Nota val kerfisins (sjálfgefið)"</item>
     <item msgid="8900559293912978337">"Einóma"</item>
     <item msgid="8883739882299884241">"Víðóma"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Hljóðgæði í forgangi (990kb/s / 909kb/s)"</item>
-    <item msgid="138837449700903545">"Venjulegt (660kb/s / 606kb/s)"</item>
-    <item msgid="4777177307869441982">"Tenging í forgangi (330kb/s / 303kb/s)"</item>
+    <item msgid="3411577996960199959">"Fínstilla fyrir hljóðgæði (990 kbps / 909 kbps)"</item>
+    <item msgid="2921767058740704969">"Jafnvægi á milli gæða hljóðs og tengingar (660 kbps / 606 kbps)"</item>
+    <item msgid="3682554248829489641">"Fínstilla fyrir gæði tengingar (330 kbps / 303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Hljóðgæði í forgangi (990kb/s / 909kb/s)"</item>
-    <item msgid="9091111147684472529">"Venjulegt (660kb/s / 606kb/s)"</item>
-    <item msgid="3367904477834831032">"Tenging í forgangi (330kb/s / 303kb/s)"</item>
+    <item msgid="7668834469173465015">"Fínstilla fyrir hljóðgæði"</item>
+    <item msgid="4327143584633311908">"Jafnvægi á milli gæða hljóðs og tengingar"</item>
+    <item msgid="6155648878105378550">"Fínstilla fyrir gæði tengingar"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Slökkt"</item>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 4cecdbc..844a3b4 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Alltaf kveikt á farsímagögnum"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Slökkva á samstillingu hljóðstyrks"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth hljóðkóðari"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Veldu Bluetooth A2DP kóðara"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Velja Bluetooth-hljóðkóðara"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth hljóðtökutíðni"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Veldu Bluetooth A2DP kóðaratökutíðni"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Velja hljóðkóðara Bluetooth:\ntökutíðni"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth hljóðbitar í úrtaki"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Veldu Bluetooth A2DP kóðarabita í úrtaki"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Velja hljóðkóðara Bluetooth:\nbitar í úrtaki"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Hljóðrásarstilling Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Veldu Bluetooth A2DP stillingu kóðararásar"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth LDAC gæði hljóðspilunar"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Veldu Bluetooth LDAC spilunargæði A2DP kóðara"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Velja hljóðkóðara Bluetooth:\nstilling rásar"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth LDAC-hljóðkóðari: gæði spilunar"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Velja Bluetooth LDAC-hljóðkóðara:\ngæði spilunar"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streymi: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Sýna valkosti fyrir vottun þráðlausra skjáa"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Auka skráningarstig Wi-Fi, sýna RSSI fyrir hvert SSID í Wi-Fi vali"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Þegar þetta er virkt mun Wi-Fi ganga harðar fram í að færa gagnatenginguna yfir til símkerfisins þegar Wi-Fi merkið er lélegt"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Hjálp og ábendingar"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Valmynd"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Sláðu inn aðgangsorð til að framkvæma núllstillingu í sýnisútgáfu"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Áfram"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Aðgangsorðs krafist"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index 5349086..588b23c 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Usa sempre la verifica HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Valore predefinito"</item>
+    <item msgid="7065842274271279580">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Valore predefinito"</item>
+    <item msgid="5062108632402595000">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Valore predefinito"</item>
+    <item msgid="3093023430402746802">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Valore predefinito"</item>
+    <item msgid="3214516120190965356">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Valore predefinito"</item>
+    <item msgid="2684127272582591429">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="5618929009984956469">"16 bit/campione"</item>
     <item msgid="3412640499234627248">"24 bit/campione"</item>
     <item msgid="121583001492929387">"32 bit/campione"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Valore predefinito"</item>
+    <item msgid="1081159789834584363">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="4726688794884191540">"16 bit/campione"</item>
     <item msgid="305344756485516870">"24 bit/campione"</item>
     <item msgid="244568657919675099">"32 bit/campione"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Valore predefinito"</item>
+    <item msgid="5226878858503393706">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Valore predefinito"</item>
+    <item msgid="4118561796005528173">"Usa selezione di sistema (predefinita)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Qualità audio preferita (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Collegamento preferito (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Ottimizza per qualità audio (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Audio bilanciato e qualità di connessione (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Ottimizza per qualità di connessione (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Qualità audio preferita (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Collegamento preferito (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Ottimizza per qualità audio"</item>
+    <item msgid="4327143584633311908">"Audio bilanciato e qualità di connessione"</item>
+    <item msgid="6155648878105378550">"Ottimizza per qualità di connessione"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Off"</item>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index a33fa87..7e3fd48 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Dati cellulare sempre attivi"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Disattiva volume assoluto"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Seleziona codec A2DP Bluetooth preferito"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Seleziona il codec audio Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Frequenza di campionamento audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Seleziona frequenza di campionamento preferita codec A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Seleziona il codec audio Bluetooth:\nFrequenza di campionamento"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bit per campione dell\'audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Seleziona bit per campione preferiti codec A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Seleziona il codec audio Bluetooth:\nBit per campione"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modalità canale audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Seleziona modalità canale preferita codec A2DP Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Qualità di riproduzione LDAC audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Seleziona qualità di riproduzione preferita LDAC codec A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Seleziona il codec audio Bluetooth:\nModalità canale"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec LDAC audio Bluetooth: qualità di riproduzione"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Seleziona il codec LDAC audio Bluetooth:\nQualità di riproduzione"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opzioni per la certificazione display wireless"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumenta il livello di registrazione Wi-Fi, mostrando il SSID RSSI nel selettore Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Quando questa impostazione è attivata, il Wi-Fi sarà più aggressivo nel passare la connessione dati al cellulare, quando il segnale Wi-Fi è basso"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Guida e feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Inserisci la password per eseguire il ripristino dei dati di fabbrica in modalità demo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Avanti"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Password obbligatoria"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index 621e14d..976eb71 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"‏תמיד השתמש בבדיקת HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ברירת מחדל"</item>
+    <item msgid="7065842274271279580">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ברירת מחדל"</item>
+    <item msgid="5062108632402595000">"השתמש בבחירת המערכת (ברירת המחדל)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ברירת מחדל"</item>
+    <item msgid="3093023430402746802">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
     <item msgid="8895532488906185219">"44.1 קילו-הרץ"</item>
     <item msgid="2909915718994807056">"48.0 קילו-הרץ"</item>
     <item msgid="3347287377354164611">"88.2 קילו-הרץ"</item>
     <item msgid="1234212100239985373">"96.0 קילו-הרץ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ברירת מחדל"</item>
+    <item msgid="3214516120190965356">"השתמש בבחירת המערכת (ברירת המחדל)"</item>
     <item msgid="4482862757811638365">"44.1 קילו-הרץ"</item>
     <item msgid="354495328188724404">"48.0 קילו-הרץ"</item>
     <item msgid="7329816882213695083">"88.2 קילו-הרץ"</item>
     <item msgid="6967397666254430476">"96.0 קילו-הרץ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ברירת מחדל"</item>
-    <item msgid="5618929009984956469">"16 סיביות/דגימה"</item>
-    <item msgid="3412640499234627248">"24 סיביות/דגימה"</item>
-    <item msgid="121583001492929387">"32 סיביות/דגימה"</item>
+    <item msgid="2684127272582591429">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
+    <item msgid="5618929009984956469">"16 סיביות לדגימה"</item>
+    <item msgid="3412640499234627248">"24 סיביות לדגימה"</item>
+    <item msgid="121583001492929387">"32 סיביות לדגימה"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ברירת מחדל"</item>
-    <item msgid="4726688794884191540">"16 סיביות/דגימה"</item>
-    <item msgid="305344756485516870">"24 סיביות/דגימה"</item>
-    <item msgid="244568657919675099">"32 סיביות/דגימה"</item>
+    <item msgid="1081159789834584363">"השתמש בבחירת המערכת (ברירת המחדל)"</item>
+    <item msgid="4726688794884191540">"16 סיביות לדגימה"</item>
+    <item msgid="305344756485516870">"24 סיביות לדגימה"</item>
+    <item msgid="244568657919675099">"32 סיביות לדגימה"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ברירת מחדל"</item>
+    <item msgid="5226878858503393706">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
     <item msgid="4106832974775067314">"מונו"</item>
     <item msgid="5571632958424639155">"סטריאו"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ברירת מחדל"</item>
+    <item msgid="4118561796005528173">"השתמש בבחירת המערכת (ברירת המחדל)"</item>
     <item msgid="8900559293912978337">"מונו"</item>
     <item msgid="8883739882299884241">"סטריאו"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"‏איכות צליל מועדפת (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"‏רגילה (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"‏חיבור מועדף (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"‏אופטימיזציה להשגת איכות אודיו מרבית (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"‏איזון בין איכות החיבור לאיכות אודיו (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"‏אופטימיזציה להשגת איכות חיבור מרבית (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"‏איכות צליל מועדפת (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"‏רגילה (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"‏חיבור מועדף (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"בצע אופטימיזציה לאיכות האודיו"</item>
+    <item msgid="4327143584633311908">"אזן בין איכות החיבור לאיכות אודיו"</item>
+    <item msgid="6155648878105378550">"בצע אופטימיזציה להשגת איכות חיבור מרבית"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"כבוי"</item>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 45dd7e7..3e1bfdb 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"נתונים סלולריים פעילים תמיד"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"השבת עוצמת קול מוחלטת"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"‏Codec אודיו ל-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"‏בחר codec ‏A2DP מועדף ל-Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"‏בחירת ‏Codec אודיו ל-Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"‏קצב דגימה של אודיו ל-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"‏בחר קצב דגימה מועדף ב-codec ‏‏A2DP ל-Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"‏בחירת Codec אודיו ל-Bluetooth‏:\nקצב דגימה"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"‏מספר סיביות לדגימה באודיו ל-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"‏בחר מספר מועדף של סיביות לדגימה ב-codec ‏‏A2DP ל-Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"‏בחירת codec אודיו ל-Bluetooth:‏\nסיביות לדגימה"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"‏מצב של ערוץ אודיו ל-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"‏בחר מצב ערוץ מועדף ב-codec ‏‏A2DP ל-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"‏איכות נגינה של אודיו LDAC ל-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"‏בחר איכות נגינה מועדפת ב-codec ‏‏A2DP ל-Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"‏בחירת codec אודיו ל-Bluetooth:‏\nמצב ערוץ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"‏Codec אודיו LDAC ל-Bluetooth: איכות נגינה"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"‏בחירת Codec אודיו LDAC ל-Bluetooth:‏\nאיכות נגינה"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"סטרימינג: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"‏הצג אפשרויות עבור אישור של תצוגת WiFi"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‏העלה את רמת הרישום של Wi‑Fi ביומן, הצג לכל SSID RSSI ב-Wi‑Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"‏כשתכונה זו מופעלת, Wi-Fi יתנהג בצורה אגרסיבית יותר בעת העברת חיבור הנתונים לרשת הסלולרית כשאות ה-Wi-Fi חלש."</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"עזרה ומשוב"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"תפריט"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"הזן סיסמה כדי לבצע איפוס להגדרות היצרן במצב הדגמה"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"הבא"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"דרושה סיסמה"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index cc00934..c8599d2 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"DRMコンテンツにのみHDCPチェックを使用する"</item>
     <item msgid="45075631231212732">"HDCPチェックを常に使用する"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"デフォルト"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"デフォルト"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"デフォルト"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"デフォルト"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"デフォルト"</item>
-    <item msgid="5618929009984956469">"16 ビット / サンプル"</item>
-    <item msgid="3412640499234627248">"24 ビット / サンプル"</item>
-    <item msgid="121583001492929387">"32 ビット / サンプル"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"デフォルト"</item>
-    <item msgid="4726688794884191540">"16 ビット / サンプル"</item>
-    <item msgid="305344756485516870">"24 ビット / サンプル"</item>
-    <item msgid="244568657919675099">"32 ビット / サンプル"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"デフォルト"</item>
-    <item msgid="4106832974775067314">"モノラル"</item>
-    <item msgid="5571632958424639155">"ステレオ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"デフォルト"</item>
-    <item msgid="8900559293912978337">"モノラル"</item>
-    <item msgid="8883739882299884241">"ステレオ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"優先する音質(990 kbps / 909 kbps)"</item>
-    <item msgid="138837449700903545">"標準(660 kbps / 606 kbps)"</item>
-    <item msgid="4777177307869441982">"優先する接続(330 kbps / 303 kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"優先する音質(990 kbps / 909 kbps)"</item>
-    <item msgid="9091111147684472529">"標準(660 kbps / 606 kbps)"</item>
-    <item msgid="3367904477834831032">"優先する接続(330 kbps / 303 kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"OFF"</item>
     <item msgid="1593289376502312923">"64 K"</item>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 12a8734..a7d607f 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"モバイルデータを常にON"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"絶対音量を無効にする"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth オーディオ コーデック"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"優先する Bluetooth A2DP コーデックを選択"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth オーディオ サンプルレート"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"優先する Bluetooth A2DP コーデック サンプルレートを選択"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"サンプルあたりの Bluetooth オーディオ ビット"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"優先するサンプルあたりの Bluetooth A2DP コーデック ビットを選択"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth オーディオ チャンネル モード"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"優先する Bluetooth A2DP コーデック チャンネル モードを選択"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth オーディオ LDAC 再生音質"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"優先する Bluetooth A2DP コーデック LDAC 再生音質を選択"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ワイヤレスディスプレイ認証のオプションを表示"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fiログレベルを上げて、Wi-Fi選択ツールでSSID RSSIごとに表示します"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"有効にすると、Wi-Fiの電波強度が弱い場合は強制的にモバイルデータ接続に切り替わるようになります"</string>
@@ -354,4 +362,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"ヘルプとフィードバック"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"メニュー"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"デモモードで初期状態にリセットするには、パスワードを入力してください"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"次へ"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"パスワード必須"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml
index 0e6eb7c..ad04e15 100644
--- a/packages/SettingsLib/res/values-ka/arrays.xml
+++ b/packages/SettingsLib/res/values-ka/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"ყოველთვის გამოიყენე HDCP შემოწმება"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ნაგულისხმევი"</item>
+    <item msgid="7065842274271279580">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ნაგულისხმევი"</item>
+    <item msgid="5062108632402595000">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ნაგულისხმევი"</item>
+    <item msgid="3093023430402746802">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="8895532488906185219">"44,1 კჰც"</item>
     <item msgid="2909915718994807056">"48,0 კჰც"</item>
     <item msgid="3347287377354164611">"88,2 კჰც"</item>
     <item msgid="1234212100239985373">"96,0 კჰც"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ნაგულისხმევი"</item>
+    <item msgid="3214516120190965356">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="4482862757811638365">"44,1 კჰც"</item>
     <item msgid="354495328188724404">"48,0 კჰც"</item>
     <item msgid="7329816882213695083">"88,2 კჰც"</item>
     <item msgid="6967397666254430476">"96,0 კჰც"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ნაგულისხმევი"</item>
+    <item msgid="2684127272582591429">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="5618929009984956469">"16 ბიტი/ნიმუში"</item>
     <item msgid="3412640499234627248">"24 ბიტი/ნიმუში"</item>
     <item msgid="121583001492929387">"32 ბიტი/ნიმუში"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ნაგულისხმევი"</item>
+    <item msgid="1081159789834584363">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="4726688794884191540">"16 ბიტი/ნიმუში"</item>
     <item msgid="305344756485516870">"24 ბიტი/ნიმუში"</item>
     <item msgid="244568657919675099">"32 ბიტი/ნიმუში"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ნაგულისხმევი"</item>
+    <item msgid="5226878858503393706">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="4106832974775067314">"მონო"</item>
     <item msgid="5571632958424639155">"სტერეო"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ნაგულისხმევი"</item>
+    <item msgid="4118561796005528173">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
     <item msgid="8900559293912978337">"მონო"</item>
     <item msgid="8883739882299884241">"სტერეო"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"სასურველია ხმის ხარისხი (990/909 კბიტი/წმ)"</item>
-    <item msgid="138837449700903545">"სტანდარტული (660/606 კბიტი/წმ)"</item>
-    <item msgid="4777177307869441982">"სასურველია კავშირი (330/303 კბიტი/წმ)"</item>
+    <item msgid="3411577996960199959">"აუდიოს ხარისხის ოპტიმიზაცია (990/909 კბიტი/წმ)"</item>
+    <item msgid="2921767058740704969">"აუდიოსა და კავშირის დაბალანსებული ხარისხი (660/606 კბიტი/წმ)"</item>
+    <item msgid="3682554248829489641">"კავშირის ხარისხის ოპტიმიზაცია (330/303 კბიტი/წმ)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"სასურველია ხმის ხარისხი (990/909 კბიტი/წმ)"</item>
-    <item msgid="9091111147684472529">"სტანდარტული (660/606 კბიტი/წმ)"</item>
-    <item msgid="3367904477834831032">"სასურველია კავშირი (330/303 კბიტი/წმ)"</item>
+    <item msgid="7668834469173465015">"აუდიოს ხარისხის ოპტიმიზაცია"</item>
+    <item msgid="4327143584633311908">"აუდიოსა და კავშირის დაბალანსებული ხარისხი"</item>
+    <item msgid="6155648878105378550">"კავშირის ხარისხის ოპტიმიზაცია"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"გამორთული"</item>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 18c2def..6838fbc 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"ფიჭური მონაცემები ყოველთვის აქტიურია"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ხმის აბსოლუტური სიძლიერის გათიშვა"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth აუდიოს კოდეკი"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"აირჩიეთ Bluetooth A2DP-ის სასურველი კოდეკი"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"აირჩიეთ Bluetooth აუდიოს კოდეკი"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth აუდიოს დისკრეტიზაციის სიხშირე"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"აირჩიეთ Bluetooth A2DP კოდეკის დისკრეტიზაციის სასურველი სიხშირე"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"აირჩიეთ Bluetooth აუდიოს კოდეკის\nდისკრეტიზაციის სიხშირე"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth აუდიოს ბიტების რაოდენობა ნიმუშზე"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"აირჩიეთ Bluetooth A2DP კოდეკის ბიტების სასურველი რაოდენობა ნიმუშზე"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"აირჩიეთ Bluetooth აუდიოს კოდეკის\nბიტების რაოდენობა ნიმუშზე"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth აუდიოს არხის რეჟიმი"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"აირჩიეთ Bluetooth A2DP კოდეკის არხის სასურველი რეჟიმი"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth აუდიოს LDAC დაკვრის ხარისხი"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"აირჩიეთ Bluetooth A2DP კოდეკის LDAC დაკვრის სასურველი ხარისხი"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"აირჩიეთ Bluetooth აუდიოს კოდეკის\nარხის რეჟიმი"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth აუდიოს LDAC კოდეკის დაკვრის ხარისხი"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"აირჩიეთ Bluetooth აუდიოს LDAC კოდეკის\nდაკვრის ხარისხი"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"სტრიმინგი: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"უსადენო ეკრანის სერტიფიცირების ვარიანტების ჩვენება"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi-ს აღრიცხვის დონის გაზრდა, Wi‑Fi ამომრჩეველში ყოველ SSID RSSI-ზე ჩვენება"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"თუ ჩართულია, Wi‑Fi სიგნალის შესუსტების შემთხვევაში Wi-Fi უფრო აქტიურად შეეცდება გადაიყვანოს ინტერნეტ-კავშირი მობილურ ინტერნეტზე"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"დახმარება და გამოხმაურება"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"მენიუ"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"შეიყვანეთ პაროლი დემო-რეჟიმში ქარხნულ მდგომარეობაზე დასაბრუნებლად"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"შემდეგი"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"საჭიროა პაროლი"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml
index e253a79..056f17b 100644
--- a/packages/SettingsLib/res/values-kk/arrays.xml
+++ b/packages/SettingsLib/res/values-kk/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Әрқашан HDCP (жоғары кең жолақты сандық мазмұн қорғаушы) тексерулерін қолданыңыз"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Әдепкі"</item>
+    <item msgid="7065842274271279580">"Жүйені таңдау (әдепкі)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Әдепкі"</item>
+    <item msgid="5062108632402595000">"Жүйені таңдау (әдепкі)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Әдепкі"</item>
+    <item msgid="3093023430402746802">"Жүйені таңдау (әдепкі)"</item>
     <item msgid="8895532488906185219">"44,1 кГц"</item>
     <item msgid="2909915718994807056">"48,0 кГц"</item>
     <item msgid="3347287377354164611">"88,2 кГц"</item>
     <item msgid="1234212100239985373">"96,0 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Әдепкі"</item>
+    <item msgid="3214516120190965356">"Жүйені таңдау (әдепкі)"</item>
     <item msgid="4482862757811638365">"44,1 кГц"</item>
     <item msgid="354495328188724404">"48,0 кГц"</item>
     <item msgid="7329816882213695083">"88,2 кГц"</item>
     <item msgid="6967397666254430476">"96,0 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Әдепкі"</item>
+    <item msgid="2684127272582591429">"Жүйені таңдау (әдепкі)"</item>
     <item msgid="5618929009984956469">"16 бит/үлгі"</item>
     <item msgid="3412640499234627248">"24 бит/үлгі"</item>
     <item msgid="121583001492929387">"32 бит/үлгі"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Әдепкі"</item>
+    <item msgid="1081159789834584363">"Жүйені таңдау (әдепкі)"</item>
     <item msgid="4726688794884191540">"16 бит/үлгі"</item>
     <item msgid="305344756485516870">"24 бит/үлгі"</item>
     <item msgid="244568657919675099">"32 бит/үлгі"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Әдепкі"</item>
+    <item msgid="5226878858503393706">"Жүйені таңдау (әдепкі)"</item>
     <item msgid="4106832974775067314">"Моно"</item>
     <item msgid="5571632958424639155">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Әдепкі"</item>
+    <item msgid="4118561796005528173">"Жүйені таңдау (әдепкі)"</item>
     <item msgid="8900559293912978337">"Моно"</item>
     <item msgid="8883739882299884241">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Таңдаулы дыбыс сапасы (990 кб/сек не 909 кб/сек)"</item>
-    <item msgid="138837449700903545">"Стандартты (660 кб/сек не 606 кб/сек)"</item>
-    <item msgid="4777177307869441982">"Таңдаулы байланыс (330 кб/сек не 303 кб/сек)"</item>
+    <item msgid="3411577996960199959">"Аудиомазмұн сапасы бойынша оңтайландыру (990 кб/сек не 909 кб/сек)"</item>
+    <item msgid="2921767058740704969">"Теңгерілген аудиомазмұн мен байланыс сапасы (660 кб/сек не 606 кб/сек)"</item>
+    <item msgid="3682554248829489641">"Байланыс сапасы бойынша оңтайландыру (330 кб/сек не 303 кб/сек)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Таңдаулы дыбыс сапасы (990 кб/сек не 909 кб/сек)"</item>
-    <item msgid="9091111147684472529">"Стандартты (660 кб/сек не 606 кб/сек)"</item>
-    <item msgid="3367904477834831032">"Таңдаулы байланыс (330 кб/сек не 303 кб/сек)"</item>
+    <item msgid="7668834469173465015">"Аудиомазмұн сапасы бойынша оңтайландыру"</item>
+    <item msgid="4327143584633311908">"Теңгерілген аудиомазмұн мен байланыс сапасы"</item>
+    <item msgid="6155648878105378550">"Байланыс сапасы бойынша оңтайландыру"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Өшірулі"</item>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 505b118..9372dbd 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -170,16 +170,17 @@
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi роумингін іздеулерге әрқашан рұқсат ету"</string>
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Ұялы деректер әрқашан белсенді"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Абсолютті дыбыс деңгейін өшіру"</string>
-    <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth аудимазмұны кодегі"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Таңдаулы Bluetooth A2DP кодегін таңдау"</string>
+    <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth аудимазмұн кодегі"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth аудиокодегін таңдау"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth аудиомазмұны бойынша үлгі жиілігі"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Таңдаулы Bluetooth A2DP кодегі бойынша үлгі жиілігі"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth аудиокодегін таңдау:\nдискреттеу жылдамдығы"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth аудиомазмұны бойынша әр үлгіге келетін биттер саны"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Таңдаулы Bluetooth A2DP кодегі бойынша әр үлгіге келетін биттер санын таңдау"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth аудиокодегін таңдау:\nбит/үлгі"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth аудиомазмұны бойынша арна режимі"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Таңдаулы Bluetooth A2DP кодегі бойынша арна режимі"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth аудиомазмұны бойынша LDAC ойнату сапасы"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Таңдаулы Bluetooth A2DP кодегі бойынша LDAC ойнату сапасын таңдау"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth аудиокодегін таңдау:\nарна режимі"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth LDAC аудиокодегі: ойнату сапасы"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth LDAC аудиокодегін таңдау:\nойнату сапасы"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Трансляция: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Сымсыз дисплей растау опцияларын көрсету"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi жур. тір. дең. арт., Wi‑Fi желісін таңдағышта әр SSID RSSI бойынша көрсету"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Wi‑Fi сигналы әлсіз болғанда, деректер байланысы мәжбүрлі түрде ұялы желіге ауысады"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Анықтама және пікір"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Mәзір"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Демо режимде зауыттық мәндерге қайтару үшін құпия сөзді енгізу"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Келесі"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Құпия сөз қажет"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml
index 569603f..470112a 100644
--- a/packages/SettingsLib/res/values-km/arrays.xml
+++ b/packages/SettingsLib/res/values-km/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"ប្រើ​ការ​ពិនិត្យ HDCP សម្រាប់​តែ​មាតិកា DRM ប៉ុណ្ណោះ"</item>
     <item msgid="45075631231212732">"ប្រើ​ការ​ពិនិត្យ HDCP ជា​និច្ច"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"លំ​នាំ​ដើម"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"លំ​នាំ​ដើម"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"លំនាំដើម"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"លំ​នាំ​ដើម"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"លំ​នាំ​ដើម"</item>
-    <item msgid="5618929009984956469">"16 ប៊ីត​/​គំរូ"</item>
-    <item msgid="3412640499234627248">"24 ប៊ីត​/​គំរូ"</item>
-    <item msgid="121583001492929387">"32 ប៊ីត​/​គំរូ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"លំ​នាំ​ដើម"</item>
-    <item msgid="4726688794884191540">"16 ប៊ីត​/​គំរូ"</item>
-    <item msgid="305344756485516870">"24 ប៊ីត​/​គំរូ"</item>
-    <item msgid="244568657919675099">"32 ប៊ីត​/​គំរូ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"លំ​នាំ​ដើម"</item>
-    <item msgid="4106832974775067314">"ម៉ូ​ណូ"</item>
-    <item msgid="5571632958424639155">"ស្តេរ៉េអូ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"លំ​នាំ​ដើម"</item>
-    <item msgid="8900559293912978337">"ម៉ូ​ណូ"</item>
-    <item msgid="8883739882299884241">"ស្តេរ៉េអូ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"គុណភាព​សំឡេង​ដែល​គួរ​ប្រើ (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"ស្តង់ដា (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"ការ​ត​ភ្ជាប់​ដែល​គួរ​ប្រើ (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"គុណភាព​សំឡេង​ដែល​គួរ​ប្រើ (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"ស្តង់ដា (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"ការ​ត​ភ្ជាប់​ដែល​គួរ​ប្រើ (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"បិទ"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index b7bddd8..0b1ba5e 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"ទិន្នន័យចល័តសកម្មជានិច្ច"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"បិទកម្រិតសំឡេងលឺខ្លាំង"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"កូឌិក​សំឡេង​ប៊្លូធូស"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"ជ្រើស​រើស​កូឌិក​សម្រាប់​ប៊្លូធូស A2DP ដែល​គួរ​ប្រើ"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"អត្រា​គំរូ​សំឡេង​ប៊្លូធូស"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"ជ្រើសរើស​អត្រា​គំរូ​កូឌិក​សម្រាប់​ប៊្លូធូស A2DP ដែល​គួរ​ប្រើ"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"កម្រិត​ប៊ីត​ក្នុង​មួយ​គំរូ​នៃ​សំឡេង​ប៊្លូធូស"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"ជ្រើសរើស​កម្រិត​ប៊ីត​ក្នុង​មួយ​គំរូ​នៃ​កូឌិក​ប៊្លូធូស A2DP ដែល​គួរ​ប្រើ"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"មុខ​ងារ​រលកសញ្ញា​សំឡេង​ប៊្លូធូស"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"ជ្រើស​រើស​មុខងារ​រលក​សញ្ញា​កូឌិក​ប៊្លូធូស A2DP ដែល​គួរ​ប្រើ"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"គុណភាព​ចាក់​សម្រាប់​សំឡេង​ប៊្លូធូស LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"ជ្រើសរើស​គុណភាព​ចាក់ LDAC កូឌិក សម្រាប់​ប៊្លូធូស A2DP ដែល​គួរ​ប្រើ"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"បង្ហាញ​ជម្រើស​សម្រាប់​វិញ្ញាបនបត្រ​បង្ហាញ​ឥត​ខ្សែ"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"បង្កើនកម្រិតកំណត់ហេតុវ៉ាយហ្វាយបង្ហាញក្នុង SSID RSSI ក្នុងកម្មវិធីជ្រើស​វ៉ាយហ្វាយ"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"ពេល​បាន​បើក វ៉ាយហ្វាយ​នឹង​កាន់តែ​បង្ខំ​ក្នុង​ការ​បញ្ជូន​ការ​ភ្ជាប់​ទិន្នន័យ​ទៅ​បណ្ដាញ​ចល័ត នៅ​ពេល​សញ្ញា​វ៉ាយហ្វាយ​យឺត"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"ជំនួយ និងមតិស្ថាបនា"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"ម៉ឺនុយ"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"បញ្ចូល​ពាក្យ​សម្ងាត់ ដើម្បី​កំណត់​​ដូចចេញ​ពី​រោងចក្រឡើង​វិញ​នៅក្នុង​មុខងារ​សាកល្បង"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"បន្ទាប់"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"តម្រូវ​ឲ្យ​មានពាក្យ​សម្ងាត់"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml
index ae3e97d..2cfe328 100644
--- a/packages/SettingsLib/res/values-kn/arrays.xml
+++ b/packages/SettingsLib/res/values-kn/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"HDCP ಪರಿಶೀಲನೆಯನ್ನು ಯಾವಾಗಲೂ ಬಳಸು"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ಡಿಫಾಲ್ಟ್"</item>
+    <item msgid="7065842274271279580">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ಡಿಫಾಲ್ಟ್"</item>
+    <item msgid="5062108632402595000">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ಡಿಫಾಲ್ಟ್"</item>
+    <item msgid="3093023430402746802">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ಡಿಫಾಲ್ಟ್"</item>
+    <item msgid="3214516120190965356">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ಡಿಫಾಲ್ಟ್"</item>
+    <item msgid="2684127272582591429">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="5618929009984956469">"16 ಬಿಟ್ಸ್/ಮಾದರಿ"</item>
     <item msgid="3412640499234627248">"24 ಬಿಟ್ಸ್/ಮಾದರಿ"</item>
     <item msgid="121583001492929387">"32 ಬಿಟ್ಸ್/ಮಾದರಿ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ಡಿಫಾಲ್ಟ್"</item>
+    <item msgid="1081159789834584363">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="4726688794884191540">"16 ಬಿಟ್ಸ್/ಮಾದರಿ"</item>
     <item msgid="305344756485516870">"24 ಬಿಟ್ಸ್/ಮಾದರಿ"</item>
     <item msgid="244568657919675099">"32 ಬಿಟ್ಸ್/ಮಾದರಿ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ಡಿಫಾಲ್ಟ್"</item>
+    <item msgid="5226878858503393706">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="4106832974775067314">"ಮೊನೊ"</item>
     <item msgid="5571632958424639155">"ಸ್ಟೀರಿಯೊ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ಡಿಫಾಲ್ಟ್"</item>
+    <item msgid="4118561796005528173">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
     <item msgid="8900559293912978337">"ಮೊನೊ"</item>
     <item msgid="8883739882299884241">"ಸ್ಟೀರಿಯೊ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"ಧ್ವನಿಯ ಗುಣಮಟ್ಟಕ್ಕೆ ಆದ್ಯತೆ ನೀಡಲಾಗಿದೆ (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"ಪ್ರಮಾಣಿತ (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"ಸಂಪರ್ಕಕ್ಕೆ ಆದ್ಯತೆ ನೀಡಲಾಗಿದೆ (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"ಆಡಿಯೊ ಗುಣಮಟ್ಟಕ್ಕಾಗಿ (990kbps/909kbps) ಆಪ್ಟಿಮೈಸ್ ಮಾಡಿ"</item>
+    <item msgid="2921767058740704969">"ಸಂತುಲಿತ ಆಡಿಯೊ ಮತ್ತು ಸಂಪರ್ಕದ ಗುಣಮಟ್ಟ (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"ಸಂಪರ್ಕದ ಗುಣಮಟ್ಟಕ್ಕಾಗಿ (330kbps/303kbps) ಆಪ್ಟಿಮೈಸ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"ಧ್ವನಿಯ ಗುಣಮಟ್ಟಕ್ಕೆ ಆದ್ಯತೆ ನೀಡಲಾಗಿದೆ (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"ಪ್ರಮಾಣಿತ (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"ಸಂಪರ್ಕಕ್ಕೆ ಆದ್ಯತೆ ನೀಡಲಾಗಿದೆ (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"ಆಡಿಯೊ ಗುಣಮಟ್ಟಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಿ"</item>
+    <item msgid="4327143584633311908">"ಸಂತುಲಿತ ಆಡಿಯೊ ಮತ್ತು ಸಂಪರ್ಕದ ಗುಣಮಟ್ಟ"</item>
+    <item msgid="6155648878105378550">"ಸಂಪರ್ಕದ ಗುಣಮಟ್ಟಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ಆಫ್"</item>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 5ac99d3..99577f7 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"ಸೆಲ್ಯುಲರ್ ಡೇಟಾ ಯಾವಾಗಲೂ ಸಕ್ರಿಯ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ಸಂಪೂರ್ಣ ವಾಲ್ಯೂಮ್‌ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೋ ಕೋಡೆಕ್"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"ಪ್ರಾಶಸ್ತ್ಯದ ಬ್ಲೂಟೂತ್ A2DP ಕೋಡೆಕ್ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth ಆಡಿಯೊ ಕೋಡೆಕ್ ಆಯ್ಕೆ ಮಾಡಿ"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೋ ಮಾದರಿ ದರ"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"ಪ್ರಾಶಸ್ತ್ಯದ ಬ್ಲೂಟೂಥ್ A2DP ಕೋಡೆಕ್ ಮಾದರಿ ದರವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth ಆಡಿಯೋ ಕೋಡೆಕ್:\nಮಾದರಿ ದರ ಆಯ್ಕೆ ಮಾಡಿ"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"ಬ್ಲೂಟೂತ್‌ ಆಡಿಯೊ ಬಿಟ್ಸ್‌‌ನ ಪ್ರತಿ ಮಾದರಿ"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"ಪ್ರಾಶಸ್ತ್ಯದ ಬ್ಲೂಟೂತ್‌ A2DP ಕೋಡೆಕ್ ಬಿಟ್ಸ್‌ನ ಪ್ರತಿ ಮಾದರಿಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth ಆಡಿಯೋ ಕೋಡೆಕ್‌:\nಬಿಟ್ಸ್ ಪ್ರತಿ ಮಾದರಿ ಆಯ್ಕೆ ಮಾಡಿ"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೋ ಚಾನೆಲ್ ಮೋಡ್"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"ಪ್ರಾಶಸ್ತ್ಯದ ಬ್ಲೂಟೂತ್‌ A2DP ಕೋಡೆಕ್ ಚಾನಲ್ ಮೋಡ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"ಬ್ಲೂಟೂತ್‌ ಆಡಿಯೊ LDAC ಪ್ಲೇಬ್ಯಾಕ್ ಗುಣಮಟ್ಟ"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"ಪ್ರಾಶಸ್ತ್ಯದ ಬ್ಲೂಟೂತ್‌ A2DP ಕೋಡೆಕ್ LDAC ಪ್ಲೇಬ್ಯಾಕ್ ಗುಣಮಟ್ಟವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth ಆಡಿಯೋ ಕೋಡೆಕ್:\nಚಾನೆಲ್ ಮೋಡ್ ಆಯ್ಕೆ ಮಾಡಿ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth ಆಡಿಯೊ LDAC ಕೋಡೆಕ್: ಪ್ಲೇಬ್ಯಾಕ್ ಗುಣಮಟ್ಟ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth ಆಡಿಯೊ LDAC ಕೋಡೆಕ್:\nಪ್ಲೇಬ್ಯಾಕ್‌ ಗುಣಮಟ್ಟ ಆಯ್ಕೆ ಮಾಡಿ"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ಸ್ಟ್ರೀಮಿಂಗ್: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ವೈರ್‌ಲೆಸ್‌‌‌ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಆಯ್ಕೆಗಳನ್ನು ತೋರಿಸು"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ಲಾಗಿಂಗ್ ಮಟ್ಟನ್ನು ಹೆಚ್ಚಿಸಿ, Wi‑Fi ಆಯ್ಕೆಯಲ್ಲಿ ಪ್ರತಿಯೊಂದು SSID RSSI ತೋರಿಸಿ"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"ಸಕ್ರಿಯಗೊಂಡರೆ, Wi‑Fi ಸಿಗ್ನಲ್ ದುರ್ಬಲವಾಗಿದ್ದರೂ ಕೂಡ, ಸೆಲ್ಯುಲರ್‌ಗೆ ಡೇಟಾ ಸಂಪರ್ಕವನ್ನು ಹಸ್ತಾಂತರಿಸುವಲ್ಲಿ Wi‑Fi ಹೆಚ್ಚು ಆಕ್ರಮಣಕಾರಿಯಾಗಿರುತ್ತದೆ"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"ಮೆನು"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"ಫ್ಯಾಕ್ಟರಿ ರಿಸೆಟ್‌ಗೆ ಪಾಸ್‌ವರ್ಡ್ ನಮೂದಿಸಿ"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"ಮುಂದೆ"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"ಪಾಸ್‌ವರ್ಡ್ ಅಗತ್ಯವಿದೆ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index f709e88..3c8424a 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"DRM 콘텐츠에 대해서만 HDCP 확인 사용"</item>
     <item msgid="45075631231212732">"항상 HDCP 확인 사용"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"기본값"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"기본값"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"기본값"</item>
-    <item msgid="8895532488906185219">"44.1kHz"</item>
-    <item msgid="2909915718994807056">"48.0kHz"</item>
-    <item msgid="3347287377354164611">"88.2kHz"</item>
-    <item msgid="1234212100239985373">"96.0kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"기본값"</item>
-    <item msgid="4482862757811638365">"44.1kHz"</item>
-    <item msgid="354495328188724404">"48.0kHz"</item>
-    <item msgid="7329816882213695083">"88.2kHz"</item>
-    <item msgid="6967397666254430476">"96.0kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"기본값"</item>
-    <item msgid="5618929009984956469">"16비트/샘플"</item>
-    <item msgid="3412640499234627248">"24비트/샘플"</item>
-    <item msgid="121583001492929387">"32비트/샘플"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"기본값"</item>
-    <item msgid="4726688794884191540">"16비트/샘플"</item>
-    <item msgid="305344756485516870">"24비트/샘플"</item>
-    <item msgid="244568657919675099">"32비트/샘플"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"기본값"</item>
-    <item msgid="4106832974775067314">"모노"</item>
-    <item msgid="5571632958424639155">"스테레오"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"기본값"</item>
-    <item msgid="8900559293912978337">"모노"</item>
-    <item msgid="8883739882299884241">"스테레오"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"음질 우선(990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"표준(660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"연결 우선(330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"음질 우선(990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"표준(660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"연결 우선(330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"사용 안함"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index a21fb25..3606497 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"모바일 데이터 항상 활성화"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"절대 볼륨 사용 안함"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"블루투스 오디오 코덱"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"기본 블루투스 A2DP 코덱 선택"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"블루투스 오디오 샘플링 비율"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"기본 블루투스 A2DP 코덱 샘플링 비율 선택"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"블루투스 오디오 샘플당 비트"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"기본 블루투스 A2DP 코덱 샘플당 비트 선택"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"블루투스 오디오 채널 모드"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"기본 블루투스 A2DP 코덱 채널 모드 선택"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"블루투스 오디오 LDAC 재생 품질"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"기본 블루투스 A2DP 코덱 LDAC 재생 품질 선택"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"무선 디스플레이 인증서 옵션 표시"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi 로깅 수준을 높이고, Wi‑Fi 선택도구에서 SSID RSSI당 값을 표시합니다."</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"사용 설정하면 Wi-Fi 신호가 약할 때 데이터 연결을 Wi-Fi에서 데이터 네트워크로 더욱 적극적으로 핸드오버합니다."</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"고객센터"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"메뉴"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"데모 모드에서 초기화하려면 비밀번호 입력"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"다음"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"비밀번호 입력 필요"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml
index 84d9002..4cdee97 100644
--- a/packages/SettingsLib/res/values-ky/arrays.xml
+++ b/packages/SettingsLib/res/values-ky/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Ар дайым HDCP текшерүү колдонулсун"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Демейки"</item>
+    <item msgid="7065842274271279580">"Тутум тандаганды колдонуу (демейки)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Демейки"</item>
+    <item msgid="5062108632402595000">"Тутум тандаганды колдонуу (демейки)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Демейки"</item>
+    <item msgid="3093023430402746802">"Тутум тандаганды колдонуу (демейки)"</item>
     <item msgid="8895532488906185219">"44,1 кГц"</item>
     <item msgid="2909915718994807056">"48,0 кГц"</item>
     <item msgid="3347287377354164611">"88,2 кГц"</item>
     <item msgid="1234212100239985373">"96,0 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Демейки"</item>
+    <item msgid="3214516120190965356">"Тутум тандаганды колдонуу (демейки)"</item>
     <item msgid="4482862757811638365">"44,1 кГц"</item>
     <item msgid="354495328188724404">"48,0 кГц"</item>
     <item msgid="7329816882213695083">"88,2 кГц"</item>
     <item msgid="6967397666254430476">"96,0 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Демейки"</item>
+    <item msgid="2684127272582591429">"Тутум тандаганды колдонуу (демейки)"</item>
     <item msgid="5618929009984956469">"16 бит/үлгү"</item>
     <item msgid="3412640499234627248">"24 бит/үлгү"</item>
     <item msgid="121583001492929387">"32 бит/үлгү"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Демейки"</item>
+    <item msgid="1081159789834584363">"Тутум тандаганды колдонуу (демейки)"</item>
     <item msgid="4726688794884191540">"16 бит/үлгү"</item>
     <item msgid="305344756485516870">"24 бит/үлгү"</item>
     <item msgid="244568657919675099">"32 бит/үлгү"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Демейки"</item>
+    <item msgid="5226878858503393706">"Тутум тандаганды колдонуу (демейки)"</item>
     <item msgid="4106832974775067314">"Моно"</item>
     <item msgid="5571632958424639155">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Демейки"</item>
+    <item msgid="4118561796005528173">"Тутум тандаганды колдонуу (демейки)"</item>
     <item msgid="8900559293912978337">"Моно"</item>
     <item msgid="8883739882299884241">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Тандалган добуш сапаты (990Кб/сек./909Кб/сек.)"</item>
-    <item msgid="138837449700903545">"Стандарт (660Кб/сек./606Кб/сек.)"</item>
-    <item msgid="4777177307869441982">"Тандалган туташуу (330Кб/сек./303Кб/сек.)"</item>
+    <item msgid="3411577996960199959">"Аудио сапаты үчүн оптималдаштыруу (990кб/сек./909кб/сек.)"</item>
+    <item msgid="2921767058740704969">"Теңделген аудио жана туташуу сапаты (660кб/сек./606кб/сек.)"</item>
+    <item msgid="3682554248829489641">"Туташуу сапаты үчүн оптималдаштыруу (330кб/сек./303кб/сек.)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Тандалган добуш сапаты (990Кб/сек./909Кб/сек.)"</item>
-    <item msgid="9091111147684472529">"Стандарт (660Кб/сек./606Кб/сек.)"</item>
-    <item msgid="3367904477834831032">"Тандалган туташуу (330Кб/сек./303Кб/сек.)"</item>
+    <item msgid="7668834469173465015">"Аудио сапаты үчүн оптималдаштыруу"</item>
+    <item msgid="4327143584633311908">"Теңделген аудио жана туташуу сапаты"</item>
+    <item msgid="6155648878105378550">"Туташуу сапаты үчүн оптималдаштыруу"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Өчүк"</item>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index d1320da..6608ae5 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Уюлдук дайындар ар дайым активдүү"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Үндүн абсолюттук деңгээли өчүрүлсүн"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth аудио кодек"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Керектүү Bluetooth A2DP кодекти тандаңыз"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth аудио кодегин тандаңыз"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth аудио үлгүсүнүн ылдамдыгы"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Bluetooth A2DP кодек үлгүсүнүн керектүү ылдамдыгын тандаңыз"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth аудио кодегин тандаңыз:\nҮлгү жыштыгы"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Бир үлгүдөгү Bluetooth аудио биттери"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Бир үлгү үчүн керектүү Bluetooth A2DP кодек биттерин тандаңыз"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth аудио кодегин тандаңыз:\nБир үлгүдөгү биттер"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth аудио каналынын режими"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Bluetooth A2DP кодек каналынын керектүү режимин тандаңыз"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth аудио LDAC ойнотуу сапаты"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Bluetooth A2DP кодек үчүн керектүү LDAC ойнотуу сапатын тандаңыз"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth аудио кодегин тандаңыз:\nКанал режими"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth аудио LDAC кодеги: Ойнотуу сапаты"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth аудио LDAC кодегин тандаңыз:\nОйнотуу сапаты"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Трансляция: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Зымсыз дисплейди сертификатто мүмкүнчүлүктөрүн көргөзүү"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi Кармагычта Wi‑Fi протокол деңгээлин жогорулатуу жана ар бир SSID RSSI үчүн көрсөтүү."</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Иштетилгенде, Wi-Fi байланышы үзүл-кесил болуп жатканда, Wi-Fi дайындарды уюктук операторго өжөрлүк менен өткөрөт."</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Жардам жана жооп пикир"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Демо режиминде демейки жөндөөлөргө кайтаруу үчүн сырсөздү киргизиңиз"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Кийинки"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Сырсөз талап кылынат"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml
index 45bb579..0967128 100644
--- a/packages/SettingsLib/res/values-lo/arrays.xml
+++ b/packages/SettingsLib/res/values-lo/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"ໃຊ້ການກວດສອບ HDCP ສະເໝີ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ຄ່າເລີ່ມຕົ້ນ"</item>
+    <item msgid="7065842274271279580">"Use System Selection (Default)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX-HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ຄ່າເລີ່ມຕົ້ນ"</item>
+    <item msgid="5062108632402595000">"Use System Selection (Default)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX-HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ຄ່າເລີ່ມຕົ້ນ"</item>
+    <item msgid="3093023430402746802">"Use System Selection (Default)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ຄ່າເລີ່ມຕົ້ນ"</item>
+    <item msgid="3214516120190965356">"Use System Selection (Default)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ຄ່າເລີ່ມຕົ້ນ"</item>
+    <item msgid="2684127272582591429">"Use System Selection (Default)"</item>
     <item msgid="5618929009984956469">"16 bits/sample"</item>
     <item msgid="3412640499234627248">"24 bits/sample"</item>
     <item msgid="121583001492929387">"32 bits/sample"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ຄ່າເລີ່ມຕົ້ນ"</item>
+    <item msgid="1081159789834584363">"Use System Selection (Default)"</item>
     <item msgid="4726688794884191540">"16 bits/sample"</item>
     <item msgid="305344756485516870">"24 bits/sample"</item>
     <item msgid="244568657919675099">"32 bits/sample"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ຄ່າເລີ່ມຕົ້ນ"</item>
+    <item msgid="5226878858503393706">"Use System Selection (Default)"</item>
     <item msgid="4106832974775067314">"ໂທນດຽວ"</item>
     <item msgid="5571632958424639155">"ສະເຕຣິໂອ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ຄ່າເລີ່ມຕົ້ນ"</item>
+    <item msgid="4118561796005528173">"Use System Selection (Default)"</item>
     <item msgid="8900559293912978337">"ໂທນດຽວ"</item>
     <item msgid="8883739882299884241">"ສະເຕຣິໂອ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Sound quality preferred (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Connection preferred (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"Optimize for Audio Quality (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"Balanced Audio And Connection Quality (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"Optimize for Connection Quality (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Sound quality preferred (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Connection preferred (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"Optimize for Audio Quality"</item>
+    <item msgid="4327143584633311908">"Balanced Audio And Connection Quality"</item>
+    <item msgid="6155648878105378550">"Optimize for Connection Quality"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ປິດ"</item>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 639769a..9c493f2 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"ຂໍ້​ມູນ​ມື​ຖື​ເປີດ​ຢູ່​ສະ​ເໝີ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ປິດໃຊ້ລະດັບສຽງສົມບູນ"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Select Preferred Bluetooth A2DP Codec"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Select Bluetooth Audio Codec"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth Audio Sample Rate"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Select Preferred Bluetooth A2DP Codec Sample Rate"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Select Bluetooth Audio Codec:\nSample Rate"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth Audio Bits Per Sample"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Select Preferred Bluetooth A2DP Codec Bits Per Sample"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Select Bluetooth Audio Codec:\nBits Per Sample"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth Audio Channel Mode"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Select Preferred Bluetooth A2DP Codec Channel Mode"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth Audio LDAC Playback Quality"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Select Preferred Bluetooth A2DP Codec LDAC Playback Quality"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Select Bluetooth Audio Codec:\nChannel Mode"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC Codec: Playback Quality"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Select Bluetooth Audio LDAC Codec:\nPlayback Quality"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ສະແດງໂຕເລືອກສຳລັບການສະແດງການຮັບຮອງລະບົບໄຮ້ສາຍ"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ເພີ່ມ​ລະ​ດັບ​ການ​ເກັບ​ປະ​ຫວັດ Wi‑Fi, ສະ​ແດງ​ຕໍ່ SSID RSSI ​ໃນ​ Wi‑Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"ເມື່ອ​ເປີດ​ນຳ​ໃຊ້​ແລ້ວ, ເຄືອ​ຂ່າຍ Wi-Fi ຈະ​ຖືກ​ປ່ຽນ​ໄປ​ໃຊ້​ເຄືອ​ຂ່າຍ​ໂທ​ລະ​ສັບ​ແທນ​ຫາກ​ສັນ​ຍານ Wi-Fi ອ່ອນ"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"ຊ່ວຍເຫຼືອ &amp; ຄຳຕິຊົມ"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"ເມນູ"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"ຕໍ່ໄປ"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"​ຕ້ອງ​ໃສ່​ລະ​ຫັດ​ຜ່ານ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index b61b6d8..29a4671 100644
--- a/packages/SettingsLib/res/values-lt/arrays.xml
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Visada naudoti HDCP tikrinimą"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Numatytasis"</item>
+    <item msgid="7065842274271279580">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Numatytasis"</item>
+    <item msgid="5062108632402595000">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Numatytasis"</item>
+    <item msgid="3093023430402746802">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Numatytasis"</item>
+    <item msgid="3214516120190965356">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Numatytasis"</item>
+    <item msgid="2684127272582591429">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="5618929009984956469">"16 bitų pavyzdyje"</item>
     <item msgid="3412640499234627248">"24 bitai pavyzdyje"</item>
     <item msgid="121583001492929387">"32 bitai pavyzdyje"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Numatytasis"</item>
+    <item msgid="1081159789834584363">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="4726688794884191540">"16 bitų pavyzdyje"</item>
     <item msgid="305344756485516870">"24 bitai pavyzdyje"</item>
     <item msgid="244568657919675099">"32 bitai pavyzdyje"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Numatytasis"</item>
+    <item msgid="5226878858503393706">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="4106832974775067314">"Monofoninis garsas"</item>
     <item msgid="5571632958424639155">"Stereofoninis garsas"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Numatytasis"</item>
+    <item msgid="4118561796005528173">"Naudoti sistemos pasirink. (numatytasis)"</item>
     <item msgid="8900559293912978337">"Monofoninis garsas"</item>
     <item msgid="8883739882299884241">"Stereofoninis garsas"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Pageid. garso kok. (990 KB/s / 909 KB/s)"</item>
-    <item msgid="138837449700903545">"Įprasta (660 KB/s / 606 KB/s)"</item>
-    <item msgid="4777177307869441982">"Pageidautin. ryšys (330 KB/s / 303 KB/s)"</item>
+    <item msgid="3411577996960199959">"Optimizuoti garso kokybę (990 kbps / 909 kbps)"</item>
+    <item msgid="2921767058740704969">"Subalansuotą garso ir ryšio kokybė (660 kbps / 606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimizuoti ryšio kokybę (330 kbps / 303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Pageid. garso kok. (990 KB/s / 909 KB/s)"</item>
-    <item msgid="9091111147684472529">"Įprasta (660 KB/s / 606 KB/s)"</item>
-    <item msgid="3367904477834831032">"Pageidautin. ryšys (330 KB/s / 303 KB/s)"</item>
+    <item msgid="7668834469173465015">"Optimizuoti garso kokybę"</item>
+    <item msgid="4327143584633311908">"Subalansuota garso ir ryšio kokybė"</item>
+    <item msgid="6155648878105378550">"Optimizuoti ryšio kokybę"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Išjungta"</item>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 4616b86..3090d30 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Korinio ryšio duomenys visada aktyvūs"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Išjungti didžiausią garsą"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"„Bluetooth“ garso kodekas"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Pasirinkite pageidaujamą „Bluetooth“ A2DP kodeką"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Pasirinkite „Bluetooth“ garso kodeką"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"„Bluetooth“ garso pavyzdžio dažnis"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Pasirinkite pageidaujamą „Bluetooth“ A2DP kodeko pavyzdžio dažnį"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Pasirinkite „Bluetooth“ garso kodekas:\nimties dydis"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"„Bluetooth“ garso įrašo bitų skaičius pavyzdyje"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Pasirinkite pageidaujamą „Bluetooth“ A2DP kodeko bitų skaičių pavyzdyje"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Pasirinkite „Bluetooth“ garso kodeką:\nbitų skaičius viename pavyzdyje"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"„Bluetooth“ garso kanalo režimas"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Pasirinkite pageidaujamą „Bluetooth“ A2DP kodeko kanalo režimą"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"„Bluetooth“ garso LDAC atkūrimo kokybė"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Pasirinkite pageidaujamą „Bluetooth“ A2DP kodeko LDAC atkūrimo kokybę"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Pasirinkite „Bluetooth“ garso kodeką:\n kanalo režimas"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"„Bluetooth“ garso LDAC kodekas: atkūrimo kokybė"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Pasirinkite „Bluetooth“ garso LDAC kodeką:\natkūrimo kokybė"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Srautinis perdavimas: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Rodyti belaidžio rodymo sertifikavimo parinktis"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Padidinti „Wi‑Fi“ įrašymo į žurnalą lygį, rodyti SSID RSSI „Wi-Fi“ rinkiklyje"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Jei įgalinta ši parinktis, „Wi‑Fi“ agresyviau perduos duomenų ryšį į mobiliojo ryšio tinklą, kai „Wi‑Fi“ signalas bus silpnas"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Pagalba ir atsiliepimai"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Meniu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Įv. slapt. ir atk. gam. nust. dem. rež."</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Kitas"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Būtina nurodyti slaptažodį"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index 1d8a501..cf8b2c9 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Izmantot HDCP pārbaudi tikai DRM saturam"</item>
     <item msgid="45075631231212732">"Vienmēr izmantot HDCP pārbaudi"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Noklusējums"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Noklusējums"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Noklusējums"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Noklusējums"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Noklusējums"</item>
-    <item msgid="5618929009984956469">"16 biti iztvērumā"</item>
-    <item msgid="3412640499234627248">"24 biti iztvērumā"</item>
-    <item msgid="121583001492929387">"32 biti iztvērumā"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Noklusējums"</item>
-    <item msgid="4726688794884191540">"16 biti iztvērumā"</item>
-    <item msgid="305344756485516870">"24 biti iztvērumā"</item>
-    <item msgid="244568657919675099">"32 biti iztvērumā"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Noklusējums"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Noklusējums"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Vēlamā skaņas kvalitāte (990/909 Kb/s)"</item>
-    <item msgid="138837449700903545">"Standarta (660/606 Kb/s)"</item>
-    <item msgid="4777177307869441982">"Vēlamais savienojums (330/303 Kb/s)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Vēlamā skaņas kvalitāte (990/909 Kb/s)"</item>
-    <item msgid="9091111147684472529">"Standarta (660/606 Kb/s)"</item>
-    <item msgid="3367904477834831032">"Vēlamais savienojums (330/303 Kb/s)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Izslēgts"</item>
     <item msgid="1593289376502312923">"64 KB"</item>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index dd17509..070583e 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Vienmēr aktīvs mobilo datu savienojums"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Atspējot absolūto skaļumu"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth audio kodeks"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Atlasīt vēlamo Bluetooth A2DP kodeku"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth audio iztveršanas ātrums"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Atlasīt vēlamo Bluetooth A2DP kodeka iztveršanas ātrumu"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth audio bitu skaits iztvērumā"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Atlasīt vēlamo Bluetooth A2DP kodeka bitu skaitu iztvērumā"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth audio kanāla režīms"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Atlasīt vēlamo Bluetooth A2DP kodeka kanāla režīmu"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth audio LDAC atskaņošanas kvalitāte"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Atlasīt vēlamo Bluetooth A2DP kodeka LDAC atskaņošanas kvalitāti"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Rādīt bezvadu attēlošanas sertifikācijas iespējas"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Palieliniet Wi‑Fi reģistrēšanas līmeni; rādīt katram SSID RSSI Wi‑Fi atlasītājā."</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Ja opcija ir iespējota un Wi‑Fi signāls ir vājš, datu savienojuma pāreja no Wi-Fi uz mobilo tīklu tiks veikta agresīvāk."</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Palīdzība un atsauksmes"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Izvēlne"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Iev. paroli, lai atiest. rūpnīcas iest. dem. režīmā"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Tālāk"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Nepieciešama parole"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml
index e7a9563..e27d02d 100644
--- a/packages/SettingsLib/res/values-mk/arrays.xml
+++ b/packages/SettingsLib/res/values-mk/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Секогаш користи ХДЦП проверка"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Стандардно"</item>
+    <item msgid="7065842274271279580">"Користи избор на системот (стандардно)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Стандардно"</item>
+    <item msgid="5062108632402595000">"Користи избор на системот (стандардно)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Стандардно"</item>
+    <item msgid="3093023430402746802">"Користи избор на системот (стандардно)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Стандардно"</item>
+    <item msgid="3214516120190965356">"Користи избор на системот (стандардно)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Стандардно"</item>
-    <item msgid="5618929009984956469">"16 бита/примерок"</item>
-    <item msgid="3412640499234627248">"24 бита/примерок"</item>
-    <item msgid="121583001492929387">"32 бита/примерок"</item>
+    <item msgid="2684127272582591429">"Користи избор на системот (стандардно)"</item>
+    <item msgid="5618929009984956469">"16 бита/семпл"</item>
+    <item msgid="3412640499234627248">"24 бита/семпл"</item>
+    <item msgid="121583001492929387">"32 бита/семпл"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Стандардно"</item>
-    <item msgid="4726688794884191540">"16 бита/примерок"</item>
-    <item msgid="305344756485516870">"24 бита/примерок"</item>
-    <item msgid="244568657919675099">"32 бита/примерок"</item>
+    <item msgid="1081159789834584363">"Користи избор на системот (стандардно)"</item>
+    <item msgid="4726688794884191540">"16 бита/семпл"</item>
+    <item msgid="305344756485516870">"24 бита/семпл"</item>
+    <item msgid="244568657919675099">"32 бита/семпл"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Стандардно"</item>
+    <item msgid="5226878858503393706">"Користи избор на системот (стандардно)"</item>
     <item msgid="4106832974775067314">"Моно"</item>
     <item msgid="5571632958424639155">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Стандардно"</item>
+    <item msgid="4118561796005528173">"Користи избор на системот (стандардно)"</item>
     <item msgid="8900559293912978337">"Моно"</item>
     <item msgid="8883739882299884241">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Прет. квалитет на звук (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Стандардно (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Претпочитана врска (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"Оптимизирај за квалитет на аудиото (990 kb/s/909 kb/s)"</item>
+    <item msgid="2921767058740704969">"Балансиран квалитет на звукот и врската (660 kb/s/606 kb/s)"</item>
+    <item msgid="3682554248829489641">"Оптимизирај за квалитет на врската (330 kb/s/303 kb/s)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Прет. квалитет на звук (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Стандардно (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Претпочитана врска (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"Оптимизирај за квалитет на аудиото"</item>
+    <item msgid="4327143584633311908">"Балансиран квалитет на звукот и врската"</item>
+    <item msgid="6155648878105378550">"Оптимизирај за квалитет на врската"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Исклучено"</item>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index eb0760b..0662c8f 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Мобилниот интернет е секогаш активен"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Оневозможете апсолутна јачина на звук"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Кодек за аудио преку Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Изберете претпочитан кодек за A2DP преку Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Стапка на примерок аудио преку Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Изберете претпочитана стапка на примерок за кодек за A2DP преку Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Бита по примерок аудио преку Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Изберете претпочитани бита по примерок кодек за A2DP преку Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Изберете кодек за аудио преку Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Стапка на семпл преку Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Изберете кодек за аудио преку Bluetooth:\nСтапка на примерок"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Аудио бит-по-семпл преку Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Изберете кодек за аудио преку Bluetooth:\nБитови/примерок"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Режим на канал за аудио преку Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Изберете претпочитан режим на канал за кодек за A2DP преку Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Квалитет на репродукција LDAC на аудио преку Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Изберете претпочитан квалитет на репродукција LDAC на кодек за A2DP преку Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Изберете кодек за аудио преку Bluetooth:\nРежим на канал"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Кодек за LDAC-аудио преку Bluetooth: квалитет на репродукција"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Изберете кодек за LDAC-аудио преку Bluetooth:\nКвалитет на репродукција"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Емитување: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Покажи ги опциите за безжичен приказ на сертификат"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Зголеми Wi‑Fi ниво на пријавување, прикажи по SSID RSSI во Wi‑Fi бирач"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Кога е вклучено, Wi-Fi ќе биде поагресивно при предавање на поврзувањето со податоци на мобилната мрежа при слаб сигнал на Wi-Fi."</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Помош и повратни информации"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Мени"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Внесете лозинка за фаб. ресет. во демо"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Следно"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Потребна е лозинка"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml
index 57755ed..eb7ef5f 100644
--- a/packages/SettingsLib/res/values-ml/arrays.xml
+++ b/packages/SettingsLib/res/values-ml/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"എല്ലായ്‌പ്പോഴും HDCP പരിശോധന ഉപയോഗിക്കുക"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ഡിഫോൾട്ട്"</item>
+    <item msgid="7065842274271279580">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ഡിഫോൾട്ട്"</item>
+    <item msgid="5062108632402595000">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ഡിഫോൾട്ട്"</item>
+    <item msgid="3093023430402746802">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ഡിഫോൾട്ട്"</item>
+    <item msgid="3214516120190965356">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ഡിഫോൾട്ട്"</item>
+    <item msgid="2684127272582591429">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="5618929009984956469">"16 ബിറ്റ്/സാമ്പിൾ"</item>
     <item msgid="3412640499234627248">"24 ബിറ്റ്/സാമ്പിൾ"</item>
     <item msgid="121583001492929387">"32 ബിറ്റ്/സാമ്പിൾ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ഡിഫോൾട്ട്"</item>
+    <item msgid="1081159789834584363">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="4726688794884191540">"16 ബിറ്റ്/സാമ്പിൾ"</item>
     <item msgid="305344756485516870">"16 ബിറ്റ്/സാമ്പിൾ"</item>
     <item msgid="244568657919675099">"32 ബിറ്റ്/സാമ്പിൾ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ഡിഫോൾട്ട്"</item>
+    <item msgid="5226878858503393706">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="4106832974775067314">"മോണോ"</item>
     <item msgid="5571632958424639155">"സ്റ്റീരിയോ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ഡിഫോൾട്ട്"</item>
+    <item msgid="4118561796005528173">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
     <item msgid="8900559293912978337">"മോണോ"</item>
     <item msgid="8883739882299884241">"സ്റ്റീരിയോ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"നിർദ്ദേശിക്കുന്ന ശബ്ദ നിലവാരം (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"അടിസ്ഥാനം (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"നിർദ്ദേശിക്കുന്ന കണക്ഷൻ (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"ഓഡിയോ നിലവാരമുയർത്താൻ ഒപ്റ്റിമൈസ് ചെയ്യുക (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"സന്തുലിതമായ ‌ഓഡിയോ/കണക്ഷൻ നിലവാരം (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"കണക്ഷൻ നിലവാരമുയർത്താൻ ഒപ്റ്റിമൈസ് ചെയ്യുക (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"നിർദ്ദേശിക്കുന്ന ശബ്ദ നിലവാരം (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"അടിസ്ഥാനം (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"നിർദ്ദേശിക്കുന്ന കണക്ഷൻ (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"ശബ്ദനിലവാരമുയർത്താൻ ഒപ്റ്റിമൈസ് ചെയ്യുക"</item>
+    <item msgid="4327143584633311908">"സന്തുലിതമായ ‌ഓഡിയോ/കണക്ഷൻ നിലവാരം"</item>
+    <item msgid="6155648878105378550">"കണക്ഷൻ നിലവാരമുയർത്താൻ ഒപ്റ്റിമൈസ് ചെയ്യുക"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ഓഫ്"</item>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index b88298d..b238625 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"സെല്ലുലാർ ഡാറ്റ എല്ലായ്‌പ്പോഴും സജീവം"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"അബ്‌സൊല്യൂട്ട് വോളിയം പ്രവർത്തനരഹിതമാക്കുക"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth ഓഡിയോ കോഡെക്"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"നിർദ്ദേശിക്കുന്ന Bluetooth A2DP കോഡെക് തിരഞ്ഞെടുക്കുക"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth ഓഡിയോ കോഡെക് തിരഞ്ഞെടുക്കുക"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth ഓഡിയോ സാമ്പിൾ നിരക്ക്"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"നിർദ്ദേശിക്കുന്ന Bluetooth A2DP കോഡെക് സാമ്പിൾ നിരക്ക് തിരഞ്ഞെടുക്കുക"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth ഓഡിയോ കോഡെക് തിരഞ്ഞെടുക്കുക:\nസാമ്പിൾ നിരക്ക്"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"പ്രതി സാമ്പിളിലെ Bluetooth ഓഡിയോ ബിറ്റ് നി"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"പ്രതി സാമ്പിളിന് നിർദ്ദേശിക്കുന്ന Bluetooth A2DP കോഡെക് ബിറ്റ് നിരക്ക് തിരഞ്ഞെടുക്കുക"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth ഓഡിയോ കോഡെക് തിരഞ്ഞെടുക്കുക:\nബിറ്റ്‌/സാമ്പിൾ"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth ഓഡിയോ ചാനൽ മോഡ്"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"നിർദ്ദേശിക്കുന്ന Bluetooth A2DP കോഡെക് ചാനൽ മോഡ് തിരഞ്ഞെടുക്കുക"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth ഓഡിയോ LDAC പ്ലേബാക്ക് നിലവാരം"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"നിർദ്ദേശിക്കുന്ന Bluetooth A2DP കോഡെക് LDAC പ്ലേബാക്ക് നിലവാരം തിരഞ്ഞെടുക്കുക"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth ഓഡിയോ കോഡെക് തിരഞ്ഞെടുക്കുക:\nചാനൽ മോഡ്"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth ഓഡിയോ LDAC കോഡെക്: പ്ലേബാക്ക് ‌നിലവാരം"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth ഓഡിയോ LDAC കോഡെക് തിരഞ്ഞെടുക്കുക:\nപ്ലേബാക്ക് ‌നിലവാരം"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"സ്ട്രീമിംഗ്: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"വയർലെസ് ഡിസ്‌പ്ലേ സർട്ടിഫിക്കേഷനായി ഓപ്‌ഷനുകൾ ദൃശ്യമാക്കുക"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"വൈഫൈ പിക്കറിൽ ഓരോ SSID RSSI പ്രകാരം കാണിക്കാൻ വൈഫൈ ലോഗിംഗ് നില വർദ്ധിപ്പിക്കുക"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"പ്രവർത്തനക്ഷമമായിരിക്കുമ്പോൾ, വൈഫൈ സിഗ്‌നൽ കുറവായിരിക്കുന്ന സമയത്ത് സെല്ലുലാറിലേക്ക് ഡാറ്റ കണക്ഷൻ മുഖേന കൈമാറുന്നതിൽ വൈഫൈ കൂടുതൽ പ്രവർത്തനക്ഷമമാകും"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"സഹായവും പ്രതികരണവും"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"മെനു"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"ഡെമോ ‌മോഡിൽ ഫാക്ടറി റീസെറ്റിന് പാസ്‌വേഡ് നൽകുക"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"അടുത്തത്"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"പാസ്‌വേഡ് ആവശ്യമാണ്"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml
index 88d6517..8902946 100644
--- a/packages/SettingsLib/res/values-mn/arrays.xml
+++ b/packages/SettingsLib/res/values-mn/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Байнга HDCP шалгахыг ашиглах"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Өгөгдмөл"</item>
+    <item msgid="7065842274271279580">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Өгөгдмөл"</item>
+    <item msgid="5062108632402595000">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Өгөгдмөл"</item>
+    <item msgid="3093023430402746802">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="8895532488906185219">"44.1 кГц"</item>
     <item msgid="2909915718994807056">"48.0 кГц"</item>
     <item msgid="3347287377354164611">"88.2 кГц"</item>
     <item msgid="1234212100239985373">"96.0 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Өгөгдмөл"</item>
+    <item msgid="3214516120190965356">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="4482862757811638365">"44.1 кГц"</item>
     <item msgid="354495328188724404">"48.0 кГц"</item>
     <item msgid="7329816882213695083">"88.2 кГц"</item>
     <item msgid="6967397666254430476">"96.0 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Өгөгдмөл"</item>
+    <item msgid="2684127272582591429">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="5618929009984956469">"16 бит/жишээ"</item>
     <item msgid="3412640499234627248">"24 бит/жишээ"</item>
     <item msgid="121583001492929387">"32 бит/жишээ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Өгөгдмөл"</item>
+    <item msgid="1081159789834584363">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="4726688794884191540">"16 бит/жишээ"</item>
     <item msgid="305344756485516870">"24 бит/жишээ"</item>
     <item msgid="244568657919675099">"32 бит/жишээ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Өгөгдмөл"</item>
+    <item msgid="5226878858503393706">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="4106832974775067314">"Моно"</item>
     <item msgid="5571632958424639155">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Өгөгдмөл"</item>
+    <item msgid="4118561796005528173">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
     <item msgid="8900559293912978337">"Моно"</item>
     <item msgid="8883739882299884241">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Давуу дууны чанар (990кб/с/909кб/с)"</item>
-    <item msgid="138837449700903545">"Стандарт (660кб/с/606кб/с)"</item>
-    <item msgid="4777177307869441982">"Давуу холболт (330кб/с/303kbps)"</item>
+    <item msgid="3411577996960199959">"Аудио чанарт оновчлох (990кб/цаг/909кб/цаг)"</item>
+    <item msgid="2921767058740704969">"Аудио, холболтын чанарыг тэнцүүлсэн (660кб/цаг/606кб/цаг)"</item>
+    <item msgid="3682554248829489641">"Холболтын чанарт оновчлох (330кб/цаг/303кб/цаг)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Давуу дууны чанар (990кб/с/909кб/с)"</item>
-    <item msgid="9091111147684472529">"Стандарт (660кб/с/606кб/с)"</item>
-    <item msgid="3367904477834831032">"Давуу холболт (330кб/с/303kbps)"</item>
+    <item msgid="7668834469173465015">"Аудио чанарт оновчлох"</item>
+    <item msgid="4327143584633311908">"Аудио, холболтын чанарыг тэнцүүлсэн"</item>
+    <item msgid="6155648878105378550">"Холболтын чанарт оновчлох"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Идэвхгүй"</item>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index f412ac4..68f3df5 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Үүрэн холбооны датаг үргэлж идэвхтэй байлгана"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Үнэмлэхүй дууны түвшинг идэвхгүй болгох"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth аудио кодлогч"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Давуу Bluetooth A2DP кодлогч сонгох"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth аудио кодлогч сонгох"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth аудио жишээний үнэлгээ"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Давуу Bluetooth A2DP кодлогч жишээний үнэлгээ"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth аудио кодлогч сонгох:\nЖишээний хэмжээ"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Жишээ тутмын Bluetooth аудионы бит"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Жишээ тутмын давуу Bluetooth A2DP кодлогч битийг сонгох"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth аудио кодлогч сонгох:\nЖишээ бүрт бит"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth аудио сувгийн горим"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Давуу Bluetooth A2DP кодлогч сувгийн горимыг сонгох"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth аудио LDAC тоглуулагчийн чанар"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Давуу Bluetooth A2DP кодлогч LDAC тоглуулагчийн чанарыг сонгох"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth аудио кодлогч сонгох:\nСувгийн горим"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Аудио LDAC Кодлогч: Тоглуулагчийн чанар"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth Аудио LDAC Кодлогч сонгох:\nТоглуулагчийн чанар"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Дамжуулж байна: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Утасгүй дэлгэцийн сертификатын сонголтыг харуулах"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi лог-н түвшинг нэмэгдүүлэх, Wi‑Fi Сонгогч дээрх SSID-д ногдох RSSI-г харуулах"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Идэвхжүүлсэн үед Wi‑Fi дохио сул бол дата холболтыг Үүрэн рүү шилжүүлэхдээ илүү идэвхтэй байх болно"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Тусламж, санал хүсэлт"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Цэс"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"Гринвичийн цаг"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Үйлдвэрийн тохиргоог демо горимд ажиллуулахын тулд нууц үг оруулна уу"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Дараагийн"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Нууц үг шаардлагатай"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index 9204c458..f6ebf83 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"नेहमी HDCP तपासणी वापरा"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"डीफॉल्ट"</item>
+    <item msgid="7065842274271279580">"प्रणाली निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX-HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"डीफॉल्ट"</item>
+    <item msgid="5062108632402595000">"प्रणाली निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX-HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"डीफॉल्ट"</item>
+    <item msgid="3093023430402746802">"प्रणाली निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"डीफॉल्ट"</item>
+    <item msgid="3214516120190965356">"प्रणाली निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"डीफॉल्ट"</item>
+    <item msgid="2684127272582591429">"प्रणाली निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="5618929009984956469">"16 बिट/नमुना"</item>
     <item msgid="3412640499234627248">"24 बिट/नमुना"</item>
     <item msgid="121583001492929387">"32 बिट/नमुना"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"डीफॉल्ट"</item>
+    <item msgid="1081159789834584363">"प्रणाली निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="4726688794884191540">"16 बिट/नमुना"</item>
     <item msgid="305344756485516870">"24 बिट/नमुना"</item>
     <item msgid="244568657919675099">"32 बिट/नमुना"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"डीफॉल्ट"</item>
+    <item msgid="5226878858503393706">"प्रणाली निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="4106832974775067314">"मोनो"</item>
     <item msgid="5571632958424639155">"स्टिरिओ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"डीफॉल्ट"</item>
+    <item msgid="4118561796005528173">"प्रणाली निवड वापरा (डीफॉल्ट)"</item>
     <item msgid="8900559293912978337">"मोनो"</item>
     <item msgid="8883739882299884241">"स्टिरिओ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"प्राधान्य दिलेली ध्वनी गुणवत्ता (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"मानक (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"प्राधान्य दिलेलेे कनेेक्शन (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"ऑडिओ गुणवत्तेसाठी (990kbps/909kbps) ऑप्टिमाइझ करा"</item>
+    <item msgid="2921767058740704969">"संतुलित ऑडिओ आणि कनेक्शन गुणवत्ता (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"कनेक्शन गुणवत्तेसाठी (330kbps/303kbps) ऑप्टिमाइझ करा"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"प्राधान्य दिलेली ध्वनी गुणवत्ता (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"मानक (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"प्राधान्य दिलेलेे कनेेक्शन (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"ऑडिओ गुणवत्तेसाठी ऑप्टिमाइझ करा"</item>
+    <item msgid="4327143584633311908">"संतुलित ऑडिओ आणि कनेक्शन गुणवत्ता"</item>
+    <item msgid="6155648878105378550">"कनेक्शन गुणवत्तेसाठी ऑप्टिमाइझ करा"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"बंद"</item>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 8b73d21..a91ad45 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"सेल्युलर डेटा नेहमी सक्रिय"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"संपूर्ण आवाज अक्षम करा"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ब्लूटूथ ऑडिओ कोडेक"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"प्राधान्यीकृत ब्लूटुथ A2DP कोडेक निवडा"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth ऑडिओ कोडेक निवडा"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"ब्लूटूथ ऑडिओ नमुना दर"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"प्राधान्यीकृत ब्लूटुथ A2DP कोडेक नमुना दर निवडा"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth ऑडिओ कोडेक निवडा:\nनमुना दर"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"प्रति नमुना ब्लूटुथ ऑडिओ बिट"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"प्रति नमुना प्राधान्यीकृत ब्लूटुथ A2DP कोडेक बिट निवडा"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth ऑडिओ कोडेक निवडा:\nबिट प्रति नमुना"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ब्लूटूथ ऑडिओ चॅनेल मोड"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"प्राधान्यीकृत ब्लूटुथ A2DP कोडेक चॅनेल मोड निवडा"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"ब्लूटुथ ऑडिओ LDAC प्लेबॅक गुणवत्ता"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"प्राधान्यीकृत ब्लूटुथ A2DP कोडेक LDAC प्लेबॅक गुणवत्ता निवडा"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth ऑडिओ कोडेक निवडा:\nचॅनेल मोड"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth ऑडिओ LDAC कोडेक: प्लेबॅक गुणवत्ता"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Bluetooth ऑडिओ LDAC कोडेक निवडा:\nप्लेबॅक गुणवत्ता"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"धारावाहिक: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस प्रदर्शन प्रमाणिकरणासाठी पर्याय दर्शवा"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"वाय-फाय लॉगिंग स्‍तर वाढवा, वाय-फाय निवडकामध्‍ये प्रति SSID RSSI दर्शवा"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"सक्षम केल्यास, वाय-फाय सिग्‍नल निम्‍न असताना, वाय-फाय डेटा कनेक्‍शन सेल्‍युलरवर बळपूर्वक स्विच करेल."</string>
@@ -352,4 +353,10 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"मदत आणि अभिप्राय"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"मेनू"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <!-- no translation found for retail_demo_reset_message (118771671364131297) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_next (8356731459226304963) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_title (696589204029930100) -->
+    <skip />
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml
index 7ec6fab..5feff37 100644
--- a/packages/SettingsLib/res/values-ms/arrays.xml
+++ b/packages/SettingsLib/res/values-ms/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Gunakan penyemakan HDCP untuk kandungan DRM sahaja"</item>
     <item msgid="45075631231212732">"Sentiasa gunakan penyemakan HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Lalai"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Lalai"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Lalai"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Lalai"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Lalai"</item>
-    <item msgid="5618929009984956469">"16 bit/sampel"</item>
-    <item msgid="3412640499234627248">"24 bit/sampel"</item>
-    <item msgid="121583001492929387">"32 bit/sampel"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Lalai"</item>
-    <item msgid="4726688794884191540">"16 bit/sampel"</item>
-    <item msgid="305344756485516870">"24 bit/sampel"</item>
-    <item msgid="244568657919675099">"32 bit/sampel"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Lalai"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Lalai"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Kualiti bunyi dipilih (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Sambungan yang dipilih (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Kualiti bunyi dipilih (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Sambungan yang dipilih (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Mati"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index e57219c..a3459772 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Data selular sentiasa aktif"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Lumpuhkan kelantangan mutlak"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Pilih Codec A2DP Bluetooth yang Diingini"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Kadar Sampel Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Pilih Kadar Sampel Codec A2DP Bluetooth yang Diingini"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bit Per Sampel Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Pilih Bit Per Sampel Codec A2DP Bluetooth yang Diingini"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Mod Saluran Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Pilih Mod Saluran Codec A2DP Bluetooth yang Diingini"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Kualiti Main Balik LDAC Audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Pilih Kualiti Main Balik LDAC Codec A2DP Bluetooth yang Diingini"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Tunjukkan pilihan untuk pensijilan paparan wayarles"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tingkatkan tahap pengelogan Wi-Fi, tunjuk setiap SSID RSSI dalam Pemilih Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Apabila didayakan, Wi-Fi akan menjadi lebih agresif dalam menyerahkan sambungan data ke Selular, apabila isyarat Wi-Fi rendah"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Bantuan &amp; maklum balas"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Mskkn kta laluan utk ttpn sml kilang dlm mod demo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Seterusnya"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Kata laluan diperlukan"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml
index a43eb94..ddd0a64 100644
--- a/packages/SettingsLib/res/values-my/arrays.xml
+++ b/packages/SettingsLib/res/values-my/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"HDCP checkingအားအမြဲသုံးပါ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"မူရင်း"</item>
+    <item msgid="7065842274271279580">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"မူရင်း"</item>
+    <item msgid="5062108632402595000">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"မူရင်း"</item>
+    <item msgid="3093023430402746802">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="8895532488906185219">"၄၄.၁ kHz"</item>
     <item msgid="2909915718994807056">"၄၈.၀ kHz"</item>
     <item msgid="3347287377354164611">"၈၈.၂ kHz"</item>
     <item msgid="1234212100239985373">"၉၆.၀ kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"မူရင်း"</item>
+    <item msgid="3214516120190965356">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="4482862757811638365">"၄၄.၁ kHz"</item>
     <item msgid="354495328188724404">"၄၈.၀ kHz"</item>
     <item msgid="7329816882213695083">"၈၈.၂ kHz"</item>
     <item msgid="6967397666254430476">"၉၆.၀ kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"မူရင်း"</item>
+    <item msgid="2684127272582591429">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="5618929009984956469">"၁၆ bits/နမူနာ"</item>
     <item msgid="3412640499234627248">"၂၄ bits/နမူနာ"</item>
     <item msgid="121583001492929387">"၃၂ bits/နမူနာ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"မူရင်း"</item>
+    <item msgid="1081159789834584363">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="4726688794884191540">"၁၆ bits/နမူနာ"</item>
     <item msgid="305344756485516870">"၂၄ bits/နမူနာ"</item>
     <item msgid="244568657919675099">"၃၂ bits/နမူနာ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"မူရင်း"</item>
+    <item msgid="5226878858503393706">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="4106832974775067314">"မိုနို"</item>
     <item msgid="5571632958424639155">"စတီရီယို"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"မူရင်း"</item>
+    <item msgid="4118561796005528173">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
     <item msgid="8900559293912978337">"မိုနို"</item>
     <item msgid="8883739882299884241">"စတီရီယို"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"ပိုမိုနှစ်သက်သည့် အသံအရည်အသွေး (၉၉၀kbps/၉၀၉kbps)"</item>
-    <item msgid="138837449700903545">"ပုံမှန် (၆၆၀kbps/၆၀၆kbps)"</item>
-    <item msgid="4777177307869441982">"ပိုမိုနှစ်သက်သည့် မြန်နှုန်း (၃၃၀kbps/၃၀၃kbps)"</item>
+    <item msgid="3411577996960199959">"အသံအရည်အသွေးကို ပိုကောင်းအောင် ပြုလုပ်ပေးသည် (၉၉၀kbps/၉၀၉kbps)"</item>
+    <item msgid="2921767058740704969">"အသံနှင့် ချိတ်ဆက်မှု အရည်အသွေးကို မျှတအောင် ချိန်ဆပေးသည် (၆၆၀kbps/၆၀၆kbps)"</item>
+    <item msgid="3682554248829489641">"ချိတ်ဆက်မှု အရည်အသွေးကို ပိုကောင်းအောင် ပြုလုပ်ပေးသည် (၃၃၀kbps/၃၀၃kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"ပိုမိုနှစ်သက်သည့် အသံအရည်အသွေး (၉၉၀kbps/၉၀၉kbps)"</item>
-    <item msgid="9091111147684472529">"ပုံမှန် (၆၆၀kbps/၆၀၆kbps)"</item>
-    <item msgid="3367904477834831032">"ပိုမိုနှစ်သက်သည့် ချိတ်ဆက်မှု (၃၃၀kbps/၃၀၃kbps)"</item>
+    <item msgid="7668834469173465015">"အသံအရည်အသွေးကို ပိုကောင်းအောင် ပြုလုပ်ပေးသည်"</item>
+    <item msgid="4327143584633311908">"အသံနှင့် ချိတ်ဆက်မှု အရည်သွေးကို မျှတအောင် ချိန်ဆပေးသည်"</item>
+    <item msgid="6155648878105378550">"ချိတ်ဆက်မှု အရည်အသွေးကို ပိုကောင်းအောင် ပြုလုပ်ပေးသည်"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ပိတ်ပါ"</item>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 3b70cc3..e2850ae 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"ဆဲလ်လူလာဒေတာ အမြဲတမ်းဖွင့်ထားသည်"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ပကတိ အသံနှုန်း သတ်မှတ်ချက် ပိတ်ရန်"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ဘလူးတုသ်အသံ ကိုးဒက်ခ်"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"ပိုမိုနှစ်သက်သည့် ဘလူးတုသ် A2DP ကိုးဒက်ခ်ကို ရွေးချယ်ပါ"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"ဘလူးတုသ်အသံကိုးဒက်ခ်ကို ရွေးပါ"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"ဘလူးတုသ်အသံနမူနာနှုန်း"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"ပိုမိုနှစ်သက်သည့် ဘလူးတုသ် A2DP ကိုးဒက်ခ် အသံနမူနာနှုန်းကို ရွေးချယ်ပါ"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"ဘလူးတုသ်အသံ ကိုးဒက်ခ်ကို ရွေးပါ−\nနမူနာနှုန်း"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"နမူနာတစ်ခုစီတွင် ပါဝင်သော ဘလူးတုသ်အသံပမာဏ Bits"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"ပိုမိုနှစ်သက်သည့် နမူနာတစ်ခုစီတွင် ပါဝင်သော ဘလူးတုသ် A2DP ကိုးဒက်ခ် Bits ကို ရွေးပါ"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"ဘလူးတုသ်အသံ ကိုးဒက်ခ်ကို ရွေးပါ−\nနမူနာတစ်ခုချင်းအတွက် Bits"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ဘလူးတုသ်အသံချန်နယ်မုဒ်"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"ပိုမိုနှစ်သက်သည့် ဘလူးတုသ် A2DP ကိုးဒက်ခ် ချန်နယ်မုဒ်ကိုရွေးချယ်ပါ"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"ဘလူးတုသ်အသံ LDAC ကြည့်ရန် အရည်အသွေး"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"ပိုမိုနှစ်သက်သည့် ဘလူးတုသ်အသံ LDAC ကြည့်ရန် အရည်အသွေးကို ရွေးချယ်ပါ"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"ဘလူးတုသ်အသံကိုးဒက်ခ်ကို ရွေးပါ−\nချန်နယ်မုဒ်"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ဘလူးတုသ်အသံ LDAC ကိုးဒက်ခ်− နားထောင်ရန် အရည်အသွေး"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ဘလူးတုသ်အသံ LDAC ကိုးဒက်ခ်ကို ရွေးပါ−\nနားထောင်ရန် အရည်အသွေး"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"တိုက်ရိုက်လွှင့်နေသည်− <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ကြိုးမဲ့ အခင်းအကျင်း အသိအမှတ်ပြုလက်မှတ်အတွက် ရွေးချယ်စရာများပြရန်"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi မှတ်တမ်းတင်ခြင်း နှုန်းအားမြင့်ကာ၊ Wi‑Fi ရွေးရာတွင် SSID RSSI ဖြင့်ပြပါ"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"ဖွင့်ထားလျှင်၊ Wi‑Fi မှ ဆယ်လူလာသို့ အချက်လက် ချိတ်ဆက်မှုအား လွှဲပြောင်းရာ၌ ပိုမိုထိရောက်ပါသည်၊ WIFI အားနည်းနေချိန်တွင်"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"အကူအညီနှင့် အကြံပြုချက်"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"မီနူး"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"ဂရင်းနစ်စံတော်ချိန်"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"ဒီမိုမုဒ်၌နဂိုဆက်တင်ထားရန်စကားဝှက်ထည့်ပါ"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"ရှေ့သို့"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"စကားဝှက် လိုအပ်သည်"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
index bf0b414..bca999c 100644
--- a/packages/SettingsLib/res/values-nb/arrays.xml
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Bruk alltid HDCP-kontroll"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Standard"</item>
+    <item msgid="7065842274271279580">"Bruk systemvalg (standard)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Standard"</item>
+    <item msgid="5062108632402595000">"Bruk systemvalg (standard)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Standard"</item>
+    <item msgid="3093023430402746802">"Bruk systemvalg (standard)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Standard"</item>
+    <item msgid="3214516120190965356">"Bruk systemvalg (standard)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Standard"</item>
+    <item msgid="2684127272582591429">"Bruk systemvalg (standard)"</item>
     <item msgid="5618929009984956469">"16 bits/sample"</item>
     <item msgid="3412640499234627248">"24 bits/sample"</item>
     <item msgid="121583001492929387">"32 bits/sample"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Standard"</item>
+    <item msgid="1081159789834584363">"Bruk systemvalg (standard)"</item>
     <item msgid="4726688794884191540">"16 bits/sample"</item>
     <item msgid="305344756485516870">"24 bits/sample"</item>
     <item msgid="244568657919675099">"32 bits/sample"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Standard"</item>
+    <item msgid="5226878858503393706">"Bruk systemvalg (standard)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Standard"</item>
+    <item msgid="4118561796005528173">"Bruk systemvalg (standard)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Prioriter lydkvalitet (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Prioriter tilkobling (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"Optimaliser for lydkvalitet (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Balansert lyd- og tilkoblingskvalitet (660 kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"Optimaliser for tilkoblingskvalitet (330 kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Prioriter lydkvalitet (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Prioriter tilkobling (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"Optimaliser for lydkvalitet"</item>
+    <item msgid="4327143584633311908">"Balansert lyd- og tilkoblingskvalitet"</item>
+    <item msgid="6155648878105378550">"Optimaliser for tilkoblingskvalitet"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Av"</item>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 53e0127..172d59c 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobildata er alltid aktiv"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Slå av funksjonen for absolutt volum"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Kodek for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Velg foretrukket A2DP-kodek for Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Velg kodek for Bluetooth-lyd"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Samplefrekvens for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Velg foretrukket samplefrekvens fra A2DP-kodek for Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Velg kodek for Bluetooth-lyd:\nSamplefrekvens"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits per sample for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Velg foretrukket bits per sample fra A2DP-kodek for Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Velg lydkodek for Bluetooth:\nBits Per Sample"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Kanalmodus for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Velg foretrukket kanalmodus fra A2DP-kodek for Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"LDAC-avspillingskvalitet for Bluetooth-lyd"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Velg foretrukket LDAC-avspillingskvalitet fra A2DP-kodek for Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Velg lydkodek for Bluetooth:\nKanalmodus"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"LDAC-kodek for Bluetooth-lyd: Avspillingskvalitet"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Velg LDAC-kodek for Bluetooth-lyd:\nAvspillingskvalitet"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Strømming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vis alternativer for sertifisering av trådløs skjerm"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Øk Wi-Fi-loggenivå – vis per SSID RSSI i Wi-Fi-velgeren"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Hvis dette slås på, overfører Wi-Fi-nettverket datatilkoblingen til mobil mer aggressivt når Wi-Fi-signalet er lavt"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Hjelp og tilbakemelding"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Meny"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Skriv inn passordet for å tilbakestille til fabrikkstandard i demomodus"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Neste"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Passord er obligatorisk"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index d9cd5d2..ddde4e2 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"सधैँ HDCP जाँच प्रयोग गर्नुहोस्"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"पूर्वनिर्धारित मान"</item>
+    <item msgid="7065842274271279580">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"पूर्वनिर्धारित मान"</item>
+    <item msgid="5062108632402595000">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"पूर्वनिर्धारित मान"</item>
+    <item msgid="3093023430402746802">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="8895532488906185219">"४४.१ kHz"</item>
     <item msgid="2909915718994807056">"४८.० kHz"</item>
     <item msgid="3347287377354164611">"८८.२ kHz"</item>
     <item msgid="1234212100239985373">"९६.० kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"पूर्वनिर्धारित मान"</item>
+    <item msgid="3214516120190965356">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="4482862757811638365">"४४.१ kHz"</item>
     <item msgid="354495328188724404">"४८.० kHz"</item>
     <item msgid="7329816882213695083">"८८.२ kHz"</item>
     <item msgid="6967397666254430476">"९६.० kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"पूर्वनिर्धारित मान"</item>
+    <item msgid="2684127272582591429">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="5618929009984956469">"१६ बिट/नमूना"</item>
     <item msgid="3412640499234627248">"२४ बिट/नमूना"</item>
     <item msgid="121583001492929387">"३२ बिट/नमूना"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"पूर्वनिर्धारित मान"</item>
+    <item msgid="1081159789834584363">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="4726688794884191540">"१६ बिट/नमूना"</item>
     <item msgid="305344756485516870">"२४ बिट/नमूना"</item>
     <item msgid="244568657919675099">"३२ बिट/नमूना"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"पूर्वनिर्धारित मान"</item>
+    <item msgid="5226878858503393706">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="4106832974775067314">"मोनो"</item>
     <item msgid="5571632958424639155">"स्टेरियो"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"पूर्वनिर्धारित मान"</item>
+    <item msgid="4118561796005528173">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item>
     <item msgid="8900559293912978337">"मोनो"</item>
     <item msgid="8883739882299884241">"स्टेरियो"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"रुचाइएको आवाजको गुणस्तर (९९०kbps/९०९kbps)"</item>
-    <item msgid="138837449700903545">"मानक (६६०kbps/६०६kbps)"</item>
-    <item msgid="4777177307869441982">"रुचाइएको जडान (३३०kbps/३०३kbps)"</item>
+    <item msgid="3411577996960199959">"अडियोको गुणस्तर सुधार्न अनुकूलन गर्नुहोस् (९९०kbps/९०९kbps)"</item>
+    <item msgid="2921767058740704969">"सन्तुलित अडियो र जडान गुणस्तर (६६०kbps/६०६kbps)"</item>
+    <item msgid="3682554248829489641">"जडानको गुणस्तर सुधार्न अनुकूलन गर्नुहोस् (३३०kbps/३०३kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"रुचाइएको आवाजको गुणस्तर (९९०kbps/९०९kbps)"</item>
-    <item msgid="9091111147684472529">"मानक (६६०kbps/६०६kbps)"</item>
-    <item msgid="3367904477834831032">"रुचाइएको जडान (३३०kbps/३०३kbps)"</item>
+    <item msgid="7668834469173465015">"अडियोको गुणस्तर सुधार्न अनुकूलन गर्नुहोस्"</item>
+    <item msgid="4327143584633311908">"सन्तुलित अडियो र जडान गुणस्तर"</item>
+    <item msgid="6155648878105378550">"जडानको गुणस्तर सुधार्न अनुकूलन गर्नुहोस्"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"निष्क्रिय गर्नुहोस्"</item>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 311af42..376fe9c 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"सेलुलर डेटा सधैं सक्रिय"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"निरपेक्ष आवाज असक्षम गर्नुहोस्"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ब्लुटुथ अडियोको कोडेक"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"रुचाइको ब्लुटुथ A2DP कोडेक चयन गर्नुहोस्"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"ब्लुटुथ अडियोको कोडेक चयन गर्नुहोस्‌"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"ब्लुटुथ अडियोको नमूना दर"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"रुचाइको ब्लुटुथ A2DP कोडेक नमूना दर चयन गर्नुहोस्"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"ब्लुटुथ अडियोको कोडेक चयन गर्नुहोस्‌:\nनमूना दर"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"प्रति नमूना ब्लुटुथ अडियोका बिटहरू"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"रुचाइको प्रति नमूना ब्लुटुथ A2DP कोडेकको बिट चयन गर्नुहोस्"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"ब्लुटुथ अडियोको कोडेक चयन गर्नुहोस्‌:\n प्रति नमूना बिट"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ब्लुटुथ अडियो च्यानलको मोड"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"रुचाइको ब्लुटुथ A2DP कोडेक च्यानलको मोड चयन गर्नुहोस्"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"ब्लुटुथ अडियो LDAC प्लेब्याकको गुणस्तर"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"रुचाइको ब्लुटुथ A2DP कोडेक LDAC प्लेब्याकको गुणस्तर चयन गर्नुहोस्"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"ब्लुटुथ अडियोको कोडेक चयन गर्नुहोस्‌:\nच्यानलको मोड"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ब्लुटुथ अडियो LDAC कोडेक: प्लेब्याक गुणस्तर"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ब्लुटुथ अडियो LDAC कोडेक चयन गर्नुहोस्‌:\nप्लेब्याक गुणस्तर"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"स्ट्रिमिङ: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Wi-Fi संकेत कम हुँदा, सक्षम जब गरिन्छ, Wi-Fi सेलुलर लागि डेटा जडान सुम्पनामा बढी आक्रामक हुनेछ"</string>
@@ -352,4 +353,10 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"मद्दत र प्रतिक्रिया"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"मेनु"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <!-- no translation found for retail_demo_reset_message (118771671364131297) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_next (8356731459226304963) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_title (696589204029930100) -->
+    <skip />
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
index 6eb2b67..e1822c6 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"HDCP-controle altijd gebruiken"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Standaard"</item>
+    <item msgid="7065842274271279580">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Standaard"</item>
+    <item msgid="5062108632402595000">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Standaard"</item>
+    <item msgid="3093023430402746802">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Standaard"</item>
+    <item msgid="3214516120190965356">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Standaard"</item>
+    <item msgid="2684127272582591429">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="5618929009984956469">"16 bits per sample"</item>
     <item msgid="3412640499234627248">"24 bits per sample"</item>
     <item msgid="121583001492929387">"32 bits per sample"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Standaard"</item>
+    <item msgid="1081159789834584363">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="4726688794884191540">"16 bits per sample"</item>
     <item msgid="305344756485516870">"24 bits per sample"</item>
     <item msgid="244568657919675099">"32 bits per sample"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Standaard"</item>
+    <item msgid="5226878858503393706">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Standaard"</item>
+    <item msgid="4118561796005528173">"Systeemselectie gebruiken (standaard)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Voorkeur geluidskwaliteit (990/909 kbps)"</item>
-    <item msgid="138837449700903545">"Standaard (660/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Voorkeursverbinding (330/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Optimaliseren voor audiokwaliteit (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Gebalanceerde audio- en verbindingskwaliteit (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimaliseren voor verbindingskwaliteit (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Voorkeur geluidskwaliteit (990/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Standaard (660/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Voorkeursverbinding (330/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Optimaliseren voor audiokwaliteit"</item>
+    <item msgid="4327143584633311908">"Gebalanceerde audio- en verbindingskwaliteit"</item>
+    <item msgid="6155648878105378550">"Optimaliseren voor verbindingskwaliteit"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Uit"</item>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 698ff09..30439710 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobiele data altijd actief"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Absoluut volume uitschakelen"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth-audiocodec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Voorkeur voor Bluetooth A2DP-codec selecteren"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth-audiocodec selecteren"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bemonsteringsfrequentie (sample rate) van Bluetooth-audio"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Voorkeur voor bemonsteringsfrequentie (sample rate) voor Bluetooth A2DP-codec selecteren"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth-audiocodec selecteren:\nbemonsteringsfrequentie"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits per sample voor Bluetooth-audio"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Voorkeur voor bits per sample voor Bluetooth A2DP-codec selecteren"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth-audiocodec selecteren:\nbit per sample"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Kanaalmodus voor Bluetooth-audio"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Voorkeur voor kanaalmodus voor Bluetooth A2DP-codec selecteren"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"LDAC-afspeelkwaliteit voor Bluetooth-audio"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Voorkeur voor LDAC-afspeelkwaliteit voor Bluetooth A2DP-codec selecteren"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth-audiocodec selecteren:\nkanaalmodus"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"LDAC-codec voor Bluetooth-audio: afspeelkwaliteit"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"LDAC-codec voor Bluetooth-audio selecteren:\nafspeelkwaliteit"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Opties weergeven voor certificering van draadloze weergave"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Logniveau voor wifi verhogen, weergeven per SSID RSSI in wifi-kiezer"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Indien ingeschakeld, is wifi agressiever bij het overgeven van de gegevensverbinding aan mobiel wanneer het wifi-signaal zwak is"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Help en feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Geef wachtwoord op om terug te zetten op fabrieksinstellingen in demomodus"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Volgende"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Wachtwoord vereist"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml
index a53d534..ed44102 100644
--- a/packages/SettingsLib/res/values-pa/arrays.xml
+++ b/packages/SettingsLib/res/values-pa/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"ਹਮੇਸਾਂ HDCP ਜਾਂਚ ਵਰਤੋ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</item>
+    <item msgid="7065842274271279580">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</item>
+    <item msgid="5062108632402595000">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</item>
+    <item msgid="3093023430402746802">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</item>
+    <item msgid="3214516120190965356">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</item>
-    <item msgid="5618929009984956469">"16 ਬਿੱਟ/ਨਮੂਨਾ"</item>
-    <item msgid="3412640499234627248">"24 ਬਿੱਟ/ਨਮੂਨਾ"</item>
-    <item msgid="121583001492929387">"32 ਬਿੱਟ/ਨਮੂਨਾ"</item>
+    <item msgid="2684127272582591429">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
+    <item msgid="5618929009984956469">"16 ਬਿਟਾਂ/ਨਮੂਨਾ"</item>
+    <item msgid="3412640499234627248">"24 ਬਿਟਾਂ/ਨਮੂਨਾ"</item>
+    <item msgid="121583001492929387">"32 ਬਿਟਾਂ/ਨਮੂਨਾ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</item>
-    <item msgid="4726688794884191540">"16 ਬਿੱਟ/ਨਮੂਨਾ"</item>
-    <item msgid="305344756485516870">"24 ਬਿੱਟ/ਨਮੂਨਾ"</item>
-    <item msgid="244568657919675099">"32 ਬਿੱਟ/ਨਮੂਨਾ"</item>
+    <item msgid="1081159789834584363">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
+    <item msgid="4726688794884191540">"16 ਬਿਟਾਂ/ਨਮੂਨਾ"</item>
+    <item msgid="305344756485516870">"24 ਬਿਟਾਂ/ਨਮੂਨਾ"</item>
+    <item msgid="244568657919675099">"32 ਬਿਟਾਂ/ਨਮੂਨਾ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</item>
+    <item msgid="5226878858503393706">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
     <item msgid="4106832974775067314">"ਮੋਨੋ"</item>
     <item msgid="5571632958424639155">"ਸਟੀਰੀਓ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</item>
+    <item msgid="4118561796005528173">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>
     <item msgid="8900559293912978337">"ਮੋਨੋ"</item>
     <item msgid="8883739882299884241">"ਸਟੀਰੀਓ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"ਤਰਜੀਹੀ ਧੁਨੀ ਗੁਣਵੱਤਾ (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"ਮਿਆਰੀ ਗੁਣਵੱਤਾ (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"ਤਰਜੀਹੀ ਕਨੈਕਸ਼ਨ (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"ਔਡੀਓ ਗੁਣਵੱਤਾ ਨੂੰ ਵਧਾਓ (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"ਬੈਲੈਂਸਡ ਔਡੀਓ ਅਤੇ ਕਨੈਕਸ਼ਨ ਗੁਣਵੱਤਾ (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"ਕਨੈਕਸ਼ਨ ਗੁਣਵੱਤਾ ਨੂੰ ਵਧਾਓ (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"ਤਰਜੀਹੀ ਧੁਨੀ ਗੁਣਵੱਤਾ (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"ਮਿਆਰੀ ਗੁਣਵੱਤਾ (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"ਤਰਜੀਹੀ ਕਨੈਕਸ਼ਨ (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"ਔਡੀਓ ਗੁਣਵੱਤਾ ਨੂੰ ਵਧਾਓ"</item>
+    <item msgid="4327143584633311908">"ਬੈਲੈਂਸਡ ਔਡੀਓ ਅਤੇ ਕਨੈਕਸ਼ਨ ਗੁਣਵੱਤਾ"</item>
+    <item msgid="6155648878105378550">"ਕਨੈਕਸ਼ਨ ਗੁਣਵੱਤਾ ਨੂੰ ਵਧਾਓ"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ਬੰਦ"</item>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f824746..f7a95cd 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"ਸੈਲਿਊਲਰ ਡੇਟਾ ਹਮੇਸ਼ਾ ਕਿਰਿਆਸ਼ੀਲ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ਪੂਰਨ ਵੌਲਿਊਮ ਨੂੰ ਅਯੋਗ ਬਣਾਓ"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਕੋਡੇਕ"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"ਤਰਜੀਹੀ ਬਲੂਟੁੱਥ A2DP ਕੋਡੇਕ ਚੁਣੋ"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਕੋਡੇਕ ਚੁਣੋ"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਨਮੂਨਾ ਦਰ"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"ਤਰਜੀਹੀ ਬਲੂਟੁੱਥ A2DP ਕੋਡੇਕ ਨਮੂਨਾ ਦਰ ਚੁਣੋ"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"ਪ੍ਰਤੀ ਨਮੂਨਾ ਬਲੂਟੁੱਥ ਔਡੀਓ ਬਿੱਟ"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"ਪ੍ਰਤੀ ਨਮੂਨਾ ਤਰਜੀਹੀ ਬਲੂਟੁੱਥ A2DP ਕੋਡੇਕ ਬਿੱਟ ਚੁਣੋ"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਕੋਡੇਕ ਚੁਣੋ:\nਨਮੂਨਾ ਗਤੀ"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"ਪ੍ਰਤੀ ਨਮੂਨਾ ਬਲੂਟੁੱਥ ਔਡੀਓ ਬਿਟਾਂ"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਕੋਡੇਕ ਚੁਣੋ:\nਬਿਟਾਂ ਪ੍ਰਤੀ ਨਮੂਨਾ"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਚੈਨਲ ਮੋਡ"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"ਤਰਜੀਹੀ ਬਲੂਟੁੱਥ A2DP ਕੋਡੇਕ ਚੈਨਲ ਮੋਡ ਚੁਣੋ"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"ਬਲੂਟੁੱਥ ਔਡੀਓ LDAC ਪਲੇਬੈਕ ਗੁਣਵੱਤਾ"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"ਤਰਜੀਹੀ ਬਲੂਟੁੱਥ A2DP ਕੋਡੇਕ LDAC ਪਲੇਬੈਕ ਗੁਣਵੱਤਾ ਚੁਣੋ"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"ਬਲੂਟੁੱਥ ਔਡੀਓ ਕੋਡੇਕ ਚੁਣੋ:\nਚੈਨਲ ਮੋਡ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ਬਲੂਟੁੱਥ ਔਡੀਓ LDAC ਕੋਡੇਕ: ਪਲੇਬੈਕ ਗੁਣਵੱਤਾ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"ਬਲੂਟੁੱਥ ਔਡੀਓ LDAC ਕੋਡੇਕ ਚੁਣੋ:\nਪਲੇਬੈਕ ਗੁਣਵੱਤਾ"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ਸਟ੍ਰੀਮਿੰਗ: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ਵਾਇਰਲੈਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚੋਣਾਂ ਦਿਖਾਓ"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ਲੌਗਿੰਗ ਪੱਧਰ ਵਧਾਓ, Wi‑Fi Picker ਵਿੱਚ ਪ੍ਰਤੀ SSID RSSI ਦਿਖਾਓ"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"ਜਦੋਂ ਸਮਰਥਿਤ ਹੋਵੇ, ਤਾਂ Wi‑Fi ਸੈਲਿਊਲਰ ਨੂੰ ਡੈਟਾ ਕਨੈਕਸ਼ਨ ਹੈਂਡ ਓਵਰ ਕਰਨ ਵਿੱਚ ਵੱਧ ਅਗ੍ਰੈਸਿਵ ਹੋ ਜਾਏਗਾ, ਜਦੋਂ Wi‑Fi ਸਿਗਨਲ ਘੱਟ ਹੋਵੇ"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"ਮਦਦ ਅਤੇ ਪ੍ਰਤੀਕਰਮ"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"ਮੀਨੂ"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"ਡੈਮੋ ਮੋਡ \'ਚ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਲਈ ਪਾਸਵਰਡ ਦਿਓ"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"ਅੱਗੇ"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"ਪਾਸਵਰਡ ਦੀ ਲੋੜ ਹੈ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
index 15a7b51..c813cad 100644
--- a/packages/SettingsLib/res/values-pl/arrays.xml
+++ b/packages/SettingsLib/res/values-pl/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Użyj sprawdzania HDCP tylko w przypadku treści chronionych DRM"</item>
     <item msgid="45075631231212732">"Zawsze używaj sprawdzania HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Wartość domyślna"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Wartość domyślna"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Wartość domyślna"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Wartość domyślna"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Wartość domyślna"</item>
-    <item msgid="5618929009984956469">"16 bitów/próbkę"</item>
-    <item msgid="3412640499234627248">"24 bity/próbkę"</item>
-    <item msgid="121583001492929387">"32 bity/próbkę"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Wartość domyślna"</item>
-    <item msgid="4726688794884191540">"16 bitów/próbkę"</item>
-    <item msgid="305344756485516870">"24 bity/próbkę"</item>
-    <item msgid="244568657919675099">"32 bity/próbkę"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Wartość domyślna"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Wartość domyślna"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Preferowana jakość dźwięku (990 kb/s / 909 kb/s)"</item>
-    <item msgid="138837449700903545">"Standardowa (660 kb/s / 606 kb/s)"</item>
-    <item msgid="4777177307869441982">"Preferowane połączenie (330 kb/s / 303 kb/s)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Preferowana jakość dźwięku (990 kb/s / 909 kb/s)"</item>
-    <item msgid="9091111147684472529">"Standardowa (660 kb/s / 606 kb/s)"</item>
-    <item msgid="3367904477834831032">"Preferowane połączenie (330 kb/s / 303 kb/s)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Wył."</item>
     <item msgid="1593289376502312923">"64 KB"</item>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 9c841fa..1c5f3d2 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Dane komórkowe zawsze aktywne"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Wyłącz głośność bezwzględną"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Kodek dźwięku Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Wybierz preferowany kodek Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Dźwięk Bluetooth – współczynnik próbkowania"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Wybierz preferowany współczynnik próbkowania w kodeku Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Dźwięk Bluetooth – liczba bitów na próbkę"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Wybierz preferowaną liczbę bitów na próbkę w kodeku Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Dźwięk Bluetooth – tryb kanału"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Wybierz preferowany tryb kanału w kodeku Bluetooth A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Dźwięk Bluetooth – jakość dźwięku LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Wybierz preferowaną jakość dźwięku LDAC w kodeku Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaż opcje certyfikacji wyświetlacza bezprzewodowego"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zwiększ poziom rejestrowania Wi‑Fi, pokazuj według RSSI SSID w selektorze Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Po włączeniu połączenie danych będzie bardziej agresywnie przełączać się z Wi-Fi na sieć komórkową przy słabym sygnale Wi-Fi"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Pomoc i opinie"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Wpisz hasło, by przywrócić ustawienia fabryczne w trybie demonstracyjnym"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Dalej"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Wymagane hasło"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index 4d1f3bb..e238905 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Sempre usar a verificação HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Padrão"</item>
+    <item msgid="7065842274271279580">"Usar seleção do sistema (padrão)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Padrão"</item>
+    <item msgid="5062108632402595000">"Usar seleção do sistema (padrão)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Padrão"</item>
+    <item msgid="3093023430402746802">"Usar seleção do sistema (padrão)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Padrão"</item>
+    <item msgid="3214516120190965356">"Usar seleção do sistema (padrão)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Padrão"</item>
+    <item msgid="2684127272582591429">"Usar seleção do sistema (padrão)"</item>
     <item msgid="5618929009984956469">"16 bits/amostra"</item>
     <item msgid="3412640499234627248">"24 bits/amostra"</item>
     <item msgid="121583001492929387">"32 bits/amostra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Padrão"</item>
+    <item msgid="1081159789834584363">"Usar seleção do sistema (padrão)"</item>
     <item msgid="4726688794884191540">"16 bits/amostra"</item>
     <item msgid="305344756485516870">"24 bits/amostra"</item>
     <item msgid="244568657919675099">"32 bits/amostra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Padrão"</item>
+    <item msgid="5226878858503393706">"Usar seleção do sistema (padrão)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Padrão"</item>
+    <item msgid="4118561796005528173">"Usar seleção do sistema (padrão)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Qualidade de som preferencial (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Padrão (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Conexão preferencial (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Otimizar para qualidade de áudio (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Qualidade de áudio e de conexão balanceada (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Otimizar para qualidade da conexão (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Qualidade de som preferencial (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Padrão (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Conexão preferencial (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Otimizar para qualidade de áudio"</item>
+    <item msgid="4327143584633311908">"Qualidade de áudio e de conexão balanceada"</item>
+    <item msgid="6155648878105378550">"Otimizar para qualidade de conexão"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Desativado"</item>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 1c34317..dcbac24 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Dados da rede celular sempre ativos"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desativar volume absoluto"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Selecione o codec Bluetooth A2DP preferencial"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Selecionar codec de áudio Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Taxa de amostra do áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Selecione a taxa de amostra do codec Bluetooth A2DP preferencial"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Selecionar codec de áudio Bluetooth:\ntaxa de amostragem"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits por amostra do áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Selecione os bits por amostra do codec Bluetooth A2DP preferencial"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Selecionar codec de áudio Bluetooth:\nbits por amostra"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modo de canal de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Selecione o modo de canal do codec Bluetooth A2DP preferencial"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Qualidade de reprodução LDAC de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Selecione a qualidade de reprodução LDAC do codec Bluetooth A2DP preferencial"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Selecionar codec de áudio Bluetooth:\nmodo de canal"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec de áudio Bluetooth LDAC: qualidade de reprodução"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selecionar codec de áudio Bluetooth LDAC:\nqualidade de reprodução"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções de certificação de Display sem fio"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de registro do Wi-Fi; mostrar conforme o RSSI de SSID na Seleção de Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Quando ativada, o Wi-Fi será mais agressivo em transferir a conexão de dados para celular, quando o sinal de Wi-Fi estiver fraco"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Digite a senha para redef. p/ configuração original em modo demo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Próxima"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Senha necessária"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index d17701e..048c727 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Utilizar sempre a verificação HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Predefinição"</item>
+    <item msgid="7065842274271279580">"Utilizar seleção do sistema (predef.)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Predefinição"</item>
+    <item msgid="5062108632402595000">"Utilizar seleção do sistema (predef.)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Predefinição"</item>
+    <item msgid="3093023430402746802">"Utilizar seleção do sistema (predef.)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Predefinição"</item>
+    <item msgid="3214516120190965356">"Utilizar seleção do sistema (predef.)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Predefinição"</item>
+    <item msgid="2684127272582591429">"Utilizar seleção do sistema (predef.)"</item>
     <item msgid="5618929009984956469">"16 bits/amostra"</item>
     <item msgid="3412640499234627248">"24 bits/amostra"</item>
     <item msgid="121583001492929387">"32 bits/amostra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Predefinição"</item>
+    <item msgid="1081159789834584363">"Utilizar seleção do sistema (predef.)"</item>
     <item msgid="4726688794884191540">"16 bits/amostra"</item>
     <item msgid="305344756485516870">"24 bits/amostra"</item>
     <item msgid="244568657919675099">"32 bits/amostra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Predefinição"</item>
-    <item msgid="4106832974775067314">"Monocromático"</item>
+    <item msgid="5226878858503393706">"Utilizar seleção do sistema (predef.)"</item>
+    <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Predefinição"</item>
-    <item msgid="8900559293912978337">"Monocromático"</item>
+    <item msgid="4118561796005528173">"Utilizar seleção do sistema (predef.)"</item>
+    <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Qual. som preferida (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Padrão (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Ligação preferida (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Otimizar para qualidade de áudio (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Qualidade de áudio e de ligação equilibrada (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Otimizar para qualidade de ligação (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Qual. som preferida (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Padrão (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Ligação preferida (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Otimizar para qualidade de áudio"</item>
+    <item msgid="4327143584633311908">"Qualidade de áudio e de ligação equilibrada"</item>
+    <item msgid="6155648878105378550">"Otimizar para qualidade de ligação"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Desativado"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index a66ead0..3163ce4 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Dados móveis sempre ativados"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desativar volume absoluto"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Selecionar Codec A2DP Bluetooth preferido"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Frequência de amostragem de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Selecionar Frequência de amostragem de codec A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Selecionar codec de áudio Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Taxa de amostragem de áudio Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Selecionar codec de áudio Bluetooth:\nTaxa de amostragem"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits por amostra de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Selecionar Bits por amostra de codec A2DP Bluetooth preferido"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Selecionar codec de áudio Bluetooth:\nBits por amostra"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modo de canal áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Selecionar Modo de canal de codec A2DP Bluetooth preferido"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Qualidade de reprodução LDAC de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Selecionar Qualidade de reprodução LDAC de codec A2DP Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Selecionar codec de áudio de Bluetooth:\nModo de canal"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec LDAC de áudio Bluetooth: qualidade de reprodução"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selecionar codec LDAC de áudio Bluetooth:\nQualidade de reprodução"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Transmissão em fluxo contínuo: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções da certificação de display sem fios"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de reg. de Wi-Fi, mostrar por RSSI de SSID no Selec. de Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Se estiver ativado, o Wi-Fi será mais agressivo ao transmitir a lig. de dados p/ a rede móvel quando o sinal Wi-Fi estiver fraco"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e comentários"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Intr. palavra-passe p/ repos. fábrica no modo demo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Próximo"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Palavra-passe obrigatória"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index 4d1f3bb..e238905 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Sempre usar a verificação HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Padrão"</item>
+    <item msgid="7065842274271279580">"Usar seleção do sistema (padrão)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Padrão"</item>
+    <item msgid="5062108632402595000">"Usar seleção do sistema (padrão)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Padrão"</item>
+    <item msgid="3093023430402746802">"Usar seleção do sistema (padrão)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Padrão"</item>
+    <item msgid="3214516120190965356">"Usar seleção do sistema (padrão)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Padrão"</item>
+    <item msgid="2684127272582591429">"Usar seleção do sistema (padrão)"</item>
     <item msgid="5618929009984956469">"16 bits/amostra"</item>
     <item msgid="3412640499234627248">"24 bits/amostra"</item>
     <item msgid="121583001492929387">"32 bits/amostra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Padrão"</item>
+    <item msgid="1081159789834584363">"Usar seleção do sistema (padrão)"</item>
     <item msgid="4726688794884191540">"16 bits/amostra"</item>
     <item msgid="305344756485516870">"24 bits/amostra"</item>
     <item msgid="244568657919675099">"32 bits/amostra"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Padrão"</item>
+    <item msgid="5226878858503393706">"Usar seleção do sistema (padrão)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Padrão"</item>
+    <item msgid="4118561796005528173">"Usar seleção do sistema (padrão)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Estéreo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Qualidade de som preferencial (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Padrão (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Conexão preferencial (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Otimizar para qualidade de áudio (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Qualidade de áudio e de conexão balanceada (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Otimizar para qualidade da conexão (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Qualidade de som preferencial (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Padrão (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Conexão preferencial (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Otimizar para qualidade de áudio"</item>
+    <item msgid="4327143584633311908">"Qualidade de áudio e de conexão balanceada"</item>
+    <item msgid="6155648878105378550">"Otimizar para qualidade de conexão"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Desativado"</item>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 1c34317..dcbac24 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Dados da rede celular sempre ativos"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desativar volume absoluto"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Selecione o codec Bluetooth A2DP preferencial"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Selecionar codec de áudio Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Taxa de amostra do áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Selecione a taxa de amostra do codec Bluetooth A2DP preferencial"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Selecionar codec de áudio Bluetooth:\ntaxa de amostragem"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits por amostra do áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Selecione os bits por amostra do codec Bluetooth A2DP preferencial"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Selecionar codec de áudio Bluetooth:\nbits por amostra"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modo de canal de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Selecione o modo de canal do codec Bluetooth A2DP preferencial"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Qualidade de reprodução LDAC de áudio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Selecione a qualidade de reprodução LDAC do codec Bluetooth A2DP preferencial"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Selecionar codec de áudio Bluetooth:\nmodo de canal"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec de áudio Bluetooth LDAC: qualidade de reprodução"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selecionar codec de áudio Bluetooth LDAC:\nqualidade de reprodução"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções de certificação de Display sem fio"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de registro do Wi-Fi; mostrar conforme o RSSI de SSID na Seleção de Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Quando ativada, o Wi-Fi será mais agressivo em transferir a conexão de dados para celular, quando o sinal de Wi-Fi estiver fraco"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Digite a senha para redef. p/ configuração original em modo demo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Próxima"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Senha necessária"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index a6a0757..e801213 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Utilizează întotdeauna verificarea HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Prestabilit"</item>
+    <item msgid="7065842274271279580">"Folosiți selectarea sist. (prestabilit)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Prestabilit"</item>
+    <item msgid="5062108632402595000">"Folosiți selectarea sist. (prestabilit)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Prestabilit"</item>
+    <item msgid="3093023430402746802">"Folosiți selectarea sist. (prestabilit)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Prestabilit"</item>
+    <item msgid="3214516120190965356">"Folosiți selectarea sist. (prestabilit)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Prestabilit"</item>
+    <item msgid="2684127272582591429">"Folosiți selectarea sist. (prestabilit)"</item>
     <item msgid="5618929009984956469">"16 biți/eșantion"</item>
     <item msgid="3412640499234627248">"24 biți/eșantion"</item>
     <item msgid="121583001492929387">"32 biți/eșantion"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Prestabilit"</item>
+    <item msgid="1081159789834584363">"Folosiți selectarea sist. (prestabilit)"</item>
     <item msgid="4726688794884191540">"16 biți/eșantion"</item>
     <item msgid="305344756485516870">"24 biți/eșantion"</item>
     <item msgid="244568657919675099">"32 biți/eșantion"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Prestabilit"</item>
+    <item msgid="5226878858503393706">"Folosiți selectarea sist. (prestabilit)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Prestabilit"</item>
+    <item msgid="4118561796005528173">"Folosiți selectarea sist. (prestabilit)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Cal. pref. sunet (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Standard (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Conexiune preferată (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Optimizați pentru calitatea audio (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Calitatea audio și a conexiunii echilibrată (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimizați pentru calitatea conexiunii (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Cal. pref. sunet (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Standard (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Conexiune preferată (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Optimizați pentru calitatea audio"</item>
+    <item msgid="4327143584633311908">"Calitatea audio și a conexiunii echilibrată"</item>
+    <item msgid="6155648878105378550">"Optimizați pentru calitatea conexiunii"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Dezactivată"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 512ba3a..bae40ef 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Conexiunea de date mobile este întotdeauna activată"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Dezactivați volumul absolut"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Selectați codecul Bluetooth A2DP preferat"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Selectați codecul audio Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Rată de eșantionare audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Selectați rata de eșantionare codec Bluetooth A2DP preferată"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Selectați codecul audio Bluetooth:\nrată de eșantionare"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Biți audio Bluetooth per eșantion"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Selectați biții codecului Bluetooth A2DP preferați per eșantion"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Selectați codecul audio Bluetooth:\nbiți per eșantion"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modul canal audio Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Selectați modul canal codec pentru Bluetooth A2DP preferat"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Calitatea redării Bluetooth audio LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Selectați calitatea redării LDAC a codecului pentru Bluetooth A2DP preferată"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Selectați codecul audio Bluetooth:\nmodul canal"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codecul LDAC audio pentru Bluetooth: calitatea redării"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Selectați codecul LDAC audio pentru Bluetooth:\ncalitatea redării"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Transmitere în flux: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afișați opțiunile pentru certificarea Ecran wireless"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Măriți niv. de înr. prin Wi‑Fi, afișați în fcț. de SSID RSSI în Selectorul Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Când este activată, funcția Wi-Fi va fi mai agresivă la predarea conexiunii de date către mobil când semnalul Wi-Fi este slab"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ajutor și feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Meniu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Intr. par. pt. a rev. set. fab. în demo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Înainte"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Trebuie să introduceți o parolă"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 1080b01..6cc82a2 100644
--- a/packages/SettingsLib/res/values-ru/arrays.xml
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Всегда использовать проверку HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"По умолчанию"</item>
+    <item msgid="7065842274271279580">"Выбор системы (по умолчанию)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"По умолчанию"</item>
+    <item msgid="5062108632402595000">"Выбор системы (по умолчанию)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"По умолчанию"</item>
+    <item msgid="3093023430402746802">"Выбор системы (по умолчанию)"</item>
     <item msgid="8895532488906185219">"44,1 кГц"</item>
     <item msgid="2909915718994807056">"48 кГц"</item>
     <item msgid="3347287377354164611">"88,2 кГц"</item>
     <item msgid="1234212100239985373">"96 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"По умолчанию"</item>
+    <item msgid="3214516120190965356">"Выбор системы (по умолчанию)"</item>
     <item msgid="4482862757811638365">"44,1 кГц"</item>
     <item msgid="354495328188724404">"48 кГц"</item>
     <item msgid="7329816882213695083">"88,2 кГц"</item>
     <item msgid="6967397666254430476">"96 кГц"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"По умолчанию"</item>
+    <item msgid="2684127272582591429">"Выбор системы (по умолчанию)"</item>
     <item msgid="5618929009984956469">"16 бит/отсчет"</item>
     <item msgid="3412640499234627248">"24 бит/отсчет"</item>
     <item msgid="121583001492929387">"32 бит/отсчет"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"По умолчанию"</item>
+    <item msgid="1081159789834584363">"Выбор системы (по умолчанию)"</item>
     <item msgid="4726688794884191540">"16 бит/отсчет"</item>
     <item msgid="305344756485516870">"24 бит/отсчет"</item>
     <item msgid="244568657919675099">"32 бит/отсчет"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"По умолчанию"</item>
+    <item msgid="5226878858503393706">"Выбор системы (по умолчанию)"</item>
     <item msgid="4106832974775067314">"Моно"</item>
     <item msgid="5571632958424639155">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"По умолчанию"</item>
+    <item msgid="4118561796005528173">"Выбор системы (по умолчанию)"</item>
     <item msgid="8900559293912978337">"Моно"</item>
     <item msgid="8883739882299884241">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Предпочтит. качество звука (990 кбит/с/909 кбит/с)"</item>
-    <item msgid="138837449700903545">"Стандартное (660 кбит/с/606 кбит/с)"</item>
-    <item msgid="4777177307869441982">"Предпочтит. соединение (330 кбит/с/303 кбит/с)"</item>
+    <item msgid="3411577996960199959">"Оптимизация под качество аудио (990/909 кбит/с)"</item>
+    <item msgid="2921767058740704969">"Баланс качества аудио и скорости подключения (660/606 кбит/с)"</item>
+    <item msgid="3682554248829489641">"Оптимизация под скорость подключения (330/303 кбит/с)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Предпочтит. качество звука (990 кбит/с/909 кбит/с)"</item>
-    <item msgid="9091111147684472529">"Стандартное (660 кбит/с/606 кбит/с)"</item>
-    <item msgid="3367904477834831032">"Предпочтит. соединение (330 кбит/с/303 кбит/с)"</item>
+    <item msgid="7668834469173465015">"Оптимизация под качество аудио"</item>
+    <item msgid="4327143584633311908">"Баланс качества аудио и скорости подключения"</item>
+    <item msgid="6155648878105378550">"Оптимизация под скорость подключения"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Выкл."</item>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index bad2158..df69f91 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Не отключать передачу данных"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Отключить абсолютный уровень громкости"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Аудиокодек для передачи через Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Выберите кодек A2DP для передачи через Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Аудиокодек для передачи через Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Частота дискретизации при передаче через Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Выберите частоту дискретизации для кодека A2DP (через Bluetooth)"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Аудиокодек для передачи через Bluetooth:\nчастота дискретизации"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Глубина кодирования звука при передаче через Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Выберите глубину кодирования звука для кодека A2DP (через Bluetooth)"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Аудиокодек для передачи через Bluetooth:\nбитов на образец"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Режим аудиоканала Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Выберите режим аудиоканала для кодека A2DP (через Bluetooth)"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Качество воспроизведения с кодеком A2DP (через Bluetooth)"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Выберите качество воспроизведения с кодеком A2DP (через Bluetooth)"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Аудиокодек для передачи через Bluetooth:\nрежим канала"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Качество воспроизведения с аудиокодеком LDAC (через Bluetooth)"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Аудиокодек LDAC:\nкачество воспроизведения"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Потоковая передача: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показывать параметры сертификации беспроводных мониторов"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"При выборе Wi‑Fi указывать в журнале RSSI для каждого SSID"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Принудительно переключаться на мобильную сеть, если сигнал Wi-Fi слабый"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Справка/отзыв"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Чтобы сбросить настройки в деморежиме, введите пароль."</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Далее"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Требуется пароль"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml
index afc125f..c9a5d12 100644
--- a/packages/SettingsLib/res/values-si/arrays.xml
+++ b/packages/SettingsLib/res/values-si/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"DRM අන්තර්ගත සඳහා පමණක් HDCP පරික්ෂාව භාවිතා කරන්න"</item>
     <item msgid="45075631231212732">"සැමවිටම HDCP පිරික්සුම භාවිතා කරන්න"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"පෙරනිමි"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"පෙරනිමි"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"පෙරනිමි"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"පෙරනිමි"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"පෙරනිමි"</item>
-    <item msgid="5618929009984956469">"බිටු 16/නියැදිය"</item>
-    <item msgid="3412640499234627248">"බිටු 24/නියැදිය"</item>
-    <item msgid="121583001492929387">"බිටු 32/නියැදිය"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"පෙරනිමි"</item>
-    <item msgid="4726688794884191540">"බිටු 16/නියැදිය"</item>
-    <item msgid="305344756485516870">"බිටු 24/නියැදිය"</item>
-    <item msgid="244568657919675099">"බිටු 32/නියැදිය"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"පෙරනිමි"</item>
-    <item msgid="4106832974775067314">"ඒකල"</item>
-    <item msgid="5571632958424639155">"ස්ටීරියෝ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"පෙරනිමි"</item>
-    <item msgid="8900559293912978337">"ඒකල"</item>
-    <item msgid="8883739882299884241">"ස්ටීරියෝ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"වඩා කැමති හඬ ගුණත්වය (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"සම්මත (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"වඩා කැමති සබැඳුම (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"වඩා කැමති හඬ ගුණත්වය (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"සම්මත (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"වඩා කැමති සබැඳුම (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ක්‍රියාවිරහිතය"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index fb6545b..bfe1e9b 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"සෙලියුලර් දත්ත සැමවිට ක්‍රියාකාරීය"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"නිරපේක්ෂ හඩ පරිමාව අබල කරන්න"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"බ්ලූටූත් ශ්‍රව්‍ය Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"වඩා කැමති බ්ලූටූත් A2DP Codec තෝරන්න"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"බ්ලූටූත් ශ්‍රව්‍ය නියැදි අනුපාතය"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"වඩා කැමති බ්ලූටූත් A2DP Codec නියැදි අනුපාතය තෝරන්න"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"නියැදියකට බ්ලූටූත් ශ්‍රව්‍ය බිටු"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"නියැදියකට වඩා කැමති බ්ලූටූත් A2DP Codec බිටු තෝරන්න"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"බ්ලූටූත් ශ්‍රව්‍ය නාලිකා ප්‍රකාරය"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"වඩා කැමති බ්ලූටූත් A2DP Codec නාලිකා ප්‍රකාරය තෝරන්න"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"බ්ලූටූත් ශ්‍රව්‍ය LDAC පසුධාවන ගුණත්වය"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"වඩා කැමති බ්ලූටූත් A2DP Codec පසුධාවන ගුණත්වය තෝරන්න"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"නොරැහැන් සංදර්ශක සහතිකය සඳහා විකල්ප පෙන්වන්න"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ලොග් මට්ටම වැඩි කරන්න, Wi‑Fi තෝරනයෙහි SSID RSSI අනුව පෙන්වන්න"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"සබල විට Wi‑Fi සිග්නලය අඩු විට Wi‑Fi දත්ත සම්බන්ධතාවය සෙලියුලර් වෙත භාර දීමට වඩා ආක්‍රමණික වේ"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"උදව් සහ ප්‍රතිපෝෂණ"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"මෙනුව"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"ආදර්ශන ප්‍රකාර කර්මාන්තශාලා යළි සැකසීමට මුරපදය ඇ. ක."</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"ඊළඟ"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"මුරපදය අවශ්‍යයි"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index 66fabb2..178388e 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Vždy používať kontrolu HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Predvolené"</item>
+    <item msgid="7065842274271279580">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Predvolené"</item>
+    <item msgid="5062108632402595000">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Predvolené"</item>
+    <item msgid="3093023430402746802">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Predvolené"</item>
+    <item msgid="3214516120190965356">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Predvolené"</item>
+    <item msgid="2684127272582591429">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="5618929009984956469">"16 bitov na vzorku"</item>
     <item msgid="3412640499234627248">"24 bitov na vzorku"</item>
     <item msgid="121583001492929387">"32 bitov na vzorku"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Predvolené"</item>
+    <item msgid="1081159789834584363">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="4726688794884191540">"16 bitov na vzorku"</item>
     <item msgid="305344756485516870">"24 bitov na vzorku"</item>
     <item msgid="244568657919675099">"32 bitov na vzorku"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Predvolené"</item>
+    <item msgid="5226878858503393706">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Predvolené"</item>
+    <item msgid="4118561796005528173">"Použiť voľbu systému (predvolené)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Preferovanie kvality zvuku (990/909 kb/s)"</item>
-    <item msgid="138837449700903545">"Štandardné (660/606 kb/s)"</item>
-    <item msgid="4777177307869441982">"Preferovanie pripojenia (330/303 kb/s)"</item>
+    <item msgid="3411577996960199959">"Optimalizovať na kvalitu zvuku (990/909 kb/s)"</item>
+    <item msgid="2921767058740704969">"Vyrovnaná kvalita zvuku a pripojenia (660/606 kb/s)"</item>
+    <item msgid="3682554248829489641">"Optimalizovať na kvalitu pripojenia (330/303 kb/s)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Preferovanie kvality zvuku (990/909 kb/s)"</item>
-    <item msgid="9091111147684472529">"Štandardné (660/606 kb/s)"</item>
-    <item msgid="3367904477834831032">"Preferovanie pripojenia (330/303 kb/s)"</item>
+    <item msgid="7668834469173465015">"Optimalizovať na kvalitu zvuku"</item>
+    <item msgid="4327143584633311908">"Vyrovnaná kvalita zvuku a pripojenia"</item>
+    <item msgid="6155648878105378550">"Optimalizovať na kvalitu pripojenia"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Vypnuté"</item>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 65de397..ce1315a 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobilné dáta vždy aktívne"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Zakázať absolútnu hlasitosť"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio – kodek"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Vyberte preferovaný kodek Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Vybrať kodek Bluetooth Audio"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth Audio – vzorkovacia frekvencia"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Zvoľte preferovanú vzorkovaciu frekvenciu kodeku Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Vybrať kodek Bluetooth Audio:\nVzorkovacia frekvencia"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth Audio – počet bitov na vzorku"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Zvoľte preferovaný počet bitov na vzorky kodeku Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Vybrať kodek Bluetooth Audio:\nPočet bitov na vzorku"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth Audio – režim kanála"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Zvoľte preferovaný režim kanála kodeku Bluetooth A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Kvalita prehrávania LDAC Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Zvoľte preferovanú kvalitu prehrávania LDAC kodeku Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Vybrať kodek Bluetooth Audio:\nRežim kanála"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Kodek LDAC Bluetooth Audio: Kvalita prehrávania"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Vybrať kodek LDAC Bluetooth Audio:\nKvalita prehrávania"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streamovanie: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Zobraziť možnosti certifikácie bezdrôtového zobrazenia"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zvýšiť úroveň denníkov Wi-Fi, zobrazovať podľa SSID RSSI pri výbere siete Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Keď túto možnosť zapnete, Wi-Fi bude agresívnejšie odovzdávať dát. pripoj. na mob. sieť vtedy, keď bude slabý signál Wi-Fi"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Pomocník a spätná väzba"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Ponuka"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Zadajte heslo na obnovenie továrenských nastavení v režime ukážky"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Ďalej"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Vyžaduje sa heslo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index 6b6e0c8..d717308 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Preverjanje HDCP uporabi samo za vsebino DRM"</item>
     <item msgid="45075631231212732">"Vedno uporabi preverjanje HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Privzeto"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Privzeto"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Privzeto"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Privzeto"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Privzeto"</item>
-    <item msgid="5618929009984956469">"16 bitov/vzorec"</item>
-    <item msgid="3412640499234627248">"24 bitov/vzorec"</item>
-    <item msgid="121583001492929387">"32 bitov/vzorec"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Privzeto"</item>
-    <item msgid="4726688794884191540">"16 bitov/vzorec"</item>
-    <item msgid="305344756485516870">"24 bitov/vzorec"</item>
-    <item msgid="244568657919675099">"32 bitov/vzorec"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Privzeto"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Privzeto"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Prednostna kakovost zvoka (990/909 kb/s)"</item>
-    <item msgid="138837449700903545">"Standardno (660/606 kb/s)"</item>
-    <item msgid="4777177307869441982">"Prednostna povezava (330/303 kb/s)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Prednostna kakovost zvoka (990/909 kb/s)"</item>
-    <item msgid="9091111147684472529">"Standardno (660/606 kb/s)"</item>
-    <item msgid="3367904477834831032">"Prednostna povezava (330/303 kb/s)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Izklopljeno"</item>
     <item msgid="1593289376502312923">"64 K"</item>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index ef21240..eb80a30 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Prenos podatkov v mobilnih omrežjih je vedno aktiven"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Onemogočanje absolutnega praga glasnosti"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Zvočni kodek za Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Izberite prednostni kodek A2DP za Bluetooth"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Hitrost vzorčenja zvoka prek Bluetootha"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Izberite prednostno hitrost vzorčenja za kodek A2DP za Bluetooth"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Število bitov na vzorec za zvok prek Bluetootha"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Izberite prednostno število bitov na vzorec za kodek A2DP za Bluetooth"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Način zvočnega kanala prek Bluetootha"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Izberite prednostni način kanala za kodek A2DP za Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Kakovost predvajanja LDAC za zvok prek Bluetootha"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Izberite prednostno kakovost predvajanja LDAC za kodek A2DP za Bluetooth"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži možnosti za potrdilo brezžičnega zaslona"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povečaj raven zapis. dnev. za Wi-Fi; v izbir. Wi‑Fi-ja pokaži glede na SSID RSSI"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Če je ta možnost omogočena, Wi-Fi odločneje preda podatkovno povezavo mobilnemu omrežju, ko je signal Wi-Fi šibek"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Pomoč in povratne informacije"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Geslo za tovar. nast. v predstav. načinu"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Naprej"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Vnesite geslo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml
index e52b9fa..29f27a4 100644
--- a/packages/SettingsLib/res/values-sq/arrays.xml
+++ b/packages/SettingsLib/res/values-sq/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Përdor gjithmonë kontrollin e HDCP-së"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"I parazgjedhur"</item>
+    <item msgid="7065842274271279580">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"I parazgjedhur"</item>
+    <item msgid="5062108632402595000">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"I parazgjedhur"</item>
+    <item msgid="3093023430402746802">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"I parazgjedhur"</item>
+    <item msgid="3214516120190965356">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"I parazgjedhur"</item>
+    <item msgid="2684127272582591429">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="5618929009984956469">"16 bite/shembull"</item>
     <item msgid="3412640499234627248">"24 bite/shembull"</item>
     <item msgid="121583001492929387">"32 bite/shembull"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"I parazgjedhur"</item>
+    <item msgid="1081159789834584363">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="4726688794884191540">"16 bite/shembull"</item>
     <item msgid="305344756485516870">"24 bite/shembull"</item>
     <item msgid="244568657919675099">"32 bite/shembull"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"I parazgjedhur"</item>
+    <item msgid="5226878858503393706">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"I parazgjedhur"</item>
+    <item msgid="4118561796005528173">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Cilësia e preferuar e zërit (990 kbps/909 kbps)"</item>
-    <item msgid="138837449700903545">"Standarde (660 kbps/606 kbps)"</item>
-    <item msgid="4777177307869441982">"Lidhja e preferuar (330 kbps/303 kbps)"</item>
+    <item msgid="3411577996960199959">"Optimizoje për cilësi audioje (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"Cilësi e balancuar e audios dhe e lidhjes (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"Optimizoje për cilësi lidhjeje (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Cilësia e preferuar e zërit (990 kbps/909 kbps)"</item>
-    <item msgid="9091111147684472529">"Standarde (660 kbps/606 kbps)"</item>
-    <item msgid="3367904477834831032">"Lidhja e preferuar (330 kbps/303 kbps)"</item>
+    <item msgid="7668834469173465015">"Optimizoje për cilësi audioje"</item>
+    <item msgid="4327143584633311908">"Cilësi e balancuar e audios dhe e lidhjes"</item>
+    <item msgid="6155648878105378550">"Optimizoje për cilësi lidhjeje"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Joaktiv"</item>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 56c36c0..7163852 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Të dhënat celulare gjithmonë aktive"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Çaktivizo volumin absolut"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Kodeku Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Zgjidh kodekun e preferuar Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Zgjidh kodekun e audios së Bluetooth-it"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Shpejtësia e shembullit të Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Zgjidh shpejtësinë e preferuar të shembullit të kodekut Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Zgjidh kodekun e audios së Bluetooth-it:\nShpejtësia e shembullit"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bite për shembull Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Zgjidh bite për shembull të preferuar të kodekut Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Zgjidh kodekun e audios së Bluetooth-it:\nBite për shembull"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Regjimi i kanalit Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Zgjidh regjimi e preferuar të kanalit të kodekut Bluetooth A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Cilësia e luajtjes së Bluetooth Audio LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Zgjidh cilësinë e preferuar të luajtjes të kodekut Bluetooth A2DP"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Zgjidh kodekun e audios së Bluetooth-it:\nModaliteti i kanalit"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Kodeku LDAC i audios së Bluetooth-it: Cilësia e luajtjes"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Zgjidh kodekun LDAC të audios së Bluetooth-it:\nCilësia e luajtjes"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Transmetimi: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Shfaq opsionet për certifikimin e ekranit valor"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Rrit nivelin regjistrues të Wi‑Fi duke shfaqur SSID RSSI-në te Zgjedhësi i Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Kur ky funksion aktivizohet, Wi‑Fi bëhet më agresiv në kalimin e lidhjes së të dhënave te rrjeti celular, në rastet kur sinjali Wi‑Fi është i dobët"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Ndihma dhe komentet"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menyja"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Fut fjalëkalimin për të kryer rivendosje në gjendje fabrike në modalitetin e demonstrimit"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Përpara"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Kërkohet fjalëkalimi"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml
index 8448c10..6956bb8 100644
--- a/packages/SettingsLib/res/values-sr/arrays.xml
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Увек користи HDCP проверу"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Подразумевано"</item>
+    <item msgid="7065842274271279580">"Користи избор система (подразумевано)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Подразумевано"</item>
+    <item msgid="5062108632402595000">"Користи избор система (подразумевано)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Подразумевано"</item>
+    <item msgid="3093023430402746802">"Користи избор система (подразумевано)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Подразумевано"</item>
+    <item msgid="3214516120190965356">"Користи избор система (подразумевано)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Подразумевано"</item>
+    <item msgid="2684127272582591429">"Користи избор система (подразумевано)"</item>
     <item msgid="5618929009984956469">"16 битова по узорку"</item>
     <item msgid="3412640499234627248">"24 бита по узорку"</item>
     <item msgid="121583001492929387">"32 бита по узорку"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Подразумевано"</item>
+    <item msgid="1081159789834584363">"Користи избор система (подразумевано)"</item>
     <item msgid="4726688794884191540">"16 битова по узорку"</item>
     <item msgid="305344756485516870">"24 бита по узорку"</item>
     <item msgid="244568657919675099">"32 бита по узорку"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Подразумевано"</item>
+    <item msgid="5226878858503393706">"Користи избор система (подразумевано)"</item>
     <item msgid="4106832974775067314">"Моно"</item>
     <item msgid="5571632958424639155">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Подразумевано"</item>
+    <item msgid="4118561796005528173">"Користи избор система (подразумевано)"</item>
     <item msgid="8900559293912978337">"Моно"</item>
     <item msgid="8883739882299884241">"Стерео"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Жељени квалитет звука (990 kb/s/909 kb/s)"</item>
-    <item msgid="138837449700903545">"Стандардно (660 kb/s/606 kb/s)"</item>
-    <item msgid="4777177307869441982">"Жељена веза (330 kb/s/303 kb/s)"</item>
+    <item msgid="3411577996960199959">"Оптимизуј за квалитет звука (990 kb/s/909 kb/s)"</item>
+    <item msgid="2921767058740704969">"Уједначен квалитет звука и везе (660 kb/s/606 kb/s)"</item>
+    <item msgid="3682554248829489641">"Оптимизуј за квалитет везе (330 kb/s/303 kb/s)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Жељени квалитет звука (990 kb/s/909 kb/s)"</item>
-    <item msgid="9091111147684472529">"Стандардно (660 kb/s/606 kb/s)"</item>
-    <item msgid="3367904477834831032">"Жељена веза (330 kb/s/303 kb/s)"</item>
+    <item msgid="7668834469173465015">"Оптимизује се за квалитет звука"</item>
+    <item msgid="4327143584633311908">"Уједначен квалитет звука и везе"</item>
+    <item msgid="6155648878105378550">"Оптимизује се за квалитет везе"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Искључено"</item>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index fd33eab..7ea3bf3 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Подаци за мобилне уређаје су увек активни"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Онемогући главно подешавање јачине звука"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth аудио кодек"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Изаберите жељени Bluetooth A2DP кодек"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Изаберите Bluetooth аудио кодек"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Брзина узорковања за Bluetooth аудио"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Изаберите жељену брзину узорковања за Bluetooth A2DP кодек"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Изаберите Bluetooth аудио кодек:\nбрзина узорковања"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Битова по узорку за Bluetooth аудио"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Изаберите жељени број битова по узорку за Bluetooth A2DP кодек"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Изаберите Bluetooth аудио кодек:\nбитова по узорку"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Режим канала за Bluetooth аудио"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Изаберите жељени режим канала за Bluetooth A2DP кодек"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Квалитет LDAC снимка за Bluetooth аудио"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Изаберите жељени квалитет LDAC снимка за Bluetooth A2DP кодек"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Изаберите Bluetooth аудио кодек:\nрежим канала"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth аудио кодек LDAC: квалитет репродукције"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Изаберите Bluetooth аудио кодек LDAC:\nквалитет репродукције"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Стримовање: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Приказ опција за сертификацију бежичног екрана"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Повећава ниво евидентирања за Wi‑Fi. Приказ по SSID RSSI-у у бирачу Wi‑Fi мреже"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Када се омогући, Wi‑Fi ће бити агресивнији при пребацивању мреже за пренос података на Мобилну, када је Wi‑Fi сигнал слаб"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Помоћ и повратне информације"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Мени"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Унесите лозинку да бисте обавили ресетовање на фабричка подешавања у режиму демонстрације"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Даље"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Потребна је лозинка"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
index e3e0103..fe395ac 100644
--- a/packages/SettingsLib/res/values-sv/arrays.xml
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Använd bara HDCP-kontroll för DRM-innehåll"</item>
     <item msgid="45075631231212732">"Använd alltid HDCP-kontroll"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Standard"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Standard"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Standard"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Standard"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Standard"</item>
-    <item msgid="5618929009984956469">"16 bitar/sampling"</item>
-    <item msgid="3412640499234627248">"24 bitar/sampling"</item>
-    <item msgid="121583001492929387">"32 bitar/sampling"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Standard"</item>
-    <item msgid="4726688794884191540">"16 bitar/sampling"</item>
-    <item msgid="305344756485516870">"24 bitar/sampling"</item>
-    <item msgid="244568657919675099">"32 bitar/sampling"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Standard"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Standard"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Föredragen ljudkvalitet (990 kbit/s eller 909 kbit/s)"</item>
-    <item msgid="138837449700903545">"Standard (660 kbit/s eller 606 kbit/s)"</item>
-    <item msgid="4777177307869441982">"Föredragen anslutning (330 kbit/s eller 303 kbit/s)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Föredragen ljudkvalitet (990 kbit/s eller 909 kbit/s)"</item>
-    <item msgid="9091111147684472529">"Standard (660 kbit/s eller 606 kbit/s)"</item>
-    <item msgid="3367904477834831032">"Föredragen anslutning (330 kbit/s eller 303 kbit/s)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Av"</item>
     <item msgid="1593289376502312923">"64 kB"</item>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 3b45fde..9e4ad29 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobildata alltid aktiverad"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Inaktivera Absolute volume"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Ljudkodek för Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Välj föredragen A2DP-kodek för Bluetooth"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Samplingsfrekvens för Bluetooth-ljud"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Välj föredragen samplingsfrekvens för A2DP-kodek för Bluetooth"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Antar bitar per sampling för Bluetooth-ljud"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Välj föredraget antal bitar per sampling för A2DP-kodek för Bluetooth"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Kanalläge för Bluetooth-ljud"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Välj föredraget kanalläge för A2DP-kodek för Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Uppspelningskvalitet för Bluetooth-ljud via LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Välj föredragen uppspelningskvalitet för A2DP-kodek för Bluetooth via LDAC"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Visa certifieringsalternativ för Wi-Fi-skärmdelning"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Öka loggningsnivån för Wi-Fi, visa per SSID RSSI i Wi‑Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"När funktionen har aktiverats kommer dataanslutningen lämnas över från Wi-Fi till mobilen på ett aggressivare sätt när Wi-Fi-signalen är svag"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Hjälp och feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Meny"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Ange lösenord och utför fabriksåterställning i demoläge"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Nästa"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Lösenord krävs"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index b4aeb1f..66d65c9 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Kila wakati tumia ukakuaji wa HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Chaguo-msingi"</item>
+    <item msgid="7065842274271279580">"Tumia Uteuzi wa Mfumo (Chaguo-msingi)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Chaguo-msingi"</item>
+    <item msgid="5062108632402595000">"Tumia Uteuzi wa Mfumo (Chaguo-msingi)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Chaguo-msingi"</item>
+    <item msgid="3093023430402746802">"Tumia Uteuzi wa Mfumo (Chaguo-msingi)"</item>
     <item msgid="8895532488906185219">"kHz 44.1"</item>
     <item msgid="2909915718994807056">"kHz 48.0"</item>
     <item msgid="3347287377354164611">"kHz 88.2"</item>
     <item msgid="1234212100239985373">"kHz 96.0"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Chaguo-msingi"</item>
+    <item msgid="3214516120190965356">"Tumia Uteuzi wa Mfumo (Chaguo-msingi)"</item>
     <item msgid="4482862757811638365">"kHz 44.1"</item>
     <item msgid="354495328188724404">"kHz 48.0"</item>
     <item msgid="7329816882213695083">"kHz 88.2"</item>
     <item msgid="6967397666254430476">"kHz 96.0"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Chaguo-msingi"</item>
+    <item msgid="2684127272582591429">"Tumia Uteuzi wa Mfumo (Chaguo-msingi)"</item>
     <item msgid="5618929009984956469">"Biti 16 kwa kila sampuli"</item>
     <item msgid="3412640499234627248">"Biti 24 kwa kila sampuli"</item>
     <item msgid="121583001492929387">"Biti 32 kwa kila sampuli"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Chaguo-msingi"</item>
+    <item msgid="1081159789834584363">"Tumia Uteuzi wa Mfumo (Chaguo-msingi)"</item>
     <item msgid="4726688794884191540">"Biti 16 kwa kila sampuli"</item>
     <item msgid="305344756485516870">"Biti 24 kwa kila sampuli"</item>
     <item msgid="244568657919675099">"Biti 32 kwa kila sampuli"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Chaguo-msingi"</item>
+    <item msgid="5226878858503393706">"Tumia Uteuzi wa Mfumo (Chaguo-msingi)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Chaguo-msingi"</item>
+    <item msgid="4118561796005528173">"Tumia Uteuzi wa Mfumo (Chaguo-msingi)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Ubora wa sauti unaopendelewa (kbps990/kbps909)"</item>
-    <item msgid="138837449700903545">"Ubora wa sauti wa kawaida (kbps660/kbps606)"</item>
-    <item msgid="4777177307869441982">"Muunganisho unaopendelewa (kbps330/kbps303)"</item>
+    <item msgid="3411577996960199959">"Imarisha kwa ajili ya Ubora wa Sauti (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"Ubora wa Muunganisho na Sauti Umesawazishwa (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"Imarisha kwa ajili ya Ubora wa Muunganisho (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Ubora wa sauti unaopendelewa (kbps990/kbps909)"</item>
-    <item msgid="9091111147684472529">"Ubora wa sauti wa kawaida (kbps660/kbps606)"</item>
-    <item msgid="3367904477834831032">"Muunganisho unaopendelewa (kbps330/kbps303)"</item>
+    <item msgid="7668834469173465015">"Imarisha kwa ajili ya Ubora wa Sauti"</item>
+    <item msgid="4327143584633311908">"Ubora wa Muunganisho na Sauti Umesawazishwa"</item>
+    <item msgid="6155648878105378550">"Imarisha kwa ajili ya Ubora wa Muunganisho"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Imezimwa"</item>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 563283b..9296e81 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Data ya kifaa cha mkononi inatumika kila wakati"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Zima sauti kamili"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Kodeki ya Sauti ya Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Chagua Kodeki Unayopendelea ya A2DP ya Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Chagua Kodeki ya Sauti ya Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Kiwango cha Sampuli ya Sauti ya Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Chagua Kiwango Unachopendelea cha Sampuli ya Kodeki ya A2DP ya Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Chagua Kodeki ya Sauti ya Bluetooth:\nKiwango cha Sampuli"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Biti za Sauti ya Bluetooth kwa Kila Sampuli"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Chagua Biti za Kodeki ya A2DP ya Bluetooth Unazopendelea kwa Kila Sampuli"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Hali ya Kituo cha Sauti ya Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Chagua Hali ya Kituo cha Kodeki ya A2DP ya Bluetooth Unayopendelea"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Ubora wa Kucheza LDAC ya Sauti ya Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Chagua Ubora Unaopendelea wa Kucheza LDAC ya Kodeki ya A2DP ya Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Chagua Kodeki ya Sauti ya Bluetooth:\nBiti kwa Kila Sampuli"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Hali ya Mkondo wa Sauti ya Bluetooth"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Chagua Kodeki ya Sauti ya Bluetooth:\nHali ya Kituo"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Kodeki ya LDAC ya Sauti ya Bluetooth: Ubora wa Kucheza"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Chagua Kodeki ya LDAC ya Sauti ya Bluetooth:\nUbora wa Kucheza"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Kutiririsha: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Onyesha chaguo za cheti cha kuonyesha pasiwaya"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Ongeza hatua ya uwekaji kumbukumbu ya Wi-Fi, onyesha kwa kila SSID RSSI kwenye Kichukuzi cha Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Ikiwashwa, Wifi itakabidhi kwa hima muunganisho wa data kwa mtandao wa Simu za Mkononi, mawimbi ya Wifi yanapokuwa hafifu"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Usaidizi na maoni"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Weka nenosiri ili urejeshe mipangilio ya kiwandani ikiwa katika hali ya onyesho."</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Inayofuata"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Nenosiri linahitajika"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index 9b3b1d4..efbe16d 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"DRM உள்ளடக்கத்திற்கு மட்டும் HDCP சோதனையைப் பயன்படுத்து"</item>
     <item msgid="45075631231212732">"HDCP சரிபார்ப்பை எப்போதும் பயன்படுத்து"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"இயல்பு"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"இயல்பு"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"இயல்பு"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"இயல்பு"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"இயல்பு"</item>
-    <item msgid="5618929009984956469">"16 பிட்கள்/சாம்பிள்"</item>
-    <item msgid="3412640499234627248">"24 பிட்கள்/சாம்பிள்"</item>
-    <item msgid="121583001492929387">"32 பிட்கள்/சாம்பிள்"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"இயல்பு"</item>
-    <item msgid="4726688794884191540">"16 பிட்கள்/சாம்பிள்"</item>
-    <item msgid="305344756485516870">"24 பிட்கள்/சாம்பிள்"</item>
-    <item msgid="244568657919675099">"32 பிட்கள்/சாம்பிள்"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"இயல்பு"</item>
-    <item msgid="4106832974775067314">"மோனோ"</item>
-    <item msgid="5571632958424639155">"ஸ்டீரியோ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"இயல்பு"</item>
-    <item msgid="8900559293912978337">"மோனோ"</item>
-    <item msgid="8883739882299884241">"ஸ்டீரியோ"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"ஒலித் தரம் (பரிந்துரை) (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"நிலையானது (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"இணைப்பு (பரிந்துரை) (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"ஒலித் தரம் (பரிந்துரை) (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"நிலையானது (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"இணைப்பு (பரிந்துரை) (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"முடக்கு"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index c628d5e..790eb67 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"செல்லுலார் தரவு எப்போதும் இயக்கத்தில்"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"அப்சல்யூட் ஒலியளவு அம்சத்தை முடக்கு"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"புளூடூத் ஆடியோ கோடெக்"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"பரிந்துரைக்கப்படும் புளூடூத் A2DP கோடெக்கைத் தேர்ந்தெடுக்கவும்"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"புளூடூத் ஆடியோ சாம்பிள் ரேட்"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"பரிந்துரைக்கப்படும் புளூடூத் A2DP கோடெக் சாம்பிள் ரேட்டைத் தேர்ந்தெடுக்கவும்"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"புளூடூத் ஆடியோ பிட்கள்/சாம்பிள்"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"ஒரு சாம்பிளுக்குப் பரிந்துரைக்கப்படும் புளூடூத் A2DP கோடெக் பிட்களைத் தேர்ந்தெடுக்கவும்"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"புளூடூத் ஆடியோ சேனல் பயன்முறை"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"பரிந்துரைக்கப்படும் புளூடூத் A2DP கோடெக் சேனல் பயன்முறையைத் தேர்ந்தெடுக்கவும்"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"புளூடூத் ஆடியோ LDAC வீடியோவின் தரம்"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"பரிந்துரைக்கப்படும் புளூடூத் A2DP கோடெக் LDAC வீடியோவின் தரத்தைத் தேர்ந்தெடுக்கவும்"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"வயர்லெஸ் காட்சி சான்றுக்கான விருப்பங்களைக் காட்டு"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wifi நுழைவு அளவை அதிகரித்து, வைஃபை தேர்வியில் ஒவ்வொன்றிற்கும் SSID RSSI ஐ காட்டுக"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"இயக்கப்பட்டதும், வைஃபை சிக்னல் குறையும் போது, வைஃபை முழுமையாக ஒத்துழைக்காமல் இருப்பதால் செல்லுலாரின் தரவு இணைப்புக்கு மாறும்"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"உதவி &amp; கருத்து"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"மெனு"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"டெமோ பயன்முறையில் ஆரம்பநிலை மீட்டமைவைச் செயல்படுத்த, கடவுச்சொல்லை உள்ளிடவும்"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"அடுத்து"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"கடவுச்சொல் தேவை"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index beb705a..44a16b1 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"ఎప్పటికీ HDCP తనిఖీని ఉపయోగించు"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"డిఫాల్ట్"</item>
+    <item msgid="7065842274271279580">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"డిఫాల్ట్"</item>
+    <item msgid="5062108632402595000">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"డిఫాల్ట్"</item>
+    <item msgid="3093023430402746802">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"డిఫాల్ట్"</item>
+    <item msgid="3214516120190965356">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"డిఫాల్ట్"</item>
+    <item msgid="2684127272582591429">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item>
     <item msgid="5618929009984956469">"16 బిట్‌లు/నమూనా"</item>
     <item msgid="3412640499234627248">"24 బిట్‌లు/నమూనా"</item>
     <item msgid="121583001492929387">"32 బిట్‌లు/నమూనా"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"డిఫాల్ట్"</item>
+    <item msgid="1081159789834584363">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item>
     <item msgid="4726688794884191540">"16 బిట్‌లు/నమూనా"</item>
     <item msgid="305344756485516870">"24 బిట్‌లు/నమూనా"</item>
     <item msgid="244568657919675099">"32 బిట్‌లు/నమూనా"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"డిఫాల్ట్"</item>
+    <item msgid="5226878858503393706">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item>
     <item msgid="4106832974775067314">"మోనో"</item>
     <item msgid="5571632958424639155">"స్టీరియో"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"డిఫాల్ట్"</item>
+    <item msgid="4118561796005528173">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item>
     <item msgid="8900559293912978337">"మోనో"</item>
     <item msgid="8883739882299884241">"స్టీరియో"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"ప్రాధాన్య శబ్ద నాణ్యత (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"ప్రామాణికం (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"ప్రాధాన్య కనెక్షన్ (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"ఆడియో నాణ్యత కోసం అనుకూలీకరించండి (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"సమతుల్య ఆడియో మరియు కనెక్షన్ నాణ్యత (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"కనెక్షన్ నాణ్యత కోసం అనుకూలీకరించండి (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"ప్రాధాన్య శబ్ద నాణ్యత (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"ప్రామాణికం (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"ప్రాధాన్య కనెక్షన్ (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"ఆడియో నాణ్యత కోసం అనుకూలీకరించండి"</item>
+    <item msgid="4327143584633311908">"సమతుల్య ఆడియో మరియు కనెక్షన్ నాణ్యత"</item>
+    <item msgid="6155648878105378550">"కనెక్షన్ నాణ్యత కోసం అనుకూలీకరించండి"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ఆఫ్"</item>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 0d21371..8379dd8 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"ఎల్లప్పుడూ సెల్యులార్ డేటాను సక్రియంగా ఉంచు"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"సంపూర్ణ వాల్యూమ్‌‍ను నిలిపివేయి"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"బ్లూటూత్ ఆడియో కోడెక్"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"ప్రాధాన్య బ్లూటూత్ A2DP కోడెక్‌ను ఎంచుకోండి"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"బ్లూటూత్ ఆడియో కోడెక్‌ని ఎంచుకోండి"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"బ్లూటూత్ ఆడియో నమూనా రేట్"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"ప్రాధాన్య బ్లూటూత్ A2DP కోడెక్ నమూనా రేట్‌ను ఎంచుకోండి"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"బ్లూటూత్ ఆడియో కోడెక్‌ని ఎంచుకోండి:\nనమూనా రేటు"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"ఒక్కో నమూనాకు బ్లూటూత్ ఆడియో బిట్‌లు"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"ఒక్కో నమూనాకు ప్రాధాన్య బ్లూటూత్ A2DP కోడెక్ బిట్‌లను ఎంచుకోండి"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"బ్లూటూత్ ఆడియో కోడెక్‌ని ఎంచుకోండి:\nఒక్కో నమూనాలో బిట్‌లు"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"బ్లూటూత్ ఆడియో ఛానెల్ మోడ్"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"ప్రాధాన్య బ్లూటూత్ A2DP కోడెక్ ఛానెల్ మోడ్‌ను ఎంచుకోండి"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"బ్లూటూత్ ఆడియో LDAC ప్లేబ్యాక్ నాణ్యత"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"ప్రాధాన్య బ్లూటూత్ A2DP కోడెక్ LDAC ప్లేబ్యాక్ నాణ్యతను ఎంచుకోండి"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"బ్లూటూత్ ఆడియో కోడెక్‌ని ఎంచుకోండి:\nఛానెల్ మోడ్"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"బ్లూటూత్ ఆడియో LDAC కోడెక్: ప్లేబ్యాక్ నాణ్యత"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"బ్లూటూత్ ఆడియో LDAC కోడెక్‌ని ఎంచుకోండి:\nప్లేబ్యాక్ నాణ్యత"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ప్రసారం చేస్తోంది: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"వైర్‌లెస్ ప్రదర్శన ప్రమాణపత్రం కోసం ఎంపికలను చూపు"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ఎంపికలో SSID RSSI ప్రకారం చూపబడే Wi‑Fi లాగింగ్ స్థాయిని పెంచండి"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"ప్రారంభించబడినప్పుడు, Wi‑Fi సిగ్నల్ బలహీనంగా ఉంటే డేటా కనెక్షన్‌ను సెల్యులార్‌కి మార్చేలా Wi‑Fiపై మరింత తీవ్ర ఒత్తిడి కలుగుతుంది"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"సహాయం &amp; అభిప్రాయం"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"మెను"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"డెమో మోడ్‌లో ఫ్యాక్టరీ రీసెట్‌ను నిర్వహించడానికి పాస్‌వర్డ్‌ను నమోదు చేయండి"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"తదుపరి"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"పాస్‌వర్డ్ అవసరం"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 1032337..519a439 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"ใช้การตรวจสอบ HDCP เสมอ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ค่าเริ่มต้น"</item>
+    <item msgid="7065842274271279580">"ใช้การเลือกระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ค่าเริ่มต้น"</item>
+    <item msgid="5062108632402595000">"ใช้การเลือกระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ค่าเริ่มต้น"</item>
+    <item msgid="3093023430402746802">"ใช้การเลือกระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ค่าเริ่มต้น"</item>
+    <item msgid="3214516120190965356">"ใช้การเลือกระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ค่าเริ่มต้น"</item>
+    <item msgid="2684127272582591429">"ใช้การเลือกระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="5618929009984956469">"16 บิต/ตัวอย่าง"</item>
     <item msgid="3412640499234627248">"24 บิต/ตัวอย่าง"</item>
     <item msgid="121583001492929387">"32 บิต/ตัวอย่าง"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ค่าเริ่มต้น"</item>
+    <item msgid="1081159789834584363">"ใช้การเลือกระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="4726688794884191540">"16 บิต/ตัวอย่าง"</item>
     <item msgid="305344756485516870">"24 บิต/ตัวอย่าง"</item>
     <item msgid="244568657919675099">"32 บิต/ตัวอย่าง"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ค่าเริ่มต้น"</item>
+    <item msgid="5226878858503393706">"ใช้การเลือกระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="4106832974775067314">"โมโน"</item>
     <item msgid="5571632958424639155">"สเตอริโอ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ค่าเริ่มต้น"</item>
+    <item msgid="4118561796005528173">"ใช้การเลือกระบบ (ค่าเริ่มต้น)"</item>
     <item msgid="8900559293912978337">"โมโน"</item>
     <item msgid="8883739882299884241">"สเตอริโอ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"คุณภาพเสียงที่ต้องการ (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"มาตรฐาน (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"การเชื่อมต่อที่ต้องการ (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"เพิ่มประสิทธิภาพสำหรับคุณภาพเสียง (990 kbps/909 kbps)"</item>
+    <item msgid="2921767058740704969">"คุณภาพเสียงและการเชื่อมต่อที่สมดุล (660 kbps/606 kbps)"</item>
+    <item msgid="3682554248829489641">"เพิ่มประสิทธิภาพสำหรับคุณภาพการเชื่อมต่อ (330 kbps/303 kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"คุณภาพเสียงที่ต้องการ (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"มาตรฐาน (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"การเชื่อมต่อที่ต้องการ (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"เพิ่มประสิทธิภาพสำหรับคุณภาพเสียง"</item>
+    <item msgid="4327143584633311908">"คุณภาพเสียงและการเชื่อมต่อที่สมดุล"</item>
+    <item msgid="6155648878105378550">"เพิ่มประสิทธิภาพสำหรับคุณภาพการเชื่อมต่อ"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"ปิด"</item>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 60d91b5..8f9bd86 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"เปิดใช้ข้อมูลมือถือเสมอ"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ปิดใช้การควบคุมระดับเสียงของอุปกรณ์อื่น"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"ตัวแปลงรหัสเสียงบลูทูธ"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"เลือกตัวแปลงรหัสบลูทูธ A2DP ที่ต้องการ"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"เลือกตัวแปลงรหัสเสียงบลูทูธ"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"อัตราตัวอย่างเสียงบลูทูธ"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"เลือกอัตราตัวอย่างของตัวแปลงรหัสบลูทูธ A2DP ที่ต้องการ"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"เลือกตัวแปลงรหัสเสียงบลูทูธ:\nอัตราการสุ่มตัวอย่าง"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"บิตต่อตัวอย่างของเสียงบลูทูธ"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"เลือกบิตต่อตัวอย่างของตัวแปลงรหัสบลูทูธ A2DP ที่ต้องการ"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"เลือกตัวแปลงรหัสเสียงบลูทูธ:\nบิตต่อตัวอย่าง"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"โหมดช่องสัญญาณเสียงบลูทูธ"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"เลือกโหมดช่องสัญญาณเสียงของตัวแปลงรหัสบลูทูธ A2DP ที่ต้องการ"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"คุณภาพการเล่นเสียงบลูทูธ LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"เลือกคุณภาพการเล่น LDAC ของตัวแปลงรหัสบลูทูธ A2DP ที่ต้องการ"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"เลือกตัวแปลงรหัสเสียงบลูทูธ:\nโหมดช่องสัญญาณ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"เลือกตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC:\nคุณภาพการเล่น"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"สตรีมมิง: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"เมื่อเปิดใช้แล้ว Wi-Fi จะส่งผ่านการเชื่อมต่อข้อมูลไปยังเครือข่ายมือถือในทันทีที่พบสัญญาณ Wi-Fi อ่อน"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"ความช่วยเหลือและความคิดเห็น"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"เมนู"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"ป้อนรหัสผ่านเพื่อรีเซ็ตค่าในโหมดสาธิต"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"ถัดไป"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"ต้องป้อนรหัสผ่าน"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
index c0e9843..c67be8a 100644
--- a/packages/SettingsLib/res/values-tl/arrays.xml
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Gamitin lang ang pagsusuring HDCP para sa nilalamang DRM"</item>
     <item msgid="45075631231212732">"Palaging gumamit ng pagsusuring HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Default"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Default"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Default"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Default"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Default"</item>
-    <item msgid="5618929009984956469">"16 bits/sample"</item>
-    <item msgid="3412640499234627248">"24 bits/sample"</item>
-    <item msgid="121583001492929387">"32 bits/sample"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Default"</item>
-    <item msgid="4726688794884191540">"16 bits/sample"</item>
-    <item msgid="305344756485516870">"24 bits/sample"</item>
-    <item msgid="244568657919675099">"32 bits/sample"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Default"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Default"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Gustong kalidad tunog (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Karaniwan (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Gustong koneksyon (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Gustong kalidad tunog (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Karaniwan (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Gustong koneksyon (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"I-off"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 58781b2..682ee0ed 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Palaging aktibo ang cellular data"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"I-disable ang absolute volume"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Audio Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Piliin ang Gustong Bluetooth A2DP Codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Sample na Rate ng Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Piliin ang Gustong Sample na Rate ng Bluetooth A2DP Codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits Per Sample ng Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Piliin ang Gustong Bits Per Sample ng Bluetooth A2DP Codec"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Channel Mode ng Bluetooth Audio"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Piliin ang Gustong Channel Mode ng Bluetooth A2DP Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Playback Quality ng Bluetooth Audio LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Piliin ang Gustong Playback Quality ng Bluetooth A2DP Codec LDAC"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ipakita ang mga opsyon para sa certification ng wireless display"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Pataasin ang antas ng Wi‑Fi logging, ipakita sa bawat SSID RSSI sa Wi‑Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Kapag naka-enable, mas magiging agresibo ang Wi‑Fi sa paglipat ng koneksyon ng data sa Cellular, kapag mahina ang signal ng Wi‑Fi"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Tulong at feedback"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Ilagay ang password upang mag-factory reset sa demo mode"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Susunod"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Kinakailangan ang password"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
index ee668c7..00cf236 100644
--- a/packages/SettingsLib/res/values-tr/arrays.xml
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"HDCP denetimini yalnızca DRM içeriği için kullan"</item>
     <item msgid="45075631231212732">"HDCP denetimini her zaman kullan"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Varsayılan"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Varsayılan"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Varsayılan"</item>
-    <item msgid="8895532488906185219">"44,1 kHz"</item>
-    <item msgid="2909915718994807056">"48,0 kHz"</item>
-    <item msgid="3347287377354164611">"88,2 kHz"</item>
-    <item msgid="1234212100239985373">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Varsayılan"</item>
-    <item msgid="4482862757811638365">"44,1 kHz"</item>
-    <item msgid="354495328188724404">"48,0 kHz"</item>
-    <item msgid="7329816882213695083">"88,2 kHz"</item>
-    <item msgid="6967397666254430476">"96,0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Varsayılan"</item>
-    <item msgid="5618929009984956469">"16 bit/örnek"</item>
-    <item msgid="3412640499234627248">"24 bit/örnek"</item>
-    <item msgid="121583001492929387">"32 bit/örnek"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Varsayılan"</item>
-    <item msgid="4726688794884191540">"16 bit/örnek"</item>
-    <item msgid="305344756485516870">"24 bit/örnek"</item>
-    <item msgid="244568657919675099">"32 bit/örnek"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Varsayılan"</item>
-    <item msgid="4106832974775067314">"Mono"</item>
-    <item msgid="5571632958424639155">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Varsayılan"</item>
-    <item msgid="8900559293912978337">"Mono"</item>
-    <item msgid="8883739882299884241">"Stereo"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Ses kalitesi öncelikli (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Standart (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Bağlantı öncelikli (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Ses kalitesi öncelikli (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Standart (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Bağlantı öncelikli (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Kapalı"</item>
     <item msgid="1593289376502312923">"64 KB"</item>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index b5c081a..3101b2c 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Hücresel veri her zaman etkin"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Mutlak sesi iptal et"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth Ses Codec\'i"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Tercih Edilen Bluetooth A2DP Codec\'ini Seçin"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth Ses Örnek Hızı"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Tercih Edilen Bluetooth A2DP Codec\'i Örnek Hızını Seçin"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth Ses Örnek Başına Bit Sayısı"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Tercih Edilen Bluetooth A2DP Codec\'i Örnek Başına Bit Sayısını Seçin"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth Ses Kanalı Modu"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Tercih Edilen Bluetooth A2DP Codec\'i Kanal Modunu Seçin"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth Ses LDAC Oynatma Kalitesi"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Tercih Edilen Bluetooth A2DP Codec\'i LDAC Oynatma Kalitesini Seçin"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Kablosuz ekran sertifikası seçeneklerini göster"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Kablosuz günlük kaydı seviyesini artır. Kablosuz Seçici\'de her bir SSID RSSI için göster."</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Etkinleştirildiğinde, Kablosuz ağ sinyali zayıfken veri bağlantısının Hücresel ağa geçirilmesinde daha agresif olunur"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Yardım ve geri bildirim"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Demo modunda sıfırlamak için şifreyi girin"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Sonraki"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Şifre gerekli"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 91a6d6f..dd661c4 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"Використовувати перевірку HDCP лише для вмісту, захищеного DRM"</item>
     <item msgid="45075631231212732">"Завжди використовувати перевірку HDCP"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"За умовчанням"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"За умовчанням"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"За умовчанням"</item>
-    <item msgid="8895532488906185219">"44,1 кГц"</item>
-    <item msgid="2909915718994807056">"48 кГц"</item>
-    <item msgid="3347287377354164611">"88,2 кГц"</item>
-    <item msgid="1234212100239985373">"96 кГц"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"За умовчанням"</item>
-    <item msgid="4482862757811638365">"44,1 кГц"</item>
-    <item msgid="354495328188724404">"48 кГц"</item>
-    <item msgid="7329816882213695083">"88,2 кГц"</item>
-    <item msgid="6967397666254430476">"96 кГц"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"За умовчанням"</item>
-    <item msgid="5618929009984956469">"16 бітів на зразок"</item>
-    <item msgid="3412640499234627248">"24 біти на зразок"</item>
-    <item msgid="121583001492929387">"32 біти на зразок"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"За умовчанням"</item>
-    <item msgid="4726688794884191540">"16 бітів на зразок"</item>
-    <item msgid="305344756485516870">"24 біти на зразок"</item>
-    <item msgid="244568657919675099">"32 біти на зразок"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"За умовчанням"</item>
-    <item msgid="4106832974775067314">"Моно"</item>
-    <item msgid="5571632958424639155">"Стерео"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"За умовчанням"</item>
-    <item msgid="8900559293912978337">"Моно"</item>
-    <item msgid="8883739882299884241">"Стерео"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Реком. якість звуку (990 та 909 Кбіт/с)"</item>
-    <item msgid="138837449700903545">"Стандартна якість (660 і 606 Кбіт/с)"</item>
-    <item msgid="4777177307869441982">"Рекоменд. з’єднання (330 і 303 Кбіт/с)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Реком. якість звуку (990 та 909 Кбіт/с)"</item>
-    <item msgid="9091111147684472529">"Стандартна якість (660 і 606 Кбіт/с)"</item>
-    <item msgid="3367904477834831032">"Рекоменд. з’єднання (330 і 303 Кбіт/с)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Вимкнено"</item>
     <item msgid="1593289376502312923">"64 Кб"</item>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index bafd8b6..5e241f5 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Не вимикати передавання даних"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Вимкнути абсолютну гучність"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Кодек для аудіо Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Виберіть кодек Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Частота вибірки для аудіо Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Виберіть частоту вибірки для кодека Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Кількість бітів на зразок для аудіо Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Виберіть кількість бітів на зразок для кодека Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Режим каналу для аудіо Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Виберіть режим каналу для кодека Bluetooth A2DP"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Якість відтворення аудіо Bluetooth LDAC"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Виберіть якість відтворення для кодека Bluetooth A2DP"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показати параметри сертифікації бездротового екрана"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Показувати в журналі RSSI для кожного SSID під час вибору Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Примусово перемикатися на мобільну мережу, коли сигнал Wi-Fi слабкий"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Довідка й відгуки"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Введіть пароль, щоб скинути налаштування в демо-режимі"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Далі"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Потрібен пароль"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index d7dd3d2..d55bd2b 100644
--- a/packages/SettingsLib/res/values-ur/arrays.xml
+++ b/packages/SettingsLib/res/values-ur/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"‏ہمیشہ HDCP چیکنگ استعمال کریں"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"ڈیفالٹ"</item>
+    <item msgid="7065842274271279580">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"ڈیفالٹ"</item>
+    <item msgid="5062108632402595000">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"ڈیفالٹ"</item>
+    <item msgid="3093023430402746802">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"ڈیفالٹ"</item>
+    <item msgid="3214516120190965356">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"ڈیفالٹ"</item>
+    <item msgid="2684127272582591429">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="5618929009984956469">"16 بٹس/نمونہ"</item>
     <item msgid="3412640499234627248">"24 بٹس/نمونہ"</item>
     <item msgid="121583001492929387">"32 بٹس/نمونہ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"ڈیفالٹ"</item>
+    <item msgid="1081159789834584363">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="4726688794884191540">"16 بٹس/نمونہ"</item>
     <item msgid="305344756485516870">"24 بٹس/نمونہ"</item>
     <item msgid="244568657919675099">"32 بٹس/نمونہ"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"ڈیفالٹ"</item>
+    <item msgid="5226878858503393706">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="4106832974775067314">"مونو"</item>
     <item msgid="5571632958424639155">"اسٹیریو"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"ڈیفالٹ"</item>
+    <item msgid="4118561796005528173">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
     <item msgid="8900559293912978337">"مونو"</item>
     <item msgid="8883739882299884241">"اسٹیریو"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"‏آواز کا معیار ترجیحی (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"‏معیاری (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"‏کنکشن ترجیحی (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"‏آڈیو کے معیار کے لیے بہتر بنائیں (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"‏متوازن آڈیو اور کنکشن کا معیار (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"‏کنکشن کے معیار کے لیے بہتر بنائیں (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"‏آواز کا معیار ترجیحی (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"‏معیاری (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"‏کنکشن ترجیحی (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"آڈیو کے معیار کے لیے بہتر بنائیں"</item>
+    <item msgid="4327143584633311908">"متوازن آڈیو اور کنکشن کا معیار"</item>
+    <item msgid="6155648878105378550">"کنکشن کے معیار کے لیے بہتر بنائیں"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"آف"</item>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index ce27df8..d4c97cd 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"سیلولر ڈیٹا کو ہمیشہ فعال رکھیں"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"مطلق والیوم کو غیر فعال کریں"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"بلوٹوتھ آڈیو کوڈیک"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"‏ترجیحی بلوٹوتھ A2DP کوڈیک منتخب کریں"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"بلوٹوتھ آڈیو کوڈیک منتخب کریں"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"بلوٹوتھ آڈیو کے نمونے کی شرح"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"‏ترجیحی بلوٹوتھ A2DP کوڈیک نمونے کی شرح منتخب کریں"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"بلوٹوتھ آڈیو کوڈیک منتخب کریں:\nنمونے کی شرح"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"بلوٹوتھ آڈیو بٹس فی نمونہ"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"‏ترجیحی بلو ٹوتھ A2DP کوڈیک بٹس فی نمونہ منتخب کریں"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"بلوٹوتھ آڈیو کوڈیک منتخب کریں:\nبٹس فی نمونہ"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"بلوٹوتھ آڈیو چینل موڈ"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"‏ترجیحی بلوٹوتھ A2DP کوڈیک چینل موڈ منتخب کریں"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"‏بلوٹوتھ آڈیو LDAC پلے بیک کا معیار"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"‏ترجیحی بلوٹوتھ A2DP کوڈیک LDAC پلے بیک کا معیار منتخب کریں"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"بلوٹوتھ آڈیو کوڈیک منتخب کریں:\nچینل موڈ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"‏بلوٹوتھ آڈیو LDAC کوڈیک: پلے بیک کا معیار"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"‏بلوٹوتھ آڈیو LDAC کوڈیک منتخب کریں:\nپلے بیک کا معیار"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"سلسلہ بندی: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"وائرلیس ڈسپلے سرٹیفیکیشن کیلئے اختیارات دکھائیں"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‏Wi‑Fi لاگنگ لیول میں اضافہ کریں، Wi‑Fi منتخب کنندہ میں فی SSID RSSI دکھائیں"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"‏فعال ہونے پر، جب Wi‑Fi سگنل کمزور ہوگا تو Wi‑Fi سیلولر پر ڈیٹا کنکشن بھیجنے کیلئے مزید جارحانہ کاروائی کرے گا۔"</string>
@@ -352,4 +353,10 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"مدد اور تاثرات"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"مینو"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <!-- no translation found for retail_demo_reset_message (118771671364131297) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_next (8356731459226304963) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_title (696589204029930100) -->
+    <skip />
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml
index 11b3de7..f222d1f 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Har doim HDCP tekshiruvidan foydalanilsin"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Standart"</item>
+    <item msgid="7065842274271279580">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Standart"</item>
+    <item msgid="5062108632402595000">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Standart"</item>
+    <item msgid="3093023430402746802">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="8895532488906185219">"44.1 kGs"</item>
     <item msgid="2909915718994807056">"48.0 kGs"</item>
     <item msgid="3347287377354164611">"88.2 kGs"</item>
     <item msgid="1234212100239985373">"96.0 kGs"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Standart"</item>
+    <item msgid="3214516120190965356">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="4482862757811638365">"44.1 kGs"</item>
     <item msgid="354495328188724404">"48.0 kGs"</item>
     <item msgid="7329816882213695083">"88.2 kGs"</item>
     <item msgid="6967397666254430476">"96.0 kGs"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Standart"</item>
+    <item msgid="2684127272582591429">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="5618929009984956469">"16 bit/namuna"</item>
     <item msgid="3412640499234627248">"24 bit/namuna"</item>
     <item msgid="121583001492929387">"32 bit/namuna"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Standart"</item>
+    <item msgid="1081159789834584363">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="4726688794884191540">"16 bit/namuna"</item>
     <item msgid="305344756485516870">"24 bit/namuna"</item>
     <item msgid="244568657919675099">"32 bit/namuna"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Standart"</item>
+    <item msgid="5226878858503393706">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="4106832974775067314">"Mono"</item>
     <item msgid="5571632958424639155">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Standart"</item>
+    <item msgid="4118561796005528173">"Tizim tanlovi (birlamchi)"</item>
     <item msgid="8900559293912978337">"Mono"</item>
     <item msgid="8883739882299884241">"Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"O‘zingizga ma’qul ovoz sifati (990/909 kbit/s)"</item>
-    <item msgid="138837449700903545">"Standart (660/606 kbit/s)"</item>
-    <item msgid="4777177307869441982">"O‘zingizga ma’qul ulanish (330/303 kbit/s)"</item>
+    <item msgid="3411577996960199959">"Audio sifati uchun moslashtirish (990/909 kbit/s)"</item>
+    <item msgid="2921767058740704969">"Audio sifati balansi va ulanish tezligi (660/606 kbit/s)"</item>
+    <item msgid="3682554248829489641">"Ulanish sifati uchun moslashtirish (330/303 kbit/s)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"O‘zingizga ma’qul ovoz sifati (990/909 kbit/s)"</item>
-    <item msgid="9091111147684472529">"Standart (660/606 kbit/s)"</item>
-    <item msgid="3367904477834831032">"O‘zingizga ma’qul ulanish (330/303 kbit/s)"</item>
+    <item msgid="7668834469173465015">"Audio sifati uchun moslashtirish"</item>
+    <item msgid="4327143584633311908">"Audio sifati balansi va ulanish tezligi"</item>
+    <item msgid="6155648878105378550">"Ulanish sifati uchun moslashtirish"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"O‘chiq"</item>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 6efc6c4..b5460c2 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Mobil internet o‘chirilmasin"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Ovoz balangligining mutlaq darajasini o‘chirib qo‘yish"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Bluetooth audio kodeki"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"O‘zingizga ma’qul Bluetooth A2DP kodekini tanlang"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Bluetooth orqali uzatish uchun audiokodek"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Bluetooth audio namunasi chastotasi"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"O‘zingizga ma’qul Bluetooth A2DP kodek namunasi chastotasini tanlang"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Bluetooth orqali uzatish uchun audiokodek:\nnamuna chastotasi"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth audio namunasidagi bitlar soni"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"O‘zingizga ma’qul Bluetooth A2DP kodek namunasidagi bitlar sonini tanlang"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Bluetooth orqali uzatish uchun audiokodek:\nnamunadagi bitlar soni"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Bluetooth audio kanali rejimi"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"O‘zingizga ma’qul Bluetooth A2DP kodek kanali rejimini tanlang"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Bluetooth audio LDAC ijrosi sifati"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"O‘zingizga ma’qul Bluetooth A2DP kodek LDAC ijrosi sifatini tanlang"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Bluetooth orqali uzatish uchun audiokodek:\nkanal rejimi"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"LDAC audiokodeki bilan ijro etish sifati (Bluetooth orqali)"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"LDAC audiokodeki:\nijro sifati"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Translatsiya: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz monitorlarni sertifikatlash parametrini ko‘rsatish"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi ulanishini tanlashda har bir SSID uchun jurnalda ko‘rsatilsin"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Agar ushbu funksiya yoqilsa, Wi-Fi signali past bo‘lganda internetga ulanish majburiy ravishda mobil internetga o‘tkaziladi."</string>
@@ -240,7 +241,7 @@
     <string name="enable_opengl_traces_title" msgid="6790444011053219871">"OpenGL trassasini yoqish"</string>
     <string name="usb_audio_disable_routing" msgid="8114498436003102671">"Audio uzatishni o‘ch. qo‘yish (USB)"</string>
     <string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"Tashqi USB qurilmaga avto-yo‘naltirishni o‘ch. qo‘yish"</string>
-    <string name="debug_layout" msgid="5981361776594526155">"Elementlar chegarasini ko‘rsatish"</string>
+    <string name="debug_layout" msgid="5981361776594526155">"Elementlar hoshiyasi"</string>
     <string name="debug_layout_summary" msgid="2001775315258637682">"Klip, maydon va h.k. chegaralarini ko‘rsatish"</string>
     <string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"O‘ngdan chapga qarab yozish"</string>
     <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"Barcha tillarda o‘ngdan chapga qarab yozish"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Yordam va fikr-mulohaza"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Demo rejimda zavod holatiga qaytarish uchun parolni kiriting"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Keyingisi"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Parolni kiritish zarur"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index e7dbd4e..7b2368f 100644
--- a/packages/SettingsLib/res/values-vi/arrays.xml
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Luôn sử dụng kiểm tra HDCP"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Mặc định"</item>
+    <item msgid="7065842274271279580">"Sử dụng lựa chọn hệ thống (Mặc định)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Mặc định"</item>
+    <item msgid="5062108632402595000">"Sử dụng lựa chọn hệ thống (Mặc định)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Mặc định"</item>
+    <item msgid="3093023430402746802">"Sử dụng lựa chọn hệ thống (Mặc định)"</item>
     <item msgid="8895532488906185219">"44,1 kHz"</item>
     <item msgid="2909915718994807056">"48,0 kHz"</item>
     <item msgid="3347287377354164611">"88,2 kHz"</item>
     <item msgid="1234212100239985373">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Mặc định"</item>
+    <item msgid="3214516120190965356">"Sử dụng lựa chọn hệ thống (Mặc định)"</item>
     <item msgid="4482862757811638365">"44,1 kHz"</item>
     <item msgid="354495328188724404">"48,0 kHz"</item>
     <item msgid="7329816882213695083">"88,2 kHz"</item>
     <item msgid="6967397666254430476">"96,0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Mặc định"</item>
+    <item msgid="2684127272582591429">"Sử dụng lựa chọn hệ thống (Mặc định)"</item>
     <item msgid="5618929009984956469">"16 bit/mẫu"</item>
     <item msgid="3412640499234627248">"24 bit/mẫu"</item>
     <item msgid="121583001492929387">"32 bit/mẫu"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Mặc định"</item>
+    <item msgid="1081159789834584363">"Sử dụng lựa chọn hệ thống (Mặc định)"</item>
     <item msgid="4726688794884191540">"16 bit/mẫu"</item>
     <item msgid="305344756485516870">"24 bit/mẫu"</item>
     <item msgid="244568657919675099">"32 bit/mẫu"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Mặc định"</item>
+    <item msgid="5226878858503393706">"Sử dụng lựa chọn hệ thống (Mặc định)"</item>
     <item msgid="4106832974775067314">"Đơn âm"</item>
     <item msgid="5571632958424639155">"Âm thanh nổi"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Mặc định"</item>
+    <item msgid="4118561796005528173">"Sử dụng lựa chọn hệ thống (Mặc định)"</item>
     <item msgid="8900559293912978337">"Đơn âm"</item>
     <item msgid="8883739882299884241">"Âm thanh nổi"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"C.lượng â.thanh ưu tiên (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Tiêu chuẩn (660kb/giây/606kb/giây)"</item>
-    <item msgid="4777177307869441982">"Kết nối được ưu tiên (330kb/giây/303kb/giây)"</item>
+    <item msgid="3411577996960199959">"Tối ưu hóa chất lượng âm thanh (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"Chất lượng kết nối và âm thanh cân bằng (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"Tối ưu hóa chất lượng kết nối (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"C.lượng â.thanh ưu tiên (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Tiêu chuẩn (660kb/giây/606kb/giây)"</item>
-    <item msgid="3367904477834831032">"Kết nối được ưu tiên (330kb/giây/303kb/giây)"</item>
+    <item msgid="7668834469173465015">"Tối ưu hóa chất lượng âm thanh"</item>
+    <item msgid="4327143584633311908">"Chất lượng kết nối và âm thanh cân bằng"</item>
+    <item msgid="6155648878105378550">"Tối ưu hóa chất lượng kết nối"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Tắt"</item>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 5822f18..ff9a557 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Dữ liệu di động luôn hoạt động"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Vô hiệu hóa âm lượng tuyệt đối"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Codec âm thanh Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Chọn Codec A2DP Bluetooth ưu tiên"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Chọn Codec âm thanh Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Tốc độ lấy mẫu âm thanh Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Chọn tốc độ lấy mẫu Codec A2DP Bluetooth ưu tiên"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Chọn Codec âm thanh Bluetooth:\nTốc độ lấy mẫu"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Số bit âm thanh Bluetooth mỗi mẫu"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Chọn số bit Codec A2DP Bluetooth ưu tiên mỗi mẫu"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Chọn Codec âm thanh Bluetooth:\nSố bit trên mẫu"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Chế độ kênh âm thanh Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Chọn chế độ kênh Codec A2DP Bluetooth ưu tiên"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Chất lượng phát lại LDAC Âm thanh Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Chọn chất lượng phát lại LDAC Codec A2DP Bluetooth ưu tiên"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Chọn Codec âm thanh Bluetooth:\nChế độ kênh"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Codec LDAC âm thanh Bluetooth: Chất lượng phát lại"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Chọn Codec LDAC âm thanh Bluetooth:\nChất lượng phát lại"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Truyền trực tuyến: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Hiển thị tùy chọn chứng nhận hiển thị không dây"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tăng mức ghi nhật ký Wi‑Fi, hiển thị mỗi SSID RSSI trong bộ chọn Wi‑Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Khi được bật, Wi‑Fi sẽ tích cực hơn trong việc chuyển vùng kết nối dữ liệu sang mạng di động khi tín hiệu Wi‑Fi yếu"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Trợ giúp và phản hồi"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Nhập mật khẩu để tiến hành khôi phục cài đặt gốc ở chế độ trình diễn"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Tiếp theo"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Yêu cầu mật khẩu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index 0ef095d..5ec63b3 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"仅使用 HDCP 检查 DRM 内容"</item>
     <item msgid="45075631231212732">"始终使用 HDCP 检查"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"默认"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"默认"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"默认"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"默认"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"默认"</item>
-    <item msgid="5618929009984956469">"16 位/样本"</item>
-    <item msgid="3412640499234627248">"24 位/样本"</item>
-    <item msgid="121583001492929387">"32 位/样本"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"默认"</item>
-    <item msgid="4726688794884191540">"16 位/样本"</item>
-    <item msgid="305344756485516870">"24 位/样本"</item>
-    <item msgid="244568657919675099">"32 位/样本"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"默认"</item>
-    <item msgid="4106832974775067314">"单声道"</item>
-    <item msgid="5571632958424639155">"立体声"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"默认"</item>
-    <item msgid="8900559293912978337">"单声道"</item>
-    <item msgid="8883739882299884241">"立体声"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"音质优先 (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"标准 (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"连接优先 (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"音质优先 (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"标准 (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"连接优先 (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"关闭"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 35a1dfc..4c47ace 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"始终开启移动数据网络"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"停用绝对音量功能"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"蓝牙音频编解码器"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"选择首选的蓝牙 A2DP 编解码器"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"蓝牙音频采样率"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"选择首选的蓝牙 A2DP 编解码器采样率"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"蓝牙音频每样本位数"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"选择首选的蓝牙 A2DP 编解码器每样本位数"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"蓝牙音频声道模式"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"选择首选的蓝牙 A2DP 编解码器声道模式"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"蓝牙音频 LDAC 播放质量"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"选择首选的蓝牙 A2DP 编解码器 LDAC 播放质量"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"显示无线显示认证选项"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"提升WLAN日志记录级别(在WLAN选择器中显示每个SSID的RSSI)"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"开启此设置后,系统会在WLAN信号较弱时,主动将网络模式从WLAN网络切换到移动数据网络"</string>
@@ -352,4 +360,10 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"帮助和反馈"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"菜单"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <!-- no translation found for retail_demo_reset_message (118771671364131297) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_next (8356731459226304963) -->
+    <skip />
+    <!-- no translation found for retail_demo_reset_title (696589204029930100) -->
+    <skip />
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
index 872e7c9..953c8cc 100644
--- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"僅使用 HDCP 檢查 DRM 內容"</item>
     <item msgid="45075631231212732">"永遠使用 HDCP 檢查"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"預設"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"預設"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"預設"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"預設"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"預設"</item>
-    <item msgid="5618929009984956469">"每個樣本 16 位元"</item>
-    <item msgid="3412640499234627248">"每個樣本 24 位元"</item>
-    <item msgid="121583001492929387">"每個樣本 32 位元"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"預設"</item>
-    <item msgid="4726688794884191540">"每個樣本 16 位元"</item>
-    <item msgid="305344756485516870">"每個樣本 24 位元"</item>
-    <item msgid="244568657919675099">"每個樣本 32 位元"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"預設"</item>
-    <item msgid="4106832974775067314">"單聲道"</item>
-    <item msgid="5571632958424639155">"立體聲"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"預設"</item>
-    <item msgid="8900559293912978337">"單聲道"</item>
-    <item msgid="8883739882299884241">"立體聲"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"音質優先 (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"標準 (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"連線優先 (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"音質優先 (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"標準 (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"連線優先 (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"關閉"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 3c5c328..3d8b673 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"經常啟用流動數據"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"停用絕對音量功能"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"藍牙音訊編解碼器"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"選取偏好的藍牙 A2DP 編解碼器"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"藍牙音訊取樣率"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"選取偏好的藍牙 A2DP 編解碼器取樣率"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"藍牙音訊每個樣本位元數"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"選取偏好的藍牙 A2DP 編解碼器每個樣本位元數"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"藍牙音訊聲道模式"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"選取偏好的藍牙 A2DP 編解碼器聲道模式"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"藍牙音訊 LDAC 播放音質"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"選取偏好的藍牙 A2DP 編解碼器 LDAC 播放音質"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"啟用時,Wi-Fi 連線會在訊號不穩的情況下更積極轉換成流動數據連線"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"說明和意見反映"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"選單"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"輸入密碼即可在示範模式下重設原廠設定"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"下一步"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"請輸入密碼"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index ad99128..d426b30 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -58,66 +58,22 @@
     <item msgid="3878793616631049349">"僅使用 HDCP 檢查 DRM 內容"</item>
     <item msgid="45075631231212732">"一律使用 HDCP 檢查"</item>
   </string-array>
-  <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"預設"</item>
-    <item msgid="7539690996561263909">"SBC"</item>
-    <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
-    <item msgid="2301339338870319651">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"預設"</item>
-    <item msgid="6898329690939802290">"SBC"</item>
-    <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
-    <item msgid="508106435710925399">"LDAC"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"預設"</item>
-    <item msgid="8895532488906185219">"44.1 kHz"</item>
-    <item msgid="2909915718994807056">"48.0 kHz"</item>
-    <item msgid="3347287377354164611">"88.2 kHz"</item>
-    <item msgid="1234212100239985373">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"預設"</item>
-    <item msgid="4482862757811638365">"44.1 kHz"</item>
-    <item msgid="354495328188724404">"48.0 kHz"</item>
-    <item msgid="7329816882213695083">"88.2 kHz"</item>
-    <item msgid="6967397666254430476">"96.0 kHz"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"預設"</item>
-    <item msgid="5618929009984956469">"16 位元/樣本"</item>
-    <item msgid="3412640499234627248">"24 位元/樣本"</item>
-    <item msgid="121583001492929387">"32 位元/樣本"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"預設"</item>
-    <item msgid="4726688794884191540">"16 位元/樣本"</item>
-    <item msgid="305344756485516870">"24 位元/樣本"</item>
-    <item msgid="244568657919675099">"32 位元/樣本"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"預設"</item>
-    <item msgid="4106832974775067314">"單聲道"</item>
-    <item msgid="5571632958424639155">"立體聲"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"預設"</item>
-    <item msgid="8900559293912978337">"單聲道"</item>
-    <item msgid="8883739882299884241">"立體聲"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"音質優先 (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"標準 (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"連線優先 (330kbps/303kbps)"</item>
-  </string-array>
-  <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"音質優先 (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"標準 (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"連線優先 (330kbps/303kbps)"</item>
-  </string-array>
+    <!-- no translation found for bluetooth_a2dp_codec_titles:0 (7065842274271279580) -->
+    <!-- no translation found for bluetooth_a2dp_codec_titles:3 (500463122137421129) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:0 (5062108632402595000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_summaries:3 (3093550793512117000) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_titles:0 (3093023430402746802) -->
+    <!-- no translation found for bluetooth_a2dp_codec_sample_rate_summaries:0 (3214516120190965356) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_titles:0 (2684127272582591429) -->
+    <!-- no translation found for bluetooth_a2dp_codec_bits_per_sample_summaries:0 (1081159789834584363) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_titles:0 (5226878858503393706) -->
+    <!-- no translation found for bluetooth_a2dp_codec_channel_mode_summaries:0 (4118561796005528173) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:0 (3411577996960199959) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:1 (2921767058740704969) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_titles:2 (3682554248829489641) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:0 (7668834469173465015) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:1 (4327143584633311908) -->
+    <!-- no translation found for bluetooth_a2dp_codec_ldac_playback_quality_summaries:2 (6155648878105378550) -->
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"關閉"</item>
     <item msgid="1593289376502312923">"64K"</item>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 861834b..ad3b83a 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -171,15 +171,23 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"行動數據連線一律保持啟用狀態"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"停用絕對音量功能"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"藍牙音訊轉碼器"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"選取偏好的藍牙 A2DP 轉碼器"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_type_dialog_title (4558347981670553665) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"藍牙音訊取樣率"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"選取偏好的藍牙 A2DP 轉碼器取樣率"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_sample_rate_dialog_title (5628790207448471613) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"藍牙音訊每單位樣本位元數"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"選取偏好的藍牙 A2DP 轉碼器每單位樣本位元數"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_bits_per_sample_dialog_title (4546131401358681321) -->
+    <skip />
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"藍牙音訊聲道模式"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"選取偏好的藍牙 A2DP 轉碼器聲道模式"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"藍牙音訊 LDAC 播放品質"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"選取偏好的藍牙 A2DP 轉碼器 LDAC 播放品質"</string>
+    <!-- no translation found for bluetooth_select_a2dp_codec_channel_mode_dialog_title (9133545781346216071) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality (3619694372407843405) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (3181967377574368400) -->
+    <skip />
+    <!-- no translation found for bluetooth_select_a2dp_codec_streaming_label (5347862512596240506) -->
+    <skip />
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"啟用時,Wi-Fi 連線在訊號不穩的情況下會更積極轉換成行動數據連線"</string>
@@ -352,4 +360,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"說明與意見回饋"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"選單"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"如要在示範模式中恢復原廠設定,請輸入密碼"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"下一步"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"請輸入密碼"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 50c1274..50597f3 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -59,64 +59,64 @@
     <item msgid="45075631231212732">"Sebenzisa njalo ukuhlola kwe-HDPC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_titles">
-    <item msgid="1852387125374225729">"Okuzenzakalelayo"</item>
+    <item msgid="7065842274271279580">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="7539690996561263909">"SBC"</item>
     <item msgid="4260844283202960798">"aptX"</item>
-    <item msgid="7279983368484312990">"aptX-HD"</item>
+    <item msgid="500463122137421129">"aptX HD"</item>
     <item msgid="2301339338870319651">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
-    <item msgid="9072025520360316957">"Okuzenzakalelayo"</item>
+    <item msgid="5062108632402595000">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="6898329690939802290">"SBC"</item>
     <item msgid="1190434429082395888">"aptX"</item>
-    <item msgid="649699003004233053">"aptX-HD"</item>
+    <item msgid="3093550793512117000">"aptX HD"</item>
     <item msgid="508106435710925399">"LDAC"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-    <item msgid="7102940318360468759">"Okuzenzakalelayo"</item>
+    <item msgid="3093023430402746802">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="8895532488906185219">"44.1 kHz"</item>
     <item msgid="2909915718994807056">"48.0 kHz"</item>
     <item msgid="3347287377354164611">"88.2 kHz"</item>
     <item msgid="1234212100239985373">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
-    <item msgid="7224433008148687313">"Okuzenzakalelayo"</item>
+    <item msgid="3214516120190965356">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="4482862757811638365">"44.1 kHz"</item>
     <item msgid="354495328188724404">"48.0 kHz"</item>
     <item msgid="7329816882213695083">"88.2 kHz"</item>
     <item msgid="6967397666254430476">"96.0 kHz"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-    <item msgid="6694044160540313386">"Okuzenzakalelayo"</item>
+    <item msgid="2684127272582591429">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="5618929009984956469">"16 bits/isampula"</item>
     <item msgid="3412640499234627248">"24 bits/isampula"</item>
     <item msgid="121583001492929387">"32 bits/isampula"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
-    <item msgid="5091076677792306320">"Okuzenzakalelayo"</item>
+    <item msgid="1081159789834584363">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="4726688794884191540">"16 bits/isampula"</item>
     <item msgid="305344756485516870">"24 bits/isampula"</item>
     <item msgid="244568657919675099">"32 bits/isampula"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-    <item msgid="13423709606339855">"Okuzenzakalelayo"</item>
+    <item msgid="5226878858503393706">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="4106832974775067314">"Okukodwa"</item>
     <item msgid="5571632958424639155">"I-Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
-    <item msgid="8128478683963250130">"Okuzenzakalelayo"</item>
+    <item msgid="4118561796005528173">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
     <item msgid="8900559293912978337">"Okukodwa"</item>
     <item msgid="8883739882299884241">"I-Stereo"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="2944889121850394020">"Ikhwalithi yomsindo ekhethwayo (990kbps/909kbps)"</item>
-    <item msgid="138837449700903545">"Okujwayelekile (660kbps/606kbps)"</item>
-    <item msgid="4777177307869441982">"Uxhumo olukhethwayo (330kbps/303kbps)"</item>
+    <item msgid="3411577996960199959">"Thuthukisela ikhwalithi yomsindo (990kbps/909kbps)"</item>
+    <item msgid="2921767058740704969">"Umsindo obhalansile nekhwalithi yoxhumo (660kbps/606kbps)"</item>
+    <item msgid="3682554248829489641">"Thuthukisela ikhwalithi yoxhumo (330kbps/303kbps)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="172302906231378902">"Ikhwalithi yomsindo ekhethwayo (990kbps/909kbps)"</item>
-    <item msgid="9091111147684472529">"Okujwayelekile (660kbps/606kbps)"</item>
-    <item msgid="3367904477834831032">"Uxhumo olukhethwayo (330kbps/303kbps)"</item>
+    <item msgid="7668834469173465015">"Thuthukisela ikhwalithi yomsindo"</item>
+    <item msgid="4327143584633311908">"Umsindo obhalansile nekhwalithi yoxhumo"</item>
+    <item msgid="6155648878105378550">"Thuthukisela ikhwalithi yoxhumo"</item>
   </string-array>
   <string-array name="select_logd_size_titles">
     <item msgid="8665206199209698501">"Valiwe"</item>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 289f4f6..9f7e72e 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -171,15 +171,16 @@
     <string name="mobile_data_always_on" msgid="7745605759775320362">"Idatha yeselula ihlala isebenza"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Khubaza ivolumu ngokuphelele"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"I-Bluetooth Audio Codec"</string>
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="6470824182074383881">"Khetha i-Bluetooth A2DP Codec ethandwayo"</string>
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="4558347981670553665">"Khetha i-Bluetooth Audio Codec"</string>
     <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Isilinganiso sesampula yomsindo we-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="4263851572248033749">"Khetha isilinganiso sesampuli ye-Bluetooth A2DP Codec ethandwayo"</string>
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5628790207448471613">"Khetha i-Bluetooth Audio Codec:\nIsilinganiso sesampula"</string>
     <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Ama-Bits omsindo we-Bluetooth ngesampula ngayinye"</string>
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="2096170505745650345">"Khetha i-Bluetooth A2DP Codec Bits ethandwayo ngesampula ngayinye"</string>
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4546131401358681321">"Khetha i-Bluetooth Audio Codec:\nBits ngesampuli"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Imodi yesiteshi somsindo we-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="4073812880900816325">"Khetha imodi yesiteshi se-Bluetooth A2DP Codec ethandwayo"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="4846872213548295632">"Ikhwalithi yokudlala ye-LDAC yomsindo we-Bluetooth"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3940973633342423717">"Khetha ikhwalithi yokudlala ye-Bluetooth A2DP Codec LDAC ethandwayo"</string>
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="9133545781346216071">"Khetha i-Bluetooth Audio Codec:\nImodi yesiteshi"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"I-Bluetooth Audio LDAC Codec: Ikhwalithi yokudlala"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="3181967377574368400">"Khetha i-Bluetooth Audio LDAC Codec:\nIkhwalithi yokudlala"</string>
+    <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Ukusakaza: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Bonisa izinketho zokunikeza isitifiketi ukubukeka okungenantambo"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"khuphula izinga lokungena le-Wi-Fi, bonisa nge-SSID RSSI engayodwana kusikhethi se-Wi-Fi"</string>
     <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Uma inikwe amandla, i-Wi-Fi izoba namandla kakhulu ekunikezeleni ukuxhumeka kwedatha kuselula, uma isiginali ye-Wi-Fi iphansi"</string>
@@ -352,4 +353,7 @@
     <string name="help_feedback_label" msgid="6815040660801785649">"Usizo nempendulo"</string>
     <string name="content_description_menu_button" msgid="8182594799812351266">"Imenyu"</string>
     <string name="time_zone_gmt" msgid="2587097992671450782">"I-GMT"</string>
+    <string name="retail_demo_reset_message" msgid="118771671364131297">"Faka iphasiwedi ukuze wenze ukusetha kwefekthri kumodi yedemo"</string>
+    <string name="retail_demo_reset_next" msgid="8356731459226304963">"Okulandelayo"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Iphasiwedi iyadingeka"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 5c00985..e0157a6 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -105,10 +105,10 @@
 
     <!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40] -->
     <string-array name="bluetooth_a2dp_codec_titles">
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>SBC</item>
         <item>aptX</item>
-        <item>aptX-HD</item>
+        <item>aptX HD</item>
         <item>LDAC</item>
     </string-array>
 
@@ -123,16 +123,16 @@
 
     <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40]-->
     <string-array name="bluetooth_a2dp_codec_summaries" >
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>SBC</item>
         <item>aptX</item>
-        <item>aptX-HD</item>
+        <item>aptX HD</item>
         <item>LDAC</item>
     </string-array>
 
     <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40] -->
     <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>44.1 kHz</item>
         <item>48.0 kHz</item>
         <item>88.2 kHz</item>
@@ -150,7 +150,7 @@
 
     <!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40]-->
     <string-array name="bluetooth_a2dp_codec_sample_rate_summaries" >
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>44.1 kHz</item>
         <item>48.0 kHz</item>
         <item>88.2 kHz</item>
@@ -159,7 +159,7 @@
 
     <!-- Titles for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40] -->
     <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>16 bits/sample</item>
         <item>24 bits/sample</item>
         <item>32 bits/sample</item>
@@ -175,7 +175,7 @@
 
     <!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40]-->
     <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries" >
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>16 bits/sample</item>
         <item>24 bits/sample</item>
         <item>32 bits/sample</item>
@@ -183,7 +183,7 @@
 
     <!-- Titles for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40] -->
     <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>Mono</item>
         <item>Stereo</item>
     </string-array>
@@ -197,16 +197,16 @@
 
     <!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40]-->
     <string-array name="bluetooth_a2dp_codec_channel_mode_summaries" >
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>Mono</item>
         <item>Stereo</item>
     </string-array>
 
-    <!-- Titles for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=40] -->
+    <!-- Titles for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70] -->
     <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-        <item>Sound quality preferred (990kbps/909kbps)</item>
-        <item>Standard (660kbps/606kbps)</item>
-        <item>Connection preferred (330kbps/303kbps)</item>
+        <item>Optimized for Audio Quality (990kbps/909kbps)</item>
+        <item>Balanced Audio And Connection Quality (660kbps/606kbps)</item>
+        <item>Optimized for Connection Quality (330kbps/303kbps)</item>
     </string-array>
 
     <!-- Values for Bluetooth Audio Codec LDAC Playback Quaility selection preference. -->
@@ -216,11 +216,11 @@
         <item>1002</item>
     </string-array>
 
-    <!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=40]-->
+    <!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70]-->
     <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries" >
-        <item>Sound quality preferred (990kbps/909kbps)</item>
-        <item>Standard (660kbps/606kbps)</item>
-        <item>Connection preferred (330kbps/303kbps)</item>
+        <item>Optimized for Audio Quality</item>
+        <item>Balanced Audio And Connection Quality</item>
+        <item>Optimized for Connection Quality</item>
     </string-array>
 
     <!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] -->
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
index f064e4e..1f35d3e 100644
--- a/packages/SettingsLib/res/values/attrs.xml
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -36,7 +36,12 @@
     <declare-styleable name="WifiEncryptionState">
         <attr name="state_encrypted" format="boolean" />
     </declare-styleable>
+    <declare-styleable name="WifiSavedState">
+        <attr name="state_saved" format="boolean" />
+    </declare-styleable>
+
     <attr name="wifi_signal" format="reference" />
+    <attr name="wifi_friction" format="reference" />
 
     <declare-styleable name="UsageView">
         <attr name="android:colorAccent" />
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 93bd5dc..0129632 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -430,28 +430,31 @@
 
     <!-- UI debug setting: Select Bluetooth Audio Codec -->
     <string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec -->
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Preferred Bluetooth A2DP Codec</string>
+    <!-- UI debug setting: Select Bluetooth Audio Codec -->
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Bluetooth Audio Codec</string>
 
     <!-- UI debug setting: Select Bluetooth Audio Sample Rate -->
     <string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Sample Rate -->
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Preferred Bluetooth A2DP Codec Sample Rate</string>
+    <!-- UI debug setting: Select Bluetooth Audio Codec: Sample Rate -->
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Bluetooth Audio Codec:\u000ASample Rate</string>
 
     <!-- UI debug setting: Select Bluetooth Audio Bits Per Sample -->
     <string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Bits Per Sample -->
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Preferred Bluetooth A2DP Codec Bits Per Sample</string>
+    <!-- UI debug setting: Select Bluetooth Audio Codec: Bits Per Sample -->
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Bluetooth Audio Codec:\u000ABits Per Sample</string>
 
     <!-- UI debug setting: Select Bluetooth Audio Channel Mode -->
     <string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Channel Mode -->
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Preferred Bluetooth A2DP Codec Channel Mode</string>
+    <!-- UI debug setting: Select Bluetooth Audio Codec: Channel Mode -->
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Bluetooth Audio Codec:\u000AChannel Mode</string>
 
     <!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality -->
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Playback Quality</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec LDAC Playback Quality -->
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Preferred Bluetooth A2DP Codec LDAC Playback Quality</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Codec: Playback Quality</string>
+    <!-- UI debug setting: Select Bluetooth Audio LDAC Codec: LDAC Playback Quality -->
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Bluetooth Audio LDAC Codec:\u000APlayback Quality</string>
+
+    <!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming -->
+    <string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string>
 
     <!-- setting Checkbox summary whether to show options for wireless display certification  -->
     <string name="wifi_display_certification_summary">Show options for wireless display certification</string>
@@ -890,4 +893,11 @@
 
     <!-- Label for Greenwich mean time, used in a string like GMT+05:00. [CHAR LIMIT=NONE] -->
     <string name="time_zone_gmt">GMT</string>
+
+    <!-- Label for carrier demo mode factory reset confirmation dialog. [CHAR LIMIT=NONE] -->
+    <string name="retail_demo_reset_message">Enter password to perform factory reset in demo mode</string>
+    <!-- Label for positive button on carrier demo  mode factory reset confirmation dialog [CHAR LIMIT=40] -->
+    <string name="retail_demo_reset_next">Next</string>
+    <!-- Title for carrier demo mode factory reset confirmation dialog. [CHAR LIMIT=40] -->
+    <string name="retail_demo_reset_title">Password required</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/TronUtils.java b/packages/SettingsLib/src/com/android/settingslib/TronUtils.java
new file mode 100644
index 0000000..1d9d03a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/TronUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib;
+
+import android.content.Context;
+import android.net.ScoredNetwork;
+
+import com.android.internal.logging.MetricsLogger;
+
+/** Utilites for Tron Logging. */
+public final class TronUtils {
+
+    private TronUtils() {};
+
+    public static void logWifiSettingsBadge(Context context, int badgeEnum) {
+        logNetworkBadgeMetric(context, "settings_wifibadging", badgeEnum);
+    }
+
+    /**
+     * Logs an occurrence of the given network badge to a Histogram.
+     *
+     * @param context Context
+     * @param histogram the Tron histogram name to write to
+     * @param badgeEnum the {@link ScoredNetwork.Badging} badge value
+     * @throws IllegalArgumentException if the given badge enum is not supported
+     */
+    private static void logNetworkBadgeMetric(
+            Context context, String histogram, int badgeEnum)
+            throws IllegalArgumentException {
+        int bucket;
+        switch (badgeEnum) {
+            case ScoredNetwork.BADGING_NONE:
+                bucket = 0;
+                break;
+            case ScoredNetwork.BADGING_SD:
+                bucket = 1;
+                break;
+            case ScoredNetwork.BADGING_HD:
+                bucket = 2;
+                break;
+            case ScoredNetwork.BADGING_4K:
+                bucket = 3;
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported badge enum: " + badgeEnum);
+        }
+
+        MetricsLogger.histogram(context, histogram, bucket);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 6179244..ae6ada2a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -13,11 +13,16 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.net.ConnectivityManager;
+import android.net.ScoredNetwork;
 import android.os.BatteryManager;
 import android.os.UserManager;
 import android.print.PrintManager;
+import android.view.View;
+
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.UserIconDrawable;
 
@@ -29,6 +34,14 @@
     private static String sServicesSystemSharedLibPackageName;
     private static String sSharedSystemSharedLibPackageName;
 
+    public static final int[] WIFI_PIE_FOR_BADGING = {
+          com.android.internal.R.drawable.ic_signal_wifi_badged_0_bars,
+          com.android.internal.R.drawable.ic_signal_wifi_badged_1_bar,
+          com.android.internal.R.drawable.ic_signal_wifi_badged_2_bars,
+          com.android.internal.R.drawable.ic_signal_wifi_badged_3_bars,
+          com.android.internal.R.drawable.ic_signal_wifi_badged_4_bars
+    };
+
     /**
      * Return string resource that best describes combination of tethering
      * options available on this device.
@@ -160,10 +173,7 @@
 
     @ColorInt
     public static int getColorAccent(Context context) {
-        TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
-        @ColorInt int colorAccent = ta.getColor(0, 0);
-        ta.recycle();
-        return colorAccent;
+        return getColorAttr(context, android.R.attr.colorAccent);
     }
 
     @ColorInt
@@ -182,6 +192,29 @@
         return list.getDefaultColor();
     }
 
+    @ColorInt
+    public static int getDisabled(Context context, int inputColor) {
+        return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor);
+    }
+
+    @ColorInt
+    public static int applyAlphaAttr(Context context, int attr, int inputColor) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+        float alpha = ta.getFloat(0, 0);
+        ta.recycle();
+        alpha *= Color.alpha(inputColor);
+        return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor),
+                Color.blue(inputColor));
+    }
+
+    @ColorInt
+    public static int getColorAttr(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+        @ColorInt int colorAccent = ta.getColor(0, 0);
+        ta.recycle();
+        return colorAccent;
+    }
+
     /**
      * Determine whether a package is a "system package", in which case certain things (like
      * disabling notifications or disabling the package altogether) should be disallowed.
@@ -233,4 +266,48 @@
                 com.android.internal.R.string.config_deviceProvisioningPackage);
         return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
     }
+
+    /**
+     * Returns a badged Wifi icon drawable.
+     *
+     * <p>The first layer contains the Wifi pie and the second layer contains the badge. Callers
+     * should set the drawable to the appropriate size and tint color.
+     *
+     * @param context The caller's context (must have access to internal resources)
+     * @param level The number of bars to show (0-4)
+     * @param badge The badge enum {@see android.net.ScoredNetwork}
+     *
+     * @throws IllegalArgumentException if an invalid badge enum is given
+     *
+     * @deprecated TODO(sghuman): Finalize the form of this method and then move it to a new
+     *         location.
+     */
+    public static LayerDrawable getBadgedWifiIcon(Context context, int level, int badge) {
+        return new LayerDrawable(
+                new Drawable[] {
+                        context.getDrawable(WIFI_PIE_FOR_BADGING[level]),
+                        context.getDrawable(getWifiBadgeResource(badge))
+                });
+    }
+
+    /**
+     * Returns the resource id for the given badge or {@link View.NO_ID} if no badge is to be shown.
+     *
+     * @throws IllegalArgumentException if the given badge value is not supported.
+     */
+    public static int getWifiBadgeResource(int badge) {
+        switch (badge) {
+            case ScoredNetwork.BADGING_NONE:
+                return View.NO_ID;
+            case ScoredNetwork.BADGING_SD:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_sd;
+            case ScoredNetwork.BADGING_HD:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_hd;
+            case ScoredNetwork.BADGING_4K:
+                return com.android.internal.R.drawable.ic_signal_wifi_badged_4k;
+            default:
+                throw new IllegalArgumentException(
+                    "No badge resource found for badge value: " + badge);
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index fcff305..9bb3c36 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -28,6 +28,8 @@
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.internal.R;
+
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -147,6 +149,26 @@
                 enabledServicesBuilder.toString(), userId);
     }
 
+    /**
+     * Get the name of the service that should be toggled by the accessibility shortcut. Use
+     * an OEM-configurable default if the setting has never been set.
+     *
+     * @param context A valid context
+     * @param userId The user whose settings should be checked
+     *
+     * @return The component name, flattened to a string, of the target service.
+     */
+    public static String getShortcutTargetServiceComponentNameString(
+            Context context, int userId) {
+        final String currentShortcutServiceId = Settings.Secure.getStringForUser(
+                context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                userId);
+        if (currentShortcutServiceId != null) {
+            return currentShortcutServiceId;
+        }
+        return context.getString(R.string.config_defaultAccessibilityService);
+    }
+
     private static Set<ComponentName> getInstalledServices(Context context) {
         final Set<ComponentName> installedServices = new HashSet<>();
         installedServices.clear();
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 2b1582d..24a3aa9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1429,6 +1429,23 @@
         }
     };
 
+    public static final AppFilter FILTER_GAMES = new AppFilter() {
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(ApplicationsState.AppEntry info) {
+            // TODO: Update for the new game category.
+            boolean isGame;
+            synchronized (info.info) {
+                isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
+                        || info.info.category == ApplicationInfo.CATEGORY_GAME;
+            }
+            return isGame;
+        }
+    };
+
     public static class VolumeFilter implements AppFilter {
         private final String mVolumeUuid;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
index d34dd89..d4623d6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
@@ -22,13 +22,22 @@
 
 public class InterestingConfigChanges {
     private final Configuration mLastConfiguration = new Configuration();
+    private final int mFlags;
     private int mLastDensity;
 
+    public InterestingConfigChanges() {
+        this(0);
+    }
+
+    public InterestingConfigChanges(int extraFlags) {
+        mFlags = extraFlags | ActivityInfo.CONFIG_LOCALE
+                | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT;
+    }
+
     public boolean applyNewConfig(Resources res) {
         int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
         boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
-        if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
-                |ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
+        if (densityChanged || (configChanges & (mFlags)) != 0) {
             mLastDensity = res.getDisplayMetrics().densityDpi;
             return true;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index b327be0..6e10aab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
@@ -105,6 +106,12 @@
     private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
 
     /**
+     * The key used to get the package name of the icon resource for the preference.
+     */
+    private static final String EXTRA_PREFERENCE_ICON_PACKAGE =
+        "com.android.settings.icon_package";
+
+    /**
      * Name of the meta-data item that should be set in the AndroidManifest.xml
      * to specify the key that should be used for the preference.
      */
@@ -342,6 +349,7 @@
             ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) {
         if (applicationInfo.isSystemApp()) {
             int icon = 0;
+            Pair<String, Integer> iconFromUri = null;
             CharSequence title = null;
             String summary = null;
             String keyHint = null;
@@ -358,10 +366,10 @@
 
                 if (res != null && metaData != null) {
                     if (metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
-                        icon = getIconFromUri(context,
+                        iconFromUri = getIconFromUri(context, activityInfo.packageName,
                                 metaData.getString(META_DATA_PREFERENCE_ICON_URI), providerMap);
                     }
-                    if ((icon == 0) && metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
+                    if (iconFromUri == null && metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
                         icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
                     }
                     if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
@@ -401,12 +409,18 @@
             if (TextUtils.isEmpty(title)) {
                 title = activityInfo.loadLabel(pm).toString();
             }
-            if (icon == 0) {
-                icon = activityInfo.icon;
+
+            // Set the icon
+            if (iconFromUri != null) {
+                tile.icon = Icon.createWithResource(iconFromUri.first, iconFromUri.second);
+            } else {
+                if (icon == 0) {
+                    icon = activityInfo.icon;
+                }
+                tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
             }
 
-            // Set icon, title and summary for the preference
-            tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
+            // Set title and summary for the preference
             tile.title = title;
             tile.summary = summary;
             // Replace the intent with this specific activity
@@ -422,16 +436,33 @@
     }
 
     /**
-     * Gets the icon resource id from content provider.
+     * Gets the icon package name and resource id from content provider.
      * @param Context context
+     * @param packageName package name of the target activity
      * @param uriString URI for the content provider
      * @param providerMap Maps URI authorities to providers
-     * @return Resource id if returned by the content provider, otherwise 0
+     * @return package name and resource id of the icon specified
      */
-    public static int getIconFromUri(Context context, String uriString,
-            Map<String, IContentProvider> providerMap) {
+    public static Pair<String, Integer> getIconFromUri(Context context, String packageName,
+            String uriString, Map<String, IContentProvider> providerMap) {
         Bundle bundle = getBundleFromUri(context, uriString, providerMap);
-        return (bundle != null) ? bundle.getInt(META_DATA_PREFERENCE_ICON, 0) : 0;
+        if (bundle == null) {
+            return null;
+        }
+        String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE);
+        if (TextUtils.isEmpty(iconPackageName)) {
+            return null;
+        }
+        int resId = bundle.getInt(META_DATA_PREFERENCE_ICON, 0);
+        if (resId == 0) {
+            return null;
+        }
+        // Icon can either come from the target package or from the Settings app.
+        if (iconPackageName.equals(packageName)
+                || iconPackageName.equals(context.getPackageName())) {
+            return Pair.create(iconPackageName, bundle.getInt(META_DATA_PREFERENCE_ICON, 0));
+        }
+        return null;
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index e7450fa..06ea445 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -27,12 +27,15 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
+import android.net.NetworkKey;
+import android.net.ScoredNetwork;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkScoreCache;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -48,6 +51,7 @@
 import com.android.settingslib.R;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -132,6 +136,9 @@
 
     private Object mTag;
 
+    private int mRankingScore = Integer.MIN_VALUE;
+    private int mBadge = ScoredNetwork.BADGING_NONE;
+
     // used to co-relate internal vs returned accesspoint.
     int mId;
 
@@ -205,6 +212,8 @@
         this.mScanResultCache.clear();
         this.mScanResultCache.putAll(that.mScanResultCache);
         this.mId = that.mId;
+        this.mBadge = that.mBadge;
+        this.mRankingScore = that.mRankingScore;
     }
 
     @Override
@@ -213,6 +222,11 @@
         if (isActive() && !other.isActive()) return -1;
         if (!isActive() && other.isActive()) return 1;
 
+        // Higher scores go before lower scores
+        if (mRankingScore != other.mRankingScore) {
+            return (mRankingScore > other.mRankingScore) ? -1 : 1;
+        }
+
         // Reachable one goes before unreachable one.
         if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1;
         if (mRssi == Integer.MAX_VALUE && other.mRssi != Integer.MAX_VALUE) return 1;
@@ -268,9 +282,38 @@
         if (security != SECURITY_NONE) {
             builder.append(',').append(securityToString(security, pskType));
         }
+        builder.append(",rankingScore=").append(mRankingScore);
+        builder.append(",badge=").append(mBadge);
+
         return builder.append(')').toString();
     }
 
+    /**
+     * Updates the AccessPoint rankingScore and badge, returning true if the data has changed.
+     *
+     * @param scoreCache The score cache to use to retrieve scores.
+     */
+    boolean updateScores(WifiNetworkScoreCache scoreCache) {
+        int oldBadge = mBadge;
+        int oldRankingScore = mRankingScore;
+        mBadge = ScoredNetwork.BADGING_NONE;
+        mRankingScore = Integer.MIN_VALUE;
+
+        for (ScanResult result : mScanResultCache.values()) {
+            ScoredNetwork score = scoreCache.getScoredNetwork(result);
+            if (score == null) {
+                continue;
+            }
+
+            if (score.hasRankingScore()) {
+                mRankingScore = Math.max(mRankingScore, score.calculateRankingScore(result.level));
+            }
+            mBadge = Math.max(mBadge, score.calculateBadge(result.level));
+        }
+
+        return (oldBadge != mBadge || oldRankingScore != mRankingScore);
+    }
+
     private void evictOldScanResults() {
         long nowMs = SystemClock.elapsedRealtime();
         for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
@@ -555,6 +598,8 @@
             visibility.append(" rssi=").append(mInfo.getRssi());
             visibility.append(" ");
             visibility.append(" score=").append(mInfo.score);
+            visibility.append(" rankingScore=").append(getRankingScore());
+            visibility.append(" badge=").append(getBadge());
             visibility.append(String.format(" tx=%.1f,", mInfo.txSuccessRate));
             visibility.append(String.format("%.1f,", mInfo.txRetriesRate));
             visibility.append(String.format("%.1f ", mInfo.txBadRate));
@@ -820,6 +865,14 @@
         mRssi = rssi;
     }
 
+    int getRankingScore() {
+        return mRankingScore;
+    }
+
+    int getBadge() {
+        return mBadge;
+    }
+
     public static String getSummary(Context context, String ssid, DetailedState state,
             boolean isEphemeral, String passpointProvider) {
         if (state == DetailedState.CONNECTED && ssid == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index aae9cf6..703bc17 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -18,8 +18,12 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.StateListDrawable;
+import android.net.ScoredNetwork;
 import android.net.wifi.WifiConfiguration;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -28,19 +32,29 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.SparseArray;
+import android.widget.ImageView;
 import android.widget.TextView;
+
 import com.android.settingslib.R;
+import com.android.settingslib.TronUtils;
+import com.android.settingslib.Utils;
 
 public class AccessPointPreference extends Preference {
 
     private static final int[] STATE_SECURED = {
             R.attr.state_encrypted
     };
+    private static final int[] STATE_SAVED = {
+            R.attr.state_encrypted,
+            R.attr.state_saved
+    };
     private static final int[] STATE_NONE = {};
 
-    private static int[] wifi_signal_attributes = { R.attr.wifi_signal };
+    private static final int[] wifi_signal_attributes = { R.attr.wifi_signal };
+    private static final int[] wifi_friction_attributes = { R.attr.wifi_friction };
 
     private final StateListDrawable mWifiSld;
+    private final StateListDrawable mFrictionSld;
     private final int mBadgePadding;
     private final UserBadgeCache mBadgeCache;
     private TextView mTitleView;
@@ -51,6 +65,9 @@
     private int mLevel;
     private CharSequence mContentDescription;
     private int mDefaultIconResId;
+    private int mIconWidth;
+    private int mIconHeight;
+    private int mWifiBadge = ScoredNetwork.BADGING_NONE;
 
     static final int[] WIFI_CONNECTION_STRENGTH = {
             R.string.accessibility_wifi_one_bar,
@@ -63,6 +80,7 @@
     public AccessPointPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         mWifiSld = null;
+        mFrictionSld = null;
         mBadgePadding = 0;
         mBadgeCache = null;
     }
@@ -70,6 +88,7 @@
     public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
             boolean forSavedNetworks) {
         super(context);
+        setWidgetLayoutResource(R.layout.access_point_friction_widget);
         mBadgeCache = cache;
         mAccessPoint = accessPoint;
         mForSavedNetworks = forSavedNetworks;
@@ -78,6 +97,17 @@
 
         mWifiSld = (StateListDrawable) context.getTheme()
                 .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
+        // Save icon width and height to use for creating a badged icon
+        setIconWidthAndHeight();
+
+        TypedArray frictionSld;
+        try {
+            frictionSld = context.getTheme().obtainStyledAttributes(wifi_friction_attributes);
+        } catch (Resources.NotFoundException e) {
+            // Fallback for platforms that do not need friction icon resources.
+            frictionSld = null;
+        }
+        mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
 
         // Distance from the end of the title at which this AP's user badge should sit.
         mBadgePadding = context.getResources()
@@ -88,6 +118,7 @@
     public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
             int iconResId, boolean forSavedNetworks) {
         super(context);
+        setWidgetLayoutResource(R.layout.access_point_friction_widget);
         mBadgeCache = cache;
         mAccessPoint = accessPoint;
         mForSavedNetworks = forSavedNetworks;
@@ -97,12 +128,30 @@
 
         mWifiSld = (StateListDrawable) context.getTheme()
                 .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
+        // Save icon width and height to use for creating a badged icon
+        setIconWidthAndHeight();
+
+        TypedArray frictionSld;
+        try {
+            frictionSld = context.getTheme().obtainStyledAttributes(wifi_friction_attributes);
+        } catch (Resources.NotFoundException e) {
+            // Fallback for platforms that do not need friction icon resources.
+            frictionSld = null;
+        }
+        mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
 
         // Distance from the end of the title at which this AP's user badge should sit.
         mBadgePadding = context.getResources()
                 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
     }
 
+    private void setIconWidthAndHeight() {
+        // TODO(sghuman): Refactor this defined widths and heights into a dimension resource and
+        // reference directly.
+        mIconWidth = mWifiSld.getIntrinsicWidth();
+        mIconHeight = mWifiSld.getIntrinsicHeight();
+    }
+
     public AccessPoint getAccessPoint() {
         return mAccessPoint;
     }
@@ -126,12 +175,28 @@
             mTitleView.setCompoundDrawablePadding(mBadgePadding);
         }
         view.itemView.setContentDescription(mContentDescription);
+
+        if (!mForSavedNetworks) {
+            ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
+            bindFrictionImage(frictionImageView);
+        }
     }
 
     protected void updateIcon(int level, Context context) {
         if (level == -1) {
             safeSetDefaultIcon();
         } else {
+            TronUtils.logWifiSettingsBadge(context, mWifiBadge);
+            if (mWifiBadge != ScoredNetwork.BADGING_NONE) {
+                // TODO(sghuman): Refactor this to reuse drawable to save memory and add to a
+                // special subclass of AccessPointPreference
+                LayerDrawable drawable = Utils.getBadgedWifiIcon(context, level, mWifiBadge);
+                drawable.setLayerSize(0, mIconWidth, mIconHeight);
+                drawable.setLayerSize(1, mIconWidth, mIconHeight);
+                drawable.setTint(Utils.getColorAccent(getContext()));
+                setIcon(drawable);
+                return;
+            }
             if (getIcon() == null) {
                 // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
                 // set the icon (drawable) to that state's drawable.
@@ -152,6 +217,27 @@
         }
     }
 
+    /**
+     * Binds the friction icon drawable using a StateListDrawable.
+     *
+     * <p>Friction icons will be rebound when notifyChange() is called, and therefore
+     * do not need to be managed in refresh()</p>.
+     */
+    private void bindFrictionImage(ImageView frictionImageView) {
+        if (frictionImageView == null || mFrictionSld == null) {
+            return;
+        }
+        if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
+            if (mAccessPoint.isSaved()) {
+                mFrictionSld.setState(STATE_SAVED);
+            } else {
+                mFrictionSld.setState(STATE_SECURED);
+            }
+        }
+        Drawable drawable = mFrictionSld.getCurrent();
+        frictionImageView.setImageDrawable(drawable);
+    }
+
     private void safeSetDefaultIcon() {
         if (mDefaultIconResId != 0) {
             setIcon(mDefaultIconResId);
@@ -182,11 +268,14 @@
 
         final Context context = getContext();
         int level = mAccessPoint.getLevel();
-        if (level != mLevel) {
+        int wifiBadge = mAccessPoint.getBadge();
+        if (level != mLevel || wifiBadge != mWifiBadge) {
             mLevel = level;
+            mWifiBadge = wifiBadge;
             updateIcon(mLevel, context);
             notifyChanged();
         }
+
         updateBadge(context);
 
         setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index fabae57..6f52dca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -12,13 +12,17 @@
 
 import android.content.Intent;
 import android.net.NetworkInfo;
+import android.net.NetworkKey;
+import android.net.WifiKey;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.util.Log;
 
 import java.util.List;
 
 public class WifiStatusTracker {
+    private static final String TAG = "WifiStatusTracker";
 
     private final WifiManager mWifiManager;
     public boolean enabled;
@@ -26,6 +30,7 @@
     public String ssid;
     public int rssi;
     public int level;
+    public NetworkKey networkKey;
 
     public WifiStatusTracker(WifiManager wifiManager) {
         mWifiManager = wifiManager;
@@ -40,19 +45,32 @@
             final NetworkInfo networkInfo = (NetworkInfo)
                     intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
             connected = networkInfo != null && networkInfo.isConnected();
+            WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
+                    ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
+                    : mWifiManager.getConnectionInfo();
+
             // If Connected grab the signal strength and ssid.
-            if (connected) {
-                // try getting it out of the intent first
-                WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
-                        ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
-                        : mWifiManager.getConnectionInfo();
-                if (info != null) {
-                    ssid = getSsid(info);
+            if (connected && info != null) {
+                ssid = getSsid(info);
+                String bssid = info.getBSSID();
+                if ((ssid != null) && (bssid != null)) {
+                    // Reuse existing network key object if possible.
+                    if ((networkKey == null)
+                            || !networkKey.wifiKey.ssid.equals(ssid)
+                            || !networkKey.wifiKey.bssid.equals(bssid)) {
+                        try {
+                            networkKey = new NetworkKey(
+                                    new WifiKey(ssid, bssid));
+                        } catch (IllegalArgumentException e) {
+                            Log.e(TAG, "Cannot create NetworkKey", e);
+                        }
+                    }
                 } else {
-                    ssid = null;
+                    networkKey = null;
                 }
-            } else if (!connected) {
+            } else {
                 ssid = null;
+                networkKey = null;
             }
         } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
             // Default to -200 as its below WifiManager.MIN_RSSI.
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 77a45b3..799f388 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -24,15 +24,22 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkKey;
 import android.net.NetworkRequest;
+import android.net.NetworkScoreManager;
+import android.net.ScoredNetwork;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.WifiNetworkScoreCache.CacheListener;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.support.annotation.WorkerThread;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -49,14 +56,18 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Tracks saved or available wifi networks and their state.
  */
 public class WifiTracker {
+    // TODO(sghuman): Document remaining methods with @UiThread and @WorkerThread where possible.
+    // TODO(sghuman): Refactor to avoid calling certain methods on the UiThread.
+
     private static final String TAG = "WifiTracker";
-    private static final boolean DBG = false;
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     /** verbose logging flag. this flag is set thru developer debugging options
      * and used so as to assist with in-the-field WiFi connectivity debugging  */
@@ -122,6 +133,10 @@
     private NetworkInfo mLastNetworkInfo;
     private WifiInfo mLastInfo;
 
+    private final NetworkScoreManager mNetworkScoreManager;
+    private final WifiNetworkScoreCache mScoreCache;
+    private final Set<NetworkKey> mRequestedScores = new ArraySet<>();
+
     @VisibleForTesting
     Scanner mScanner;
 
@@ -144,14 +159,16 @@
             boolean includeSaved, boolean includeScans, boolean includePasspoints) {
         this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints,
                 context.getSystemService(WifiManager.class),
-                context.getSystemService(ConnectivityManager.class), Looper.myLooper());
+                context.getSystemService(ConnectivityManager.class),
+                context.getSystemService(NetworkScoreManager.class), Looper.myLooper()
+        );
     }
 
     @VisibleForTesting
     WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
             boolean includeSaved, boolean includeScans, boolean includePasspoints,
             WifiManager wifiManager, ConnectivityManager connectivityManager,
-            Looper currentLooper) {
+            NetworkScoreManager networkScoreManager, Looper currentLooper) {
         if (!includeSaved && !includeScans) {
             throw new IllegalArgumentException("Must include either saved or scans");
         }
@@ -186,6 +203,18 @@
                 .clearCapabilities()
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                 .build();
+
+        mNetworkScoreManager = networkScoreManager;
+
+        mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(mWorkHandler) {
+            @Override
+            public void networkCacheUpdated(List<ScoredNetwork> networks) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "Score cache was updated with networks: " + networks);
+                }
+                Message.obtain(mWorkHandler, WorkHandler.MSG_UPDATE_NETWORK_SCORES).sendToTarget();
+            }
+        });
     }
 
     /**
@@ -216,6 +245,8 @@
 
     /**
      * Resume scanning for wifi networks after it has been paused.
+     *
+     * <p>The score cache should be registered before this method is invoked.
      */
     public void resumeScanning() {
         if (mScanner == null) {
@@ -230,11 +261,20 @@
     }
 
     /**
-     * Start tracking wifi networks.
-     * Registers listeners and starts scanning for wifi networks. If this is not called
+     * Start tracking wifi networks and scores.
+     *
+     * <p>Registers listeners and starts scanning for wifi networks. If this is not called
      * then forceUpdate() must be called to populate getAccessPoints().
      */
     public void startTracking() {
+        mWorkHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                registerScoreCache();
+            }
+        });
+
+
         resumeScanning();
         if (!mRegistered) {
             mContext.registerReceiver(mReceiver, mFilter);
@@ -245,9 +285,28 @@
         }
     }
 
+    @WorkerThread
+    private void registerScoreCache() {
+        mNetworkScoreManager.registerNetworkScoreCache(
+                NetworkKey.TYPE_WIFI,
+                mScoreCache,
+                NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS);
+    }
+
+    private void requestScoresForNetworkKeys(Collection<NetworkKey> keys) {
+        if (keys.isEmpty()) return;
+
+        if (DBG) {
+            Log.d(TAG, "Requesting scores for Network Keys: " + keys);
+        }
+        mNetworkScoreManager.requestScores(keys.toArray(new NetworkKey[keys.size()]));
+        mRequestedScores.addAll(keys);
+    }
+
     /**
-     * Stop tracking wifi networks.
-     * Unregisters all listeners and stops scanning for wifi networks. This should always
+     * Stop tracking wifi networks and scores.
+     *
+     * <p>Unregisters all listeners and stops scanning for wifi networks. This should always
      * be called when done with a WifiTracker (if startTracking was called) to ensure
      * proper cleanup.
      */
@@ -255,11 +314,26 @@
         if (mRegistered) {
             mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
             mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_NETWORK_INFO);
+            mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_NETWORK_SCORES);
             mContext.unregisterReceiver(mReceiver);
             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
             mRegistered = false;
         }
         pauseScanning();
+
+        mWorkHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                unregisterAndClearScoreCache();
+            }
+        });
+    }
+
+    @WorkerThread
+    private void unregisterAndClearScoreCache() {
+        mRequestedScores.clear();
+        mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache);
+        mScoreCache.clearScores();
     }
 
     /**
@@ -415,6 +489,7 @@
             }
         }
 
+        final List<NetworkKey> scoresToRequest = new ArrayList<>();
         if (results != null) {
             for (ScanResult result : results) {
                 // Ignore hidden and ad-hoc networks.
@@ -423,6 +498,11 @@
                     continue;
                 }
 
+                NetworkKey key = NetworkKey.createFromScanResult(result);
+                if (!mRequestedScores.contains(key)) {
+                    scoresToRequest.add(key);
+                }
+
                 boolean found = false;
                 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
                     if (accessPoint.update(result)) {
@@ -457,27 +537,35 @@
             }
         }
 
+
+        requestScoresForNetworkKeys(scoresToRequest);
+        for (AccessPoint ap : accessPoints) {
+            ap.updateScores(mScoreCache);
+        }
+
         // Pre-sort accessPoints to speed preference insertion
         Collections.sort(accessPoints);
 
         // Log accesspoints that were deleted
-        if (DBG) Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------");
-        for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
-            if (prevAccessPoint.getSsid() == null)
-                continue;
-            String prevSsid = prevAccessPoint.getSsidStr();
-            boolean found = false;
-            for (AccessPoint newAccessPoint : accessPoints) {
-                if (newAccessPoint.getSsid() != null && newAccessPoint.getSsid()
-                        .equals(prevSsid)) {
-                    found = true;
-                    break;
+        if (DBG) {
+            Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------");
+            for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
+                if (prevAccessPoint.getSsid() == null)
+                    continue;
+                String prevSsid = prevAccessPoint.getSsidStr();
+                boolean found = false;
+                for (AccessPoint newAccessPoint : accessPoints) {
+                    if (newAccessPoint.getSsid() != null && newAccessPoint.getSsid()
+                            .equals(prevSsid)) {
+                        found = true;
+                        break;
+                    }
                 }
+                if (!found)
+                    Log.d(TAG, "Did not find " + prevSsid + " in this scan");
             }
-            if (!found)
-                if (DBG) Log.d(TAG, "Did not find " + prevSsid + " in this scan");
+            Log.d(TAG, "---- Done dumping SSIDs that were not seen on this scan ----");
         }
-        if (DBG) Log.d(TAG, "---- Done dumping SSIDs that were not seen on this scan ----");
 
         mInternalAccessPoints.clear();
         mInternalAccessPoints.addAll(accessPoints);
@@ -549,7 +637,38 @@
 
         boolean reorder = false;
         for (int i = mInternalAccessPoints.size() - 1; i >= 0; --i) {
-            if (mInternalAccessPoints.get(i).update(connectionConfig, mLastInfo, mLastNetworkInfo)) {
+            AccessPoint ap = mInternalAccessPoints.get(i);
+            if (ap.update(connectionConfig, mLastInfo, mLastNetworkInfo)) {
+                reorder = true;
+            }
+            if (ap.updateScores(mScoreCache)) {
+                reorder = true;
+            }
+        }
+        if (reorder) {
+            Collections.sort(mInternalAccessPoints);
+            mMainHandler.scheduleAPCopyingAndCloseWriteLock();
+        }
+    }
+
+    /**
+     * Update all the internal access points rankingScores and badge.
+     *
+     * <p>Will trigger a resort and notify listeners of changes if applicable.
+     */
+    private void updateNetworkScores() {
+        // Lock required to prevent accidental copying of AccessPoint states while the modification
+        // is in progress. see #copyAndNotifyListeners
+        long before = System.currentTimeMillis();
+        mInternalAccessPointsWriteLock.block();
+        if (DBG) {
+            Log.d(TAG, "Acquired AP lock on WorkerHandler for inserting NetworkScores. Wait time = " +
+                    (System.currentTimeMillis() - before) + "ms.");
+        }
+
+        boolean reorder = false;
+        for (int i = 0; i < mInternalAccessPoints.size(); i++) {
+            if (mInternalAccessPoints.get(i).updateScores(mScoreCache)) {
                 reorder = true;
             }
         }
@@ -661,6 +780,7 @@
         private static final int MSG_UPDATE_NETWORK_INFO = 1;
         private static final int MSG_RESUME = 2;
         private static final int MSG_UPDATE_WIFI_STATE = 3;
+        private static final int MSG_UPDATE_NETWORK_SCORES = 4;
 
         public WorkHandler(Looper looper) {
             super(looper);
@@ -695,6 +815,9 @@
                     mMainHandler.obtainMessage(MainHandler.MSG_WIFI_STATE_CHANGED, msg.arg1, 0)
                             .sendToTarget();
                     break;
+                case MSG_UPDATE_NETWORK_SCORES:
+                    updateNetworkScores();
+                    break;
             }
         }
     }
diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk
index 4208522..bd910dd 100644
--- a/packages/SettingsLib/tests/integ/Android.mk
+++ b/packages/SettingsLib/tests/integ/Android.mk
@@ -27,7 +27,9 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     espresso-core \
-    mockito-target-minus-junit4
+    mockito-target-minus-junit4 \
+    legacy-android-test \
+    truth-prebuilt
 
 include frameworks/base/packages/SettingsLib/common.mk
 
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index 00b2164..0d5ff2c 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -22,6 +22,7 @@
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
     <uses-permission android:name="android.permission.SET_TIME_ZONE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
 
     <application>
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
new file mode 100644
index 0000000..4f2347d
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.pm.ApplicationInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ApplicationsStateTest {
+    private ApplicationsState.AppFilter mFilter;
+    private ApplicationsState.AppEntry mEntry;
+
+    @Before
+    public void setUp() {
+        mFilter = ApplicationsState.FILTER_GAMES;
+        mEntry = mock(ApplicationsState.AppEntry.class);
+        mEntry.info = mock(ApplicationInfo.class);
+    }
+
+    @Test
+    public void testGamesFilterAcceptsGameDeprecated() {
+        mEntry.info.flags = ApplicationInfo.FLAG_IS_GAME;
+
+        assertThat(mFilter.filterApp(mEntry)).isTrue();
+    }
+
+    @Test
+    public void testGameFilterAcceptsCategorizedGame() {
+        mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
+
+        assertThat(mFilter.filterApp(mEntry)).isTrue();
+    }
+
+    @Test
+    public void testGameFilterAcceptsCategorizedGameAndDeprecatedIsGame() {
+        mEntry.info.flags = ApplicationInfo.FLAG_IS_GAME;
+        mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
+
+        assertThat(mFilter.filterApp(mEntry)).isTrue();
+    }
+
+    @Test
+    public void testGamesFilterRejectsNotGame() {
+        mEntry.info.category = ApplicationInfo.CATEGORY_UNDEFINED;
+
+        assertThat(mFilter.filterApp(mEntry)).isFalse();
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index c650190..eaf0367 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -15,29 +15,278 @@
  */
 package com.android.settingslib.wifi;
 
-
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
+import android.net.ScoredNetwork;
+import android.net.RssiCurve;
+import android.net.WifiKey;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.WifiSsid;
+import android.os.Bundle;
 import android.os.SystemClock;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.mockito.ArgumentCaptor;
+import org.mockito.Matchers;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.List;
-
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class WifiTrackerTest {
 
+    private static final String TAG = "WifiTrackerTest";
+    private static final int LATCH_TIMEOUT = 2000;
+
+    private static final String SSID_1 = "ssid1";
+    private static final String BSSID_1 = "00:00:00:00:00:00";
+    private static final NetworkKey NETWORK_KEY_1 =
+            new NetworkKey(new WifiKey('"' + SSID_1 + '"', BSSID_1));
+    private static final int RSSI_1 = -30;
+    private static final byte SCORE_1 = 10;
+    private static final int BADGE_1 = ScoredNetwork.BADGING_SD;
+
+    private static final String SSID_2 = "ssid2";
+    private static final String BSSID_2 = "AA:AA:AA:AA:AA:AA";
+    private static final NetworkKey NETWORK_KEY_2 =
+            new NetworkKey(new WifiKey('"' + SSID_2 + '"', BSSID_2));
+    private static final int RSSI_2 = -30;
+    private static final byte SCORE_2 = 15;
+    private static final int BADGE_2 = ScoredNetwork.BADGING_HD;
+
+    @Captor ArgumentCaptor<WifiNetworkScoreCache> mScoreCacheCaptor;
+    @Mock private ConnectivityManager mockConnectivityManager;
+    @Mock private NetworkScoreManager mockNetworkScoreManager;
+    @Mock private RssiCurve mockCurve1;
+    @Mock private RssiCurve mockCurve2;
+    @Mock private RssiCurve mockBadgeCurve1;
+    @Mock private RssiCurve mockBadgeCurve2;
+    @Mock private WifiManager mockWifiManager;
+    @Mock private WifiTracker.WifiListener mockWifiListener;
+
+    private final List<NetworkKey> mRequestedKeys = new ArrayList<>();
+
+    private Context mContext;
+    private CountDownLatch mAccessPointsChangedLatch;
+    private CountDownLatch mRequestScoresLatch;
+    private Handler mScannerHandler;
+    private HandlerThread mMainThread;
+    private HandlerThread mWorkerThread;
+    private Looper mLooper;
+    private Looper mMainLooper;
+    private int mOriginalSettingValue;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        MockitoAnnotations.initMocks(this);
+
+        mWorkerThread = new HandlerThread("TestHandlerWorkerThread");
+        mWorkerThread.start();
+        mLooper = mWorkerThread.getLooper();
+        mMainThread = new HandlerThread("TestHandlerThread");
+        mMainThread.start();
+        mMainLooper = mMainThread.getLooper();
+
+        // Make sure the scanner doesn't try to run on the testing thread.
+        HandlerThread scannerThread = new HandlerThread("ScannerWorkerThread");
+        scannerThread.start();
+        mScannerHandler = new Handler(scannerThread.getLooper());
+
+        when(mockWifiManager.isWifiEnabled()).thenReturn(true);
+        when(mockWifiManager.getScanResults())
+                .thenReturn(Arrays.asList(buildScanResult1(), buildScanResult2()));
+
+
+        when(mockCurve1.lookupScore(RSSI_1)).thenReturn(SCORE_1);
+        when(mockCurve2.lookupScore(RSSI_2)).thenReturn(SCORE_2);
+
+        when(mockBadgeCurve1.lookupScore(RSSI_1)).thenReturn((byte) BADGE_1);
+        when(mockBadgeCurve2.lookupScore(RSSI_2)).thenReturn((byte) BADGE_2);
+
+        doNothing()
+                .when(mockNetworkScoreManager)
+                .registerNetworkScoreCache(
+                        anyInt(),
+                        mScoreCacheCaptor.capture(),
+                        Matchers.anyInt());
+
+        // Capture requested keys and count down latch if present
+        doAnswer(
+                new Answer<Boolean>() {
+                    @Override
+                    public Boolean answer(InvocationOnMock input) {
+                        if (mRequestScoresLatch != null) {
+                            mRequestScoresLatch.countDown();
+                        }
+                        NetworkKey[] keys = (NetworkKey[]) input.getArguments()[0];
+                        for (NetworkKey key : keys) {
+                            mRequestedKeys.add(key);
+                        }
+                        return true;
+                    }
+                }).when(mockNetworkScoreManager).requestScores(Matchers.<NetworkKey[]>any());
+
+        doAnswer(
+                new Answer<Void>() {
+                  @Override
+                  public Void answer (InvocationOnMock invocation) throws Throwable {
+                    if (mAccessPointsChangedLatch != null) {
+                      mAccessPointsChangedLatch.countDown();
+                    }
+
+                    return null;
+                  }
+                }).when(mockWifiListener).onAccessPointsChanged();
+
+        mOriginalSettingValue = Settings.Global.getInt(
+            InstrumentationRegistry.getTargetContext().getContentResolver(),
+            Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+            0 /* disabled */);
+
+    }
+
+    @After
+    public void cleanUp() {
+        Settings.Global.putInt(
+            InstrumentationRegistry.getTargetContext().getContentResolver(),
+            Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+            mOriginalSettingValue);
+    }
+
+    private static ScanResult buildScanResult1() {
+        return new ScanResult(
+                WifiSsid.createFromAsciiEncoded(SSID_1),
+                BSSID_1,
+                0, // hessid
+                0, //anqpDomainId
+                null, // osuProviders
+                "", // capabilities
+                RSSI_1,
+                0, // frequency
+                SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
+    }
+
+    private static ScanResult buildScanResult2() {
+        return new ScanResult(
+                WifiSsid.createFromAsciiEncoded(SSID_2),
+                BSSID_2,
+                0, // hessid
+                0, //anqpDomainId
+                null, // osuProviders
+                "", // capabilities
+                RSSI_2,
+                0, // frequency
+                SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
+    }
+
+    private WifiTracker createTrackerAndInjectInitialScanResults() throws InterruptedException {
+        WifiTracker tracker = createMockedWifiTracker();
+        startTracking(tracker);
+
+        mAccessPointsChangedLatch = new CountDownLatch(1);
+        sendScanResultsAndProcess(tracker);
+        mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+
+        return tracker;
+    }
+
+    private WifiTracker createMockedWifiTracker() {
+        WifiTracker tracker =
+                new WifiTracker(
+                    mContext,
+                    mockWifiListener,
+                    mLooper,
+                    true,
+                    true,
+                    true,
+                    mockWifiManager,
+                    mockConnectivityManager,
+                        mockNetworkScoreManager, mMainLooper
+                );
+
+        return tracker;
+    }
+
+    private void startTracking(WifiTracker tracker)  throws InterruptedException {
+        CountDownLatch latch = new CountDownLatch(1);
+        mScannerHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                tracker.startTracking();
+                latch.countDown();
+            }
+        });
+        latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+    }
+
+    private void sendScanResultsAndProcess(WifiTracker tracker) throws InterruptedException {
+        mAccessPointsChangedLatch = new CountDownLatch(1);
+        Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        tracker.mReceiver.onReceive(mContext, i);
+
+        mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+    }
+
+    private void updateScores() {
+        Bundle attr1 = new Bundle();
+        attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve1);
+        ScoredNetwork sc1 =
+                new ScoredNetwork(
+                        NETWORK_KEY_1,
+                        mockCurve1,
+                        false /* meteredHint */,
+                        attr1);
+
+        Bundle attr2 = new Bundle();
+        attr2.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve2);
+        ScoredNetwork sc2 =
+                new ScoredNetwork(
+                        NETWORK_KEY_2,
+                        mockCurve2,
+                        false /* meteredHint */,
+                        attr2);
+
+        WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue();
+        scoreCache.updateScores(Arrays.asList(sc1, sc2));
+    }
+
     @Test
     public void testAccessPointListenerSetWhenLookingUpUsingScanResults() {
         ScanResult scanResult = new ScanResult();
@@ -47,7 +296,7 @@
         scanResult.capabilities = "";
 
         WifiTracker tracker = new WifiTracker(
-                InstrumentationRegistry.getTargetContext(), null, true, true);
+                InstrumentationRegistry.getTargetContext(), null, mLooper, true, true);
 
         AccessPoint result = tracker.getCachedOrCreate(scanResult, new ArrayList<AccessPoint>());
         assertTrue(result.mAccessPointListener != null);
@@ -63,9 +312,125 @@
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
 
         WifiTracker tracker = new WifiTracker(
-                InstrumentationRegistry.getTargetContext(), null, true, true);
+                InstrumentationRegistry.getTargetContext(), null, mLooper, true, true);
 
         AccessPoint result = tracker.getCachedOrCreate(configuration, new ArrayList<AccessPoint>());
         assertTrue(result.mAccessPointListener != null);
     }
+
+    @Test
+    public void startAndStopTrackingShouldRegisterAndUnregisterScoreCache()
+            throws InterruptedException {
+        WifiTracker tracker = createMockedWifiTracker();
+
+        // Test register
+        startTracking(tracker);
+        verify(mockNetworkScoreManager)
+                .registerNetworkScoreCache(
+                          Matchers.anyInt(),
+                          mScoreCacheCaptor.capture(),
+                          Matchers.anyInt());
+
+        WifiNetworkScoreCache scoreCache = mScoreCacheCaptor.getValue();
+
+        // Test unregister
+        tracker.stopTracking();
+
+        verify(mockNetworkScoreManager)
+                .unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, scoreCache);
+    }
+
+    @Test
+    public void startTrackingShouldRequestScoresForCurrentAccessPoints() throws InterruptedException {
+        // Start the tracker and inject the initial scan results and then stop tracking
+        WifiTracker tracker =  createTrackerAndInjectInitialScanResults();
+
+        tracker.stopTracking();
+        mRequestedKeys.clear();
+
+        mRequestScoresLatch = new CountDownLatch(2);
+        startTracking(tracker);
+        mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+
+        assertTrue(mRequestedKeys.contains(NETWORK_KEY_1));
+        assertTrue(mRequestedKeys.contains(NETWORK_KEY_2));
+    }
+
+    @Test
+    public void scoreCacheUpdateScoresShouldTriggerOnAccessPointsChanged() throws InterruptedException {
+        WifiTracker tracker = createMockedWifiTracker();
+        startTracking(tracker);
+        sendScanResultsAndProcess(tracker);
+
+        updateScoresAndWaitForAccessPointsChangedCallback();
+    }
+
+    private void updateScoresAndWaitForAccessPointsChangedCallback() throws InterruptedException {
+        mAccessPointsChangedLatch = new CountDownLatch(2);
+        updateScores();
+        mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void scoreCacheUpdateScoresShouldChangeSortOrder() throws InterruptedException {
+        WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+        List<AccessPoint> aps = tracker.getAccessPoints();
+        assertTrue(aps.size() == 2);
+        assertEquals(aps.get(0).getSsidStr(), SSID_1);
+        assertEquals(aps.get(1).getSsidStr(), SSID_2);
+
+        updateScoresAndWaitForAccessPointsChangedCallback();
+
+        aps = tracker.getAccessPoints();
+        assertTrue(aps.size() == 2);
+        assertEquals(aps.get(0).getSsidStr(), SSID_2);
+        assertEquals(aps.get(1).getSsidStr(), SSID_1);
+
+    }
+
+    @Test
+    public void scoreCacheUpdateScoresShouldInsertBadgeIntoAccessPoint() throws InterruptedException {
+        WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+        updateScoresAndWaitForAccessPointsChangedCallback();
+
+        List<AccessPoint> aps = tracker.getAccessPoints();
+
+        for (AccessPoint ap : aps) {
+            if (ap.getSsidStr().equals(SSID_1)) {
+                assertEquals(BADGE_1, ap.getBadge());
+            } else if (ap.getSsidStr().equals(SSID_2)) {
+                assertEquals(BADGE_2, ap.getBadge());
+            }
+        }
+    }
+
+    @Test
+    public void scoresShouldBeRequestedForNewScanResultOnly()  throws InterruptedException {
+        mRequestScoresLatch = new CountDownLatch(2);
+        WifiTracker tracker = createTrackerAndInjectInitialScanResults();
+        mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+        mRequestedKeys.clear();
+
+        String ssid = "ssid3";
+        String bssid = "00:00:00:00:00:00";
+        ScanResult newResult = new ScanResult(
+                WifiSsid.createFromAsciiEncoded(ssid),
+                bssid,
+                0, // hessid
+                0, //anqpDomainId
+                null, // osuProviders
+                "", // capabilities
+                RSSI_1,
+                0, // frequency
+                SystemClock.elapsedRealtime() * 1000);
+        when(mockWifiManager.getScanResults())
+                .thenReturn(Arrays.asList(buildScanResult1(), buildScanResult2(), newResult));
+
+        mRequestScoresLatch = new CountDownLatch(1);
+        sendScanResultsAndProcess(tracker);
+        mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+
+        assertEquals(1, mRequestedKeys.size());
+        assertTrue(mRequestedKeys.contains(new NetworkKey(new WifiKey('"' + ssid + '"', bssid))));
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index d8082c4..c95cac5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -255,6 +255,7 @@
 
         Bundle bundle = new Bundle();
         bundle.putInt("com.android.settings.icon", 161803);
+        bundle.putString("com.android.settings.icon_package", "abc");
         bundle.putString("com.android.settings.summary", "dynamic-summary");
         when(mIContentProvider.call(anyString(),
                 eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_ICON))), eq(URI_GET_ICON), any()))
diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk
index 710214c..069e83a 100644
--- a/packages/SettingsProvider/Android.mk
+++ b/packages/SettingsProvider/Android.mk
@@ -7,6 +7,7 @@
     src/com/android/providers/settings/EventLogTags.logtags
 
 LOCAL_JAVA_LIBRARIES := telephony-common ims-common
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 LOCAL_PACKAGE_NAME := SettingsProvider
 LOCAL_CERTIFICATE := platform
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
new file mode 100644
index 0000000..19ce3d0
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -0,0 +1,1655 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.annotation.NonNull;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.providers.settings.GlobalSettingsProto;
+import android.providers.settings.SecureSettingsProto;
+import android.providers.settings.SettingProto;
+import android.providers.settings.SettingsServiceDumpProto;
+import android.providers.settings.SystemSettingsProto;
+import android.providers.settings.UserSettingsProto;
+import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
+
+/** @hide */
+class SettingsProtoDumpUtil {
+    private SettingsProtoDumpUtil() {}
+
+    static void dumpProtoLocked(SettingsProvider.SettingsRegistry settingsRegistry,
+            ProtoOutputStream proto) {
+        // Global settings
+        SettingsState globalSettings = settingsRegistry.getSettingsLocked(
+                SettingsProvider.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
+        long globalSettingsToken = proto.start(SettingsServiceDumpProto.GLOBAL_SETTINGS);
+        dumpProtoGlobalSettingsLocked(globalSettings, proto);
+        proto.end(globalSettingsToken);
+
+        // Per-user settings
+        SparseBooleanArray users = settingsRegistry.getKnownUsersLocked();
+        final int userCount = users.size();
+        for (int i = 0; i < userCount; i++) {
+            long userSettingsToken = proto.start(SettingsServiceDumpProto.USER_SETTINGS);
+            dumpProtoUserSettingsLocked(
+                    settingsRegistry, UserHandle.of(users.keyAt(i)), proto);
+            proto.end(userSettingsToken);
+        }
+    }
+
+    /**
+     * Dump all settings of a user as a proto buf.
+     *
+     * @param settingsRegistry
+     * @param user The user the settings should be dumped for
+     * @param proto The proto buf stream to dump to
+     */
+    private static void dumpProtoUserSettingsLocked(
+            SettingsProvider.SettingsRegistry settingsRegistry,
+            @NonNull UserHandle user,
+            @NonNull ProtoOutputStream proto) {
+        proto.write(UserSettingsProto.USER_ID, user.getIdentifier());
+
+        SettingsState secureSettings = settingsRegistry.getSettingsLocked(
+                SettingsProvider.SETTINGS_TYPE_SECURE, user.getIdentifier());
+        long secureSettingsToken = proto.start(UserSettingsProto.SECURE_SETTINGS);
+        dumpProtoSecureSettingsLocked(secureSettings, proto);
+        proto.end(secureSettingsToken);
+
+        SettingsState systemSettings = settingsRegistry.getSettingsLocked(
+                SettingsProvider.SETTINGS_TYPE_SYSTEM, user.getIdentifier());
+        long systemSettingsToken = proto.start(UserSettingsProto.SYSTEM_SETTINGS);
+        dumpProtoSystemSettingsLocked(systemSettings, proto);
+        proto.end(systemSettingsToken);
+    }
+
+    private static void dumpProtoGlobalSettingsLocked(
+            @NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+        dumpSetting(s, p,
+                Settings.Global.ADD_USERS_WHEN_LOCKED,
+                GlobalSettingsProto.ADD_USERS_WHEN_LOCKED);
+        dumpSetting(s, p,
+                Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
+                GlobalSettingsProto.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.AIRPLANE_MODE_ON,
+                GlobalSettingsProto.AIRPLANE_MODE_ON);
+        dumpSetting(s, p,
+                Settings.Global.THEATER_MODE_ON,
+                GlobalSettingsProto.THEATER_MODE_ON);
+        dumpSetting(s, p,
+                Settings.Global.RADIO_BLUETOOTH,
+                GlobalSettingsProto.RADIO_BLUETOOTH);
+        dumpSetting(s, p,
+                Settings.Global.RADIO_WIFI,
+                GlobalSettingsProto.RADIO_WIFI);
+        dumpSetting(s, p,
+                Settings.Global.RADIO_WIMAX,
+                GlobalSettingsProto.RADIO_WIMAX);
+        dumpSetting(s, p,
+                Settings.Global.RADIO_CELL,
+                GlobalSettingsProto.RADIO_CELL);
+        dumpSetting(s, p,
+                Settings.Global.RADIO_NFC,
+                GlobalSettingsProto.RADIO_NFC);
+        dumpSetting(s, p,
+                Settings.Global.AIRPLANE_MODE_RADIOS,
+                GlobalSettingsProto.AIRPLANE_MODE_RADIOS);
+        dumpSetting(s, p,
+                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
+                GlobalSettingsProto.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_DISABLED_PROFILES,
+                GlobalSettingsProto.BLUETOOTH_DISABLED_PROFILES);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_INTEROPERABILITY_LIST,
+                GlobalSettingsProto.BLUETOOTH_INTEROPERABILITY_LIST);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_SLEEP_POLICY,
+                GlobalSettingsProto.WIFI_SLEEP_POLICY);
+        dumpSetting(s, p,
+                Settings.Global.AUTO_TIME,
+                GlobalSettingsProto.AUTO_TIME);
+        dumpSetting(s, p,
+                Settings.Global.AUTO_TIME_ZONE,
+                GlobalSettingsProto.AUTO_TIME_ZONE);
+        dumpSetting(s, p,
+                Settings.Global.CAR_DOCK_SOUND,
+                GlobalSettingsProto.CAR_DOCK_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.CAR_UNDOCK_SOUND,
+                GlobalSettingsProto.CAR_UNDOCK_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.DESK_DOCK_SOUND,
+                GlobalSettingsProto.DESK_DOCK_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.DESK_UNDOCK_SOUND,
+                GlobalSettingsProto.DESK_UNDOCK_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.DOCK_SOUNDS_ENABLED,
+                GlobalSettingsProto.DOCK_SOUNDS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY,
+                GlobalSettingsProto.DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY);
+        dumpSetting(s, p,
+                Settings.Global.LOCK_SOUND,
+                GlobalSettingsProto.LOCK_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.UNLOCK_SOUND,
+                GlobalSettingsProto.UNLOCK_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.TRUSTED_SOUND,
+                GlobalSettingsProto.TRUSTED_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.LOW_BATTERY_SOUND,
+                GlobalSettingsProto.LOW_BATTERY_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.POWER_SOUNDS_ENABLED,
+                GlobalSettingsProto.POWER_SOUNDS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.WIRELESS_CHARGING_STARTED_SOUND,
+                GlobalSettingsProto.WIRELESS_CHARGING_STARTED_SOUND);
+        dumpSetting(s, p,
+                Settings.Global.CHARGING_SOUNDS_ENABLED,
+                GlobalSettingsProto.CHARGING_SOUNDS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
+                GlobalSettingsProto.STAY_ON_WHILE_PLUGGED_IN);
+        dumpSetting(s, p,
+                Settings.Global.BUGREPORT_IN_POWER_MENU,
+                GlobalSettingsProto.BUGREPORT_IN_POWER_MENU);
+        dumpSetting(s, p,
+                Settings.Global.ADB_ENABLED,
+                GlobalSettingsProto.ADB_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.DEBUG_VIEW_ATTRIBUTES,
+                GlobalSettingsProto.DEBUG_VIEW_ATTRIBUTES);
+        dumpSetting(s, p,
+                Settings.Global.ASSISTED_GPS_ENABLED,
+                GlobalSettingsProto.ASSISTED_GPS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_ON,
+                GlobalSettingsProto.BLUETOOTH_ON);
+        dumpSetting(s, p,
+                Settings.Global.CDMA_CELL_BROADCAST_SMS,
+                GlobalSettingsProto.CDMA_CELL_BROADCAST_SMS);
+        dumpSetting(s, p,
+                Settings.Global.CDMA_ROAMING_MODE,
+                GlobalSettingsProto.CDMA_ROAMING_MODE);
+        dumpSetting(s, p,
+                Settings.Global.CDMA_SUBSCRIPTION_MODE,
+                GlobalSettingsProto.CDMA_SUBSCRIPTION_MODE);
+        dumpSetting(s, p,
+                Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
+                GlobalSettingsProto.DATA_ACTIVITY_TIMEOUT_MOBILE);
+        dumpSetting(s, p,
+                Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
+                GlobalSettingsProto.DATA_ACTIVITY_TIMEOUT_WIFI);
+        dumpSetting(s, p,
+                Settings.Global.DATA_ROAMING,
+                GlobalSettingsProto.DATA_ROAMING);
+        dumpSetting(s, p,
+                Settings.Global.MDC_INITIAL_MAX_RETRY,
+                GlobalSettingsProto.MDC_INITIAL_MAX_RETRY);
+        dumpSetting(s, p,
+                Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
+                GlobalSettingsProto.FORCE_ALLOW_ON_EXTERNAL);
+        dumpSetting(s, p,
+                Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
+                GlobalSettingsProto.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
+        dumpSetting(s, p,
+                Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
+                GlobalSettingsProto.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT);
+        dumpSetting(s, p,
+                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
+                GlobalSettingsProto.DEVELOPMENT_SETTINGS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.DEVICE_PROVISIONED,
+                GlobalSettingsProto.DEVICE_PROVISIONED);
+        dumpSetting(s, p,
+                Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
+                GlobalSettingsProto.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.DISPLAY_SIZE_FORCED,
+                GlobalSettingsProto.DISPLAY_SIZE_FORCED);
+        dumpSetting(s, p,
+                Settings.Global.DISPLAY_SCALING_FORCE,
+                GlobalSettingsProto.DISPLAY_SCALING_FORCE);
+        dumpSetting(s, p,
+                Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE,
+                GlobalSettingsProto.DOWNLOAD_MAX_BYTES_OVER_MOBILE);
+        dumpSetting(s, p,
+                Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
+                GlobalSettingsProto.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
+        dumpSetting(s, p,
+                Settings.Global.HDMI_CONTROL_ENABLED,
+                GlobalSettingsProto.HDMI_CONTROL_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.HDMI_SYSTEM_AUDIO_ENABLED,
+                GlobalSettingsProto.HDMI_SYSTEM_AUDIO_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
+                GlobalSettingsProto.HDMI_CONTROL_AUTO_WAKEUP_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
+                GlobalSettingsProto.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
+                GlobalSettingsProto.MHL_INPUT_SWITCHING_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.MHL_POWER_CHARGE_ENABLED,
+                GlobalSettingsProto.MHL_POWER_CHARGE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.MOBILE_DATA,
+                GlobalSettingsProto.MOBILE_DATA);
+        dumpSetting(s, p,
+                Settings.Global.MOBILE_DATA_ALWAYS_ON,
+                GlobalSettingsProto.MOBILE_DATA_ALWAYS_ON);
+        dumpSetting(s, p,
+                Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE,
+                GlobalSettingsProto.CONNECTIVITY_METRICS_BUFFER_SIZE);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_ENABLED,
+                GlobalSettingsProto.NETSTATS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_POLL_INTERVAL,
+                GlobalSettingsProto.NETSTATS_POLL_INTERVAL);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_TIME_CACHE_MAX_AGE,
+                GlobalSettingsProto.NETSTATS_TIME_CACHE_MAX_AGE);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES,
+                GlobalSettingsProto.NETSTATS_GLOBAL_ALERT_BYTES);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_SAMPLE_ENABLED,
+                GlobalSettingsProto.NETSTATS_SAMPLE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_DEV_BUCKET_DURATION,
+                GlobalSettingsProto.NETSTATS_DEV_BUCKET_DURATION);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_DEV_PERSIST_BYTES,
+                GlobalSettingsProto.NETSTATS_DEV_PERSIST_BYTES);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_DEV_ROTATE_AGE,
+                GlobalSettingsProto.NETSTATS_DEV_ROTATE_AGE);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_DEV_DELETE_AGE,
+                GlobalSettingsProto.NETSTATS_DEV_DELETE_AGE);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_UID_BUCKET_DURATION,
+                GlobalSettingsProto.NETSTATS_UID_BUCKET_DURATION);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_UID_PERSIST_BYTES,
+                GlobalSettingsProto.NETSTATS_UID_PERSIST_BYTES);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_UID_ROTATE_AGE,
+                GlobalSettingsProto.NETSTATS_UID_ROTATE_AGE);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_UID_DELETE_AGE,
+                GlobalSettingsProto.NETSTATS_UID_DELETE_AGE);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION,
+                GlobalSettingsProto.NETSTATS_UID_TAG_BUCKET_DURATION);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES,
+                GlobalSettingsProto.NETSTATS_UID_TAG_PERSIST_BYTES);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE,
+                GlobalSettingsProto.NETSTATS_UID_TAG_ROTATE_AGE);
+        dumpSetting(s, p,
+                Settings.Global.NETSTATS_UID_TAG_DELETE_AGE,
+                GlobalSettingsProto.NETSTATS_UID_TAG_DELETE_AGE);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_PREFERENCE,
+                GlobalSettingsProto.NETWORK_PREFERENCE);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_SCORER_APP,
+                GlobalSettingsProto.NETWORK_SCORER_APP);
+        dumpSetting(s, p,
+                Settings.Global.NITZ_UPDATE_DIFF,
+                GlobalSettingsProto.NITZ_UPDATE_DIFF);
+        dumpSetting(s, p,
+                Settings.Global.NITZ_UPDATE_SPACING,
+                GlobalSettingsProto.NITZ_UPDATE_SPACING);
+        dumpSetting(s, p,
+                Settings.Global.NTP_SERVER,
+                GlobalSettingsProto.NTP_SERVER);
+        dumpSetting(s, p,
+                Settings.Global.NTP_TIMEOUT,
+                GlobalSettingsProto.NTP_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.Global.STORAGE_BENCHMARK_INTERVAL,
+                GlobalSettingsProto.STORAGE_BENCHMARK_INTERVAL);
+        dumpSetting(s, p,
+                Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
+                GlobalSettingsProto.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS);
+        dumpSetting(s, p,
+                Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
+                GlobalSettingsProto.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT);
+        dumpSetting(s, p,
+                Settings.Global.DNS_RESOLVER_MIN_SAMPLES,
+                GlobalSettingsProto.DNS_RESOLVER_MIN_SAMPLES);
+        dumpSetting(s, p,
+                Settings.Global.DNS_RESOLVER_MAX_SAMPLES,
+                GlobalSettingsProto.DNS_RESOLVER_MAX_SAMPLES);
+        dumpSetting(s, p,
+                Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE,
+                GlobalSettingsProto.OTA_DISABLE_AUTOMATIC_UPDATE);
+        dumpSetting(s, p,
+                Settings.Global.PACKAGE_VERIFIER_ENABLE,
+                GlobalSettingsProto.PACKAGE_VERIFIER_ENABLE);
+        dumpSetting(s, p,
+                Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
+                GlobalSettingsProto.PACKAGE_VERIFIER_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
+                GlobalSettingsProto.PACKAGE_VERIFIER_DEFAULT_RESPONSE);
+        dumpSetting(s, p,
+                Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE,
+                GlobalSettingsProto.PACKAGE_VERIFIER_SETTING_VISIBLE);
+        dumpSetting(s, p,
+                Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
+                GlobalSettingsProto.PACKAGE_VERIFIER_INCLUDE_ADB);
+        dumpSetting(s, p,
+                Settings.Global.FSTRIM_MANDATORY_INTERVAL,
+                GlobalSettingsProto.FSTRIM_MANDATORY_INTERVAL);
+        dumpSetting(s, p,
+                Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS,
+                GlobalSettingsProto.PDP_WATCHDOG_POLL_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
+                GlobalSettingsProto.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
+                GlobalSettingsProto.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
+                GlobalSettingsProto.PDP_WATCHDOG_TRIGGER_PACKET_COUNT);
+        dumpSetting(s, p,
+                Settings.Global.PDP_WATCHDOG_ERROR_POLL_COUNT,
+                GlobalSettingsProto.PDP_WATCHDOG_ERROR_POLL_COUNT);
+        dumpSetting(s, p,
+                Settings.Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
+                GlobalSettingsProto.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT);
+        dumpSetting(s, p,
+                Settings.Global.SAMPLING_PROFILER_MS,
+                GlobalSettingsProto.SAMPLING_PROFILER_MS);
+        dumpSetting(s, p,
+                Settings.Global.SETUP_PREPAID_DATA_SERVICE_URL,
+                GlobalSettingsProto.SETUP_PREPAID_DATA_SERVICE_URL);
+        dumpSetting(s, p,
+                Settings.Global.SETUP_PREPAID_DETECTION_TARGET_URL,
+                GlobalSettingsProto.SETUP_PREPAID_DETECTION_TARGET_URL);
+        dumpSetting(s, p,
+                Settings.Global.SETUP_PREPAID_DETECTION_REDIR_HOST,
+                GlobalSettingsProto.SETUP_PREPAID_DETECTION_REDIR_HOST);
+        dumpSetting(s, p,
+                Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
+                GlobalSettingsProto.SMS_OUTGOING_CHECK_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
+                GlobalSettingsProto.SMS_OUTGOING_CHECK_MAX_COUNT);
+        dumpSetting(s, p,
+                Settings.Global.SMS_SHORT_CODE_CONFIRMATION,
+                GlobalSettingsProto.SMS_SHORT_CODE_CONFIRMATION);
+        dumpSetting(s, p,
+                Settings.Global.SMS_SHORT_CODE_RULE,
+                GlobalSettingsProto.SMS_SHORT_CODE_RULE);
+        dumpSetting(s, p,
+                Settings.Global.TCP_DEFAULT_INIT_RWND,
+                GlobalSettingsProto.TCP_DEFAULT_INIT_RWND);
+        dumpSetting(s, p,
+                Settings.Global.TETHER_SUPPORTED,
+                GlobalSettingsProto.TETHER_SUPPORTED);
+        dumpSetting(s, p,
+                Settings.Global.TETHER_DUN_REQUIRED,
+                GlobalSettingsProto.TETHER_DUN_REQUIRED);
+        dumpSetting(s, p,
+                Settings.Global.TETHER_DUN_APN,
+                GlobalSettingsProto.TETHER_DUN_APN);
+        dumpSetting(s, p,
+                Settings.Global.CARRIER_APP_WHITELIST,
+                GlobalSettingsProto.CARRIER_APP_WHITELIST);
+        dumpSetting(s, p,
+                Settings.Global.USB_MASS_STORAGE_ENABLED,
+                GlobalSettingsProto.USB_MASS_STORAGE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.USE_GOOGLE_MAIL,
+                GlobalSettingsProto.USE_GOOGLE_MAIL);
+        dumpSetting(s, p,
+                Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
+                GlobalSettingsProto.WEBVIEW_DATA_REDUCTION_PROXY_KEY);
+        dumpSetting(s, p,
+                Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
+                GlobalSettingsProto.WEBVIEW_FALLBACK_LOGIC_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.WEBVIEW_PROVIDER,
+                GlobalSettingsProto.WEBVIEW_PROVIDER);
+        dumpSetting(s, p,
+                Settings.Global.WEBVIEW_MULTIPROCESS,
+                GlobalSettingsProto.WEBVIEW_MULTIPROCESS);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT,
+                GlobalSettingsProto.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS,
+                GlobalSettingsProto.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_AVOID_BAD_WIFI,
+                GlobalSettingsProto.NETWORK_AVOID_BAD_WIFI);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_DISPLAY_ON,
+                GlobalSettingsProto.WIFI_DISPLAY_ON);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON,
+                GlobalSettingsProto.WIFI_DISPLAY_CERTIFICATION_ON);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_DISPLAY_WPS_CONFIG,
+                GlobalSettingsProto.WIFI_DISPLAY_WPS_CONFIG);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+                GlobalSettingsProto.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+        dumpSetting(s, p,
+                Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+                GlobalSettingsProto.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
+                GlobalSettingsProto.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_COUNTRY_CODE,
+                GlobalSettingsProto.WIFI_COUNTRY_CODE);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
+                GlobalSettingsProto.WIFI_FRAMEWORK_SCAN_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_IDLE_MS,
+                GlobalSettingsProto.WIFI_IDLE_MS);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_NUM_OPEN_NETWORKS_KEPT,
+                GlobalSettingsProto.WIFI_NUM_OPEN_NETWORKS_KEPT);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_ON,
+                GlobalSettingsProto.WIFI_ON);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
+                GlobalSettingsProto.WIFI_SCAN_ALWAYS_AVAILABLE);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_WAKEUP_ENABLED,
+                GlobalSettingsProto.WIFI_WAKEUP_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+                GlobalSettingsProto.NETWORK_RECOMMENDATIONS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
+                GlobalSettingsProto.BLE_SCAN_ALWAYS_AVAILABLE);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_SAVED_STATE,
+                GlobalSettingsProto.WIFI_SAVED_STATE);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
+                GlobalSettingsProto.WIFI_SUPPLICANT_SCAN_INTERVAL_MS);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_ENHANCED_AUTO_JOIN,
+                GlobalSettingsProto.WIFI_ENHANCED_AUTO_JOIN);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_NETWORK_SHOW_RSSI,
+                GlobalSettingsProto.WIFI_NETWORK_SHOW_RSSI);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
+                GlobalSettingsProto.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_WATCHDOG_ON,
+                GlobalSettingsProto.WIFI_WATCHDOG_ON);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
+                GlobalSettingsProto.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED,
+                GlobalSettingsProto.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED,
+                GlobalSettingsProto.WIFI_VERBOSE_LOGGING_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
+                GlobalSettingsProto.WIFI_MAX_DHCP_RETRY_COUNT);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
+                GlobalSettingsProto.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
+                GlobalSettingsProto.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_FREQUENCY_BAND,
+                GlobalSettingsProto.WIFI_FREQUENCY_BAND);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_P2P_DEVICE_NAME,
+                GlobalSettingsProto.WIFI_P2P_DEVICE_NAME);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_REENABLE_DELAY_MS,
+                GlobalSettingsProto.WIFI_REENABLE_DELAY_MS);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS,
+                GlobalSettingsProto.WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS);
+        dumpSetting(s, p,
+                Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
+                GlobalSettingsProto.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS);
+        dumpSetting(s, p,
+                Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
+                GlobalSettingsProto.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS);
+        dumpSetting(s, p,
+                Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
+                GlobalSettingsProto.PROVISIONING_APN_ALARM_DELAY_IN_MS);
+        dumpSetting(s, p,
+                Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
+                GlobalSettingsProto.GPRS_REGISTER_CHECK_PERIOD_MS);
+        dumpSetting(s, p,
+                Settings.Global.WTF_IS_FATAL,
+                GlobalSettingsProto.WTF_IS_FATAL);
+        dumpSetting(s, p,
+                Settings.Global.MODE_RINGER,
+                GlobalSettingsProto.MODE_RINGER);
+        dumpSetting(s, p,
+                Settings.Global.OVERLAY_DISPLAY_DEVICES,
+                GlobalSettingsProto.OVERLAY_DISPLAY_DEVICES);
+        dumpSetting(s, p,
+                Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
+                GlobalSettingsProto.BATTERY_DISCHARGE_DURATION_THRESHOLD);
+        dumpSetting(s, p,
+                Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
+                GlobalSettingsProto.BATTERY_DISCHARGE_THRESHOLD);
+        dumpSetting(s, p,
+                Settings.Global.SEND_ACTION_APP_ERROR,
+                GlobalSettingsProto.SEND_ACTION_APP_ERROR);
+        dumpSetting(s, p,
+                Settings.Global.DROPBOX_AGE_SECONDS,
+                GlobalSettingsProto.DROPBOX_AGE_SECONDS);
+        dumpSetting(s, p,
+                Settings.Global.DROPBOX_MAX_FILES,
+                GlobalSettingsProto.DROPBOX_MAX_FILES);
+        dumpSetting(s, p,
+                Settings.Global.DROPBOX_QUOTA_KB,
+                GlobalSettingsProto.DROPBOX_QUOTA_KB);
+        dumpSetting(s, p,
+                Settings.Global.DROPBOX_QUOTA_PERCENT,
+                GlobalSettingsProto.DROPBOX_QUOTA_PERCENT);
+        dumpSetting(s, p,
+                Settings.Global.DROPBOX_RESERVE_PERCENT,
+                GlobalSettingsProto.DROPBOX_RESERVE_PERCENT);
+        dumpSetting(s, p,
+                Settings.Global.DROPBOX_TAG_PREFIX,
+                GlobalSettingsProto.DROPBOX_TAG_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.ERROR_LOGCAT_PREFIX,
+                GlobalSettingsProto.ERROR_LOGCAT_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
+                GlobalSettingsProto.SYS_FREE_STORAGE_LOG_INTERVAL);
+        dumpSetting(s, p,
+                Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
+                GlobalSettingsProto.DISK_FREE_CHANGE_REPORTING_THRESHOLD);
+        dumpSetting(s, p,
+                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
+                GlobalSettingsProto.SYS_STORAGE_THRESHOLD_PERCENTAGE);
+        dumpSetting(s, p,
+                Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
+                GlobalSettingsProto.SYS_STORAGE_THRESHOLD_MAX_BYTES);
+        dumpSetting(s, p,
+                Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
+                GlobalSettingsProto.SYS_STORAGE_FULL_THRESHOLD_BYTES);
+        dumpSetting(s, p,
+                Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
+                GlobalSettingsProto.SYNC_MAX_RETRY_DELAY_IN_SECONDS);
+        dumpSetting(s, p,
+                Settings.Global.CONNECTIVITY_CHANGE_DELAY,
+                GlobalSettingsProto.CONNECTIVITY_CHANGE_DELAY);
+        dumpSetting(s, p,
+                Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
+                GlobalSettingsProto.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS);
+        dumpSetting(s, p,
+                Settings.Global.PAC_CHANGE_DELAY,
+                GlobalSettingsProto.PAC_CHANGE_DELAY);
+        dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_MODE,
+                GlobalSettingsProto.CAPTIVE_PORTAL_MODE);
+        dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_SERVER,
+                GlobalSettingsProto.CAPTIVE_PORTAL_SERVER);
+        dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_HTTPS_URL,
+                GlobalSettingsProto.CAPTIVE_PORTAL_HTTPS_URL);
+        dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
+                GlobalSettingsProto.CAPTIVE_PORTAL_HTTP_URL);
+        dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL,
+                GlobalSettingsProto.CAPTIVE_PORTAL_FALLBACK_URL);
+        dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_USE_HTTPS,
+                GlobalSettingsProto.CAPTIVE_PORTAL_USE_HTTPS);
+        dumpSetting(s, p,
+                Settings.Global.CAPTIVE_PORTAL_USER_AGENT,
+                GlobalSettingsProto.CAPTIVE_PORTAL_USER_AGENT);
+        dumpSetting(s, p,
+                Settings.Global.NSD_ON,
+                GlobalSettingsProto.NSD_ON);
+        dumpSetting(s, p,
+                Settings.Global.SET_INSTALL_LOCATION,
+                GlobalSettingsProto.SET_INSTALL_LOCATION);
+        dumpSetting(s, p,
+                Settings.Global.DEFAULT_INSTALL_LOCATION,
+                GlobalSettingsProto.DEFAULT_INSTALL_LOCATION);
+        dumpSetting(s, p,
+                Settings.Global.INET_CONDITION_DEBOUNCE_UP_DELAY,
+                GlobalSettingsProto.INET_CONDITION_DEBOUNCE_UP_DELAY);
+        dumpSetting(s, p,
+                Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY,
+                GlobalSettingsProto.INET_CONDITION_DEBOUNCE_DOWN_DELAY);
+        dumpSetting(s, p,
+                Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT,
+                GlobalSettingsProto.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT);
+        dumpSetting(s, p,
+                Settings.Global.HTTP_PROXY,
+                GlobalSettingsProto.HTTP_PROXY);
+        dumpSetting(s, p,
+                Settings.Global.GLOBAL_HTTP_PROXY_HOST,
+                GlobalSettingsProto.GLOBAL_HTTP_PROXY_HOST);
+        dumpSetting(s, p,
+                Settings.Global.GLOBAL_HTTP_PROXY_PORT,
+                GlobalSettingsProto.GLOBAL_HTTP_PROXY_PORT);
+        dumpSetting(s, p,
+                Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+                GlobalSettingsProto.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+        dumpSetting(s, p,
+                Settings.Global.GLOBAL_HTTP_PROXY_PAC,
+                GlobalSettingsProto.GLOBAL_HTTP_PROXY_PAC);
+        dumpSetting(s, p,
+                Settings.Global.SET_GLOBAL_HTTP_PROXY,
+                GlobalSettingsProto.SET_GLOBAL_HTTP_PROXY);
+        dumpSetting(s, p,
+                Settings.Global.DEFAULT_DNS_SERVER,
+                GlobalSettingsProto.DEFAULT_DNS_SERVER);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_HEADSET_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_HEADSET_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_MAP_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_MAP_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_SAP_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_SAP_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX,
+                GlobalSettingsProto.BLUETOOTH_PAN_PRIORITY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Global.DEVICE_IDLE_CONSTANTS,
+                GlobalSettingsProto.DEVICE_IDLE_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH,
+                GlobalSettingsProto.DEVICE_IDLE_CONSTANTS_WATCH);
+        dumpSetting(s, p,
+                Settings.Global.APP_IDLE_CONSTANTS,
+                GlobalSettingsProto.APP_IDLE_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.ALARM_MANAGER_CONSTANTS,
+                GlobalSettingsProto.ALARM_MANAGER_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.JOB_SCHEDULER_CONSTANTS,
+                GlobalSettingsProto.JOB_SCHEDULER_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
+                GlobalSettingsProto.SHORTCUT_MANAGER_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.WINDOW_ANIMATION_SCALE,
+                GlobalSettingsProto.WINDOW_ANIMATION_SCALE);
+        dumpSetting(s, p,
+                Settings.Global.TRANSITION_ANIMATION_SCALE,
+                GlobalSettingsProto.TRANSITION_ANIMATION_SCALE);
+        dumpSetting(s, p,
+                Settings.Global.ANIMATOR_DURATION_SCALE,
+                GlobalSettingsProto.ANIMATOR_DURATION_SCALE);
+        dumpSetting(s, p,
+                Settings.Global.FANCY_IME_ANIMATIONS,
+                GlobalSettingsProto.FANCY_IME_ANIMATIONS);
+        dumpSetting(s, p,
+                Settings.Global.COMPATIBILITY_MODE,
+                GlobalSettingsProto.COMPATIBILITY_MODE);
+        dumpSetting(s, p,
+                Settings.Global.EMERGENCY_TONE,
+                GlobalSettingsProto.EMERGENCY_TONE);
+        dumpSetting(s, p,
+                Settings.Global.CALL_AUTO_RETRY,
+                GlobalSettingsProto.CALL_AUTO_RETRY);
+        dumpSetting(s, p,
+                Settings.Global.EMERGENCY_AFFORDANCE_NEEDED,
+                GlobalSettingsProto.EMERGENCY_AFFORDANCE_NEEDED);
+        dumpSetting(s, p,
+                Settings.Global.PREFERRED_NETWORK_MODE,
+                GlobalSettingsProto.PREFERRED_NETWORK_MODE);
+        dumpSetting(s, p,
+                Settings.Global.DEBUG_APP,
+                GlobalSettingsProto.DEBUG_APP);
+        dumpSetting(s, p,
+                Settings.Global.WAIT_FOR_DEBUGGER,
+                GlobalSettingsProto.WAIT_FOR_DEBUGGER);
+        dumpSetting(s, p,
+                Settings.Global.LOW_POWER_MODE,
+                GlobalSettingsProto.LOW_POWER_MODE);
+        dumpSetting(s, p,
+                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL,
+                GlobalSettingsProto.LOW_POWER_MODE_TRIGGER_LEVEL);
+        dumpSetting(s, p,
+                Settings.Global.ALWAYS_FINISH_ACTIVITIES,
+                GlobalSettingsProto.ALWAYS_FINISH_ACTIVITIES);
+        dumpSetting(s, p,
+                Settings.Global.DOCK_AUDIO_MEDIA_ENABLED,
+                GlobalSettingsProto.DOCK_AUDIO_MEDIA_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.ENCODED_SURROUND_OUTPUT,
+                GlobalSettingsProto.ENCODED_SURROUND_OUTPUT);
+        dumpSetting(s, p,
+                Settings.Global.AUDIO_SAFE_VOLUME_STATE,
+                GlobalSettingsProto.AUDIO_SAFE_VOLUME_STATE);
+        dumpSetting(s, p,
+                Settings.Global.TZINFO_UPDATE_CONTENT_URL,
+                GlobalSettingsProto.TZINFO_UPDATE_CONTENT_URL);
+        dumpSetting(s, p,
+                Settings.Global.TZINFO_UPDATE_METADATA_URL,
+                GlobalSettingsProto.TZINFO_UPDATE_METADATA_URL);
+        dumpSetting(s, p,
+                Settings.Global.SELINUX_UPDATE_CONTENT_URL,
+                GlobalSettingsProto.SELINUX_UPDATE_CONTENT_URL);
+        dumpSetting(s, p,
+                Settings.Global.SELINUX_UPDATE_METADATA_URL,
+                GlobalSettingsProto.SELINUX_UPDATE_METADATA_URL);
+        dumpSetting(s, p,
+                Settings.Global.SMS_SHORT_CODES_UPDATE_CONTENT_URL,
+                GlobalSettingsProto.SMS_SHORT_CODES_UPDATE_CONTENT_URL);
+        dumpSetting(s, p,
+                Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL,
+                GlobalSettingsProto.SMS_SHORT_CODES_UPDATE_METADATA_URL);
+        dumpSetting(s, p,
+                Settings.Global.APN_DB_UPDATE_CONTENT_URL,
+                GlobalSettingsProto.APN_DB_UPDATE_CONTENT_URL);
+        dumpSetting(s, p,
+                Settings.Global.APN_DB_UPDATE_METADATA_URL,
+                GlobalSettingsProto.APN_DB_UPDATE_METADATA_URL);
+        dumpSetting(s, p,
+                Settings.Global.CERT_PIN_UPDATE_CONTENT_URL,
+                GlobalSettingsProto.CERT_PIN_UPDATE_CONTENT_URL);
+        dumpSetting(s, p,
+                Settings.Global.CERT_PIN_UPDATE_METADATA_URL,
+                GlobalSettingsProto.CERT_PIN_UPDATE_METADATA_URL);
+        dumpSetting(s, p,
+                Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL,
+                GlobalSettingsProto.INTENT_FIREWALL_UPDATE_CONTENT_URL);
+        dumpSetting(s, p,
+                Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
+                GlobalSettingsProto.INTENT_FIREWALL_UPDATE_METADATA_URL);
+        dumpSetting(s, p,
+                Settings.Global.SELINUX_STATUS,
+                GlobalSettingsProto.SELINUX_STATUS);
+        dumpSetting(s, p,
+                Settings.Global.DEVELOPMENT_FORCE_RTL,
+                GlobalSettingsProto.DEVELOPMENT_FORCE_RTL);
+        dumpSetting(s, p,
+                Settings.Global.LOW_BATTERY_SOUND_TIMEOUT,
+                GlobalSettingsProto.LOW_BATTERY_SOUND_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
+                GlobalSettingsProto.WIFI_BOUNCE_DELAY_OVERRIDE_MS);
+        dumpSetting(s, p,
+                Settings.Global.POLICY_CONTROL,
+                GlobalSettingsProto.POLICY_CONTROL);
+        dumpSetting(s, p,
+                Settings.Global.ZEN_MODE,
+                GlobalSettingsProto.ZEN_MODE);
+        dumpSetting(s, p,
+                Settings.Global.ZEN_MODE_RINGER_LEVEL,
+                GlobalSettingsProto.ZEN_MODE_RINGER_LEVEL);
+        dumpSetting(s, p,
+                Settings.Global.ZEN_MODE_CONFIG_ETAG,
+                GlobalSettingsProto.ZEN_MODE_CONFIG_ETAG);
+        dumpSetting(s, p,
+                Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+                GlobalSettingsProto.HEADS_UP_NOTIFICATIONS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.DEVICE_NAME,
+                GlobalSettingsProto.DEVICE_NAME);
+        dumpSetting(s, p,
+                Settings.Global.NETWORK_SCORING_PROVISIONED,
+                GlobalSettingsProto.NETWORK_SCORING_PROVISIONED);
+        dumpSetting(s, p,
+                Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT,
+                GlobalSettingsProto.REQUIRE_PASSWORD_TO_DECRYPT);
+        dumpSetting(s, p,
+                Settings.Global.ENHANCED_4G_MODE_ENABLED,
+                GlobalSettingsProto.ENHANCED_4G_MODE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.VT_IMS_ENABLED,
+                GlobalSettingsProto.VT_IMS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.WFC_IMS_ENABLED,
+                GlobalSettingsProto.WFC_IMS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.WFC_IMS_MODE,
+                GlobalSettingsProto.WFC_IMS_MODE);
+        dumpSetting(s, p,
+                Settings.Global.WFC_IMS_ROAMING_MODE,
+                GlobalSettingsProto.WFC_IMS_ROAMING_MODE);
+        dumpSetting(s, p,
+                Settings.Global.WFC_IMS_ROAMING_ENABLED,
+                GlobalSettingsProto.WFC_IMS_ROAMING_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.LTE_SERVICE_FORCED,
+                GlobalSettingsProto.LTE_SERVICE_FORCED);
+        dumpSetting(s, p,
+                Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
+                GlobalSettingsProto.EPHEMERAL_COOKIE_MAX_SIZE_BYTES);
+        dumpSetting(s, p,
+                Settings.Global.ENABLE_EPHEMERAL_FEATURE,
+                GlobalSettingsProto.ENABLE_EPHEMERAL_FEATURE);
+        dumpSetting(s, p,
+                Settings.Global.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS,
+                GlobalSettingsProto.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS);
+        dumpSetting(s, p,
+                Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
+                GlobalSettingsProto.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED);
+        dumpSetting(s, p,
+                Settings.Global.BOOT_COUNT,
+                GlobalSettingsProto.BOOT_COUNT);
+        dumpSetting(s, p,
+                Settings.Global.SAFE_BOOT_DISALLOWED,
+                GlobalSettingsProto.SAFE_BOOT_DISALLOWED);
+        dumpSetting(s, p,
+                Settings.Global.DEVICE_DEMO_MODE,
+                GlobalSettingsProto.DEVICE_DEMO_MODE);
+        dumpSetting(s, p,
+                Settings.Global.RETAIL_DEMO_MODE_CONSTANTS,
+                GlobalSettingsProto.RETAIL_DEMO_MODE_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.DATABASE_DOWNGRADE_REASON,
+                GlobalSettingsProto.DATABASE_DOWNGRADE_REASON);
+        dumpSetting(s, p,
+                Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
+                GlobalSettingsProto.CONTACTS_DATABASE_WAL_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
+                GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
+        dumpSetting(s, p,
+                Settings.Global.MULTI_SIM_VOICE_PROMPT,
+                GlobalSettingsProto.MULTI_SIM_VOICE_PROMPT);
+        dumpSetting(s, p,
+                Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
+                GlobalSettingsProto.MULTI_SIM_DATA_CALL_SUBSCRIPTION);
+        dumpSetting(s, p,
+                Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
+                GlobalSettingsProto.MULTI_SIM_SMS_SUBSCRIPTION);
+        dumpSetting(s, p,
+                Settings.Global.MULTI_SIM_SMS_PROMPT,
+                GlobalSettingsProto.MULTI_SIM_SMS_PROMPT);
+        dumpSetting(s, p,
+                Settings.Global.NEW_CONTACT_AGGREGATOR,
+                GlobalSettingsProto.NEW_CONTACT_AGGREGATOR);
+        dumpSetting(s, p,
+                Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
+                GlobalSettingsProto.CONTACT_METADATA_SYNC_ENABLED);
+        dumpSetting(s, p,
+                Settings.Global.ENABLE_CELLULAR_ON_BOOT,
+                GlobalSettingsProto.ENABLE_CELLULAR_ON_BOOT);
+        dumpSetting(s, p,
+                Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
+                GlobalSettingsProto.MAX_NOTIFICATION_ENQUEUE_RATE);
+        dumpSetting(s, p,
+                Settings.Global.CELL_ON,
+                GlobalSettingsProto.CELL_ON);
+    }
+
+    /** Dump a single {@link SettingsState.Setting} to a proto buf */
+    private static void dumpSetting(@NonNull SettingsState settings,
+            @NonNull ProtoOutputStream proto, String settingName, long fieldId) {
+        SettingsState.Setting setting = settings.getSettingLocked(settingName);
+        long settingsToken = proto.start(fieldId);
+        proto.write(SettingProto.ID, setting.getId());
+        proto.write(SettingProto.NAME, settingName);
+        if (setting.getPackageName() != null) {
+            proto.write(SettingProto.PKG, setting.getPackageName());
+        }
+        proto.write(SettingProto.VALUE, setting.getValue());
+        if (setting.getDefaultValue() != null) {
+            proto.write(SettingProto.DEFAULT_VALUE, setting.getDefaultValue());
+            proto.write(SettingProto.DEFAULT_FROM_SYSTEM, setting.isDefaultFromSystem());
+        }
+        proto.end(settingsToken);
+    }
+
+    static void dumpProtoSecureSettingsLocked(
+            @NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+        dumpSetting(s, p,
+                Settings.Secure.ANDROID_ID,
+                SecureSettingsProto.ANDROID_ID);
+        dumpSetting(s, p,
+                Settings.Secure.DEFAULT_INPUT_METHOD,
+                SecureSettingsProto.DEFAULT_INPUT_METHOD);
+        dumpSetting(s, p,
+                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                SecureSettingsProto.SELECTED_INPUT_METHOD_SUBTYPE);
+        dumpSetting(s, p,
+                Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY,
+                SecureSettingsProto.INPUT_METHODS_SUBTYPE_HISTORY);
+        dumpSetting(s, p,
+                Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
+                SecureSettingsProto.INPUT_METHOD_SELECTOR_VISIBILITY);
+        dumpSetting(s, p,
+                Settings.Secure.VOICE_INTERACTION_SERVICE,
+                SecureSettingsProto.VOICE_INTERACTION_SERVICE);
+        dumpSetting(s, p,
+                Settings.Secure.AUTO_FILL_SERVICE,
+                SecureSettingsProto.AUTO_FILL_SERVICE);
+        dumpSetting(s, p,
+                Settings.Secure.BLUETOOTH_HCI_LOG,
+                SecureSettingsProto.BLUETOOTH_HCI_LOG);
+        dumpSetting(s, p,
+                Settings.Secure.USER_SETUP_COMPLETE,
+                SecureSettingsProto.USER_SETUP_COMPLETE);
+        dumpSetting(s, p,
+                Settings.Secure.COMPLETED_CATEGORY_PREFIX,
+                SecureSettingsProto.COMPLETED_CATEGORY_PREFIX);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_INPUT_METHODS,
+                SecureSettingsProto.ENABLED_INPUT_METHODS);
+        dumpSetting(s, p,
+                Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
+                SecureSettingsProto.DISABLED_SYSTEM_INPUT_METHODS);
+        dumpSetting(s, p,
+                Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                SecureSettingsProto.SHOW_IME_WITH_HARD_KEYBOARD);
+        dumpSetting(s, p,
+                Settings.Secure.ALWAYS_ON_VPN_APP,
+                SecureSettingsProto.ALWAYS_ON_VPN_APP);
+        dumpSetting(s, p,
+                Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
+                SecureSettingsProto.ALWAYS_ON_VPN_LOCKDOWN);
+        dumpSetting(s, p,
+                Settings.Secure.INSTALL_NON_MARKET_APPS,
+                SecureSettingsProto.INSTALL_NON_MARKET_APPS);
+        dumpSetting(s, p,
+                Settings.Secure.LOCATION_MODE,
+                SecureSettingsProto.LOCATION_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.LOCATION_PREVIOUS_MODE,
+                SecureSettingsProto.LOCATION_PREVIOUS_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
+                SecureSettingsProto.LOCK_TO_APP_EXIT_LOCKED);
+        dumpSetting(s, p,
+                Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+                SecureSettingsProto.LOCK_SCREEN_LOCK_AFTER_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
+                SecureSettingsProto.LOCK_SCREEN_ALLOW_REMOTE_INPUT);
+        dumpSetting(s, p,
+                Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING,
+                SecureSettingsProto.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING);
+        dumpSetting(s, p,
+                Settings.Secure.TRUST_AGENTS_INITIALIZED,
+                SecureSettingsProto.TRUST_AGENTS_INITIALIZED);
+        dumpSetting(s, p,
+                Settings.Secure.PARENTAL_CONTROL_ENABLED,
+                SecureSettingsProto.PARENTAL_CONTROL_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
+                SecureSettingsProto.PARENTAL_CONTROL_LAST_UPDATE);
+        dumpSetting(s, p,
+                Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL,
+                SecureSettingsProto.PARENTAL_CONTROL_REDIRECT_URL);
+        dumpSetting(s, p,
+                Settings.Secure.SETTINGS_CLASSNAME,
+                SecureSettingsProto.SETTINGS_CLASSNAME);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.TOUCH_EXPLORATION_ENABLED,
+                SecureSettingsProto.TOUCH_EXPLORATION_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                SecureSettingsProto.ENABLED_ACCESSIBILITY_SERVICES);
+        dumpSetting(s, p,
+                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+                SecureSettingsProto.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD,
+                SecureSettingsProto.ACCESSIBILITY_SPEAK_PASSWORD);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
+                SecureSettingsProto.ACCESSIBILITY_SCRIPT_INJECTION);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL,
+                SecureSettingsProto.ACCESSIBILITY_SCREEN_READER_URL);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
+                SecureSettingsProto.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+                SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SecureSettingsProto.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_LOCALE,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_LOCALE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_PRESET);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_TYPE,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_EDGE_TYPE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_EDGE_COLOR);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_TYPEFACE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
+                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_FONT_SCALE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
+                SecureSettingsProto.ACCESSIBILITY_DISPLAY_DALTONIZER);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
+                SecureSettingsProto.ACCESSIBILITY_AUTOCLICK_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+                SecureSettingsProto.ACCESSIBILITY_AUTOCLICK_DELAY);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
+                SecureSettingsProto.ACCESSIBILITY_LARGE_POINTER_ICON);
+        dumpSetting(s, p,
+                Settings.Secure.LONG_PRESS_TIMEOUT,
+                SecureSettingsProto.LONG_PRESS_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.Secure.MULTI_PRESS_TIMEOUT,
+                SecureSettingsProto.MULTI_PRESS_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_PRINT_SERVICES,
+                SecureSettingsProto.ENABLED_PRINT_SERVICES);
+        dumpSetting(s, p,
+                Settings.Secure.DISABLED_PRINT_SERVICES,
+                SecureSettingsProto.DISABLED_PRINT_SERVICES);
+        dumpSetting(s, p,
+                Settings.Secure.DISPLAY_DENSITY_FORCED,
+                SecureSettingsProto.DISPLAY_DENSITY_FORCED);
+        dumpSetting(s, p,
+                Settings.Secure.TTS_DEFAULT_RATE,
+                SecureSettingsProto.TTS_DEFAULT_RATE);
+        dumpSetting(s, p,
+                Settings.Secure.TTS_DEFAULT_PITCH,
+                SecureSettingsProto.TTS_DEFAULT_PITCH);
+        dumpSetting(s, p,
+                Settings.Secure.TTS_DEFAULT_SYNTH,
+                SecureSettingsProto.TTS_DEFAULT_SYNTH);
+        dumpSetting(s, p,
+                Settings.Secure.TTS_DEFAULT_LOCALE,
+                SecureSettingsProto.TTS_DEFAULT_LOCALE);
+        dumpSetting(s, p,
+                Settings.Secure.TTS_ENABLED_PLUGINS,
+                SecureSettingsProto.TTS_ENABLED_PLUGINS);
+        dumpSetting(s, p,
+                Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
+                SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS);
+        dumpSetting(s, p,
+                Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
+                SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS);
+        dumpSetting(s, p,
+                Settings.Secure.PREFERRED_TTY_MODE,
+                SecureSettingsProto.PREFERRED_TTY_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED,
+                SecureSettingsProto.ENHANCED_VOICE_PRIVACY_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.TTY_MODE_ENABLED,
+                SecureSettingsProto.TTY_MODE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_ENABLED,
+                SecureSettingsProto.BACKUP_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_AUTO_RESTORE,
+                SecureSettingsProto.BACKUP_AUTO_RESTORE);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_PROVISIONED,
+                SecureSettingsProto.BACKUP_PROVISIONED);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_TRANSPORT,
+                SecureSettingsProto.BACKUP_TRANSPORT);
+        dumpSetting(s, p,
+                Settings.Secure.LAST_SETUP_SHOWN,
+                SecureSettingsProto.LAST_SETUP_SHOWN);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
+                SecureSettingsProto.SEARCH_GLOBAL_SEARCH_ACTIVITY);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_NUM_PROMOTED_SOURCES,
+                SecureSettingsProto.SEARCH_NUM_PROMOTED_SOURCES);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_RESULTS_TO_DISPLAY,
+                SecureSettingsProto.SEARCH_MAX_RESULTS_TO_DISPLAY);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_RESULTS_PER_SOURCE,
+                SecureSettingsProto.SEARCH_MAX_RESULTS_PER_SOURCE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
+                SecureSettingsProto.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS,
+                SecureSettingsProto.SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_SOURCE_TIMEOUT_MILLIS,
+                SecureSettingsProto.SEARCH_SOURCE_TIMEOUT_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_PREFILL_MILLIS,
+                SecureSettingsProto.SEARCH_PREFILL_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_STAT_AGE_MILLIS,
+                SecureSettingsProto.SEARCH_MAX_STAT_AGE_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS,
+                SecureSettingsProto.SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING,
+                SecureSettingsProto.SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING,
+                SecureSettingsProto.SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_SHORTCUTS_RETURNED,
+                SecureSettingsProto.SEARCH_MAX_SHORTCUTS_RETURNED);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_QUERY_THREAD_CORE_POOL_SIZE,
+                SecureSettingsProto.SEARCH_QUERY_THREAD_CORE_POOL_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_QUERY_THREAD_MAX_POOL_SIZE,
+                SecureSettingsProto.SEARCH_QUERY_THREAD_MAX_POOL_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE,
+                SecureSettingsProto.SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE,
+                SecureSettingsProto.SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_THREAD_KEEPALIVE_SECONDS,
+                SecureSettingsProto.SEARCH_THREAD_KEEPALIVE_SECONDS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT,
+                SecureSettingsProto.SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT);
+        dumpSetting(s, p,
+                Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND,
+                SecureSettingsProto.MOUNT_PLAY_NOTIFICATION_SND);
+        dumpSetting(s, p,
+                Settings.Secure.MOUNT_UMS_AUTOSTART,
+                SecureSettingsProto.MOUNT_UMS_AUTOSTART);
+        dumpSetting(s, p,
+                Settings.Secure.MOUNT_UMS_PROMPT,
+                SecureSettingsProto.MOUNT_UMS_PROMPT);
+        dumpSetting(s, p,
+                Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
+                SecureSettingsProto.MOUNT_UMS_NOTIFY_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ANR_SHOW_BACKGROUND,
+                SecureSettingsProto.ANR_SHOW_BACKGROUND);
+        dumpSetting(s, p,
+                Settings.Secure.VOICE_RECOGNITION_SERVICE,
+                SecureSettingsProto.VOICE_RECOGNITION_SERVICE);
+        dumpSetting(s, p,
+                Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
+                SecureSettingsProto.PACKAGE_VERIFIER_USER_CONSENT);
+        dumpSetting(s, p,
+                Settings.Secure.SELECTED_SPELL_CHECKER,
+                SecureSettingsProto.SELECTED_SPELL_CHECKER);
+        dumpSetting(s, p,
+                Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,
+                SecureSettingsProto.SELECTED_SPELL_CHECKER_SUBTYPE);
+        dumpSetting(s, p,
+                Settings.Secure.SPELL_CHECKER_ENABLED,
+                SecureSettingsProto.SPELL_CHECKER_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
+                SecureSettingsProto.INCALL_POWER_BUTTON_BEHAVIOR);
+        dumpSetting(s, p,
+                Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR,
+                SecureSettingsProto.INCALL_BACK_BUTTON_BEHAVIOR);
+        dumpSetting(s, p,
+                Settings.Secure.WAKE_GESTURE_ENABLED,
+                SecureSettingsProto.WAKE_GESTURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_ENABLED,
+                SecureSettingsProto.DOZE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_ALWAYS_ON,
+                SecureSettingsProto.DOZE_ALWAYS_ON);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_PULSE_ON_PICK_UP,
+                SecureSettingsProto.DOZE_PULSE_ON_PICK_UP);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
+                SecureSettingsProto.DOZE_PULSE_ON_DOUBLE_TAP);
+        dumpSetting(s, p,
+                Settings.Secure.UI_NIGHT_MODE,
+                SecureSettingsProto.UI_NIGHT_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_ENABLED,
+                SecureSettingsProto.SCREENSAVER_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_COMPONENTS,
+                SecureSettingsProto.SCREENSAVER_COMPONENTS);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+                SecureSettingsProto.SCREENSAVER_ACTIVATE_ON_DOCK);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+                SecureSettingsProto.SCREENSAVER_ACTIVATE_ON_SLEEP);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
+                SecureSettingsProto.SCREENSAVER_DEFAULT_COMPONENT);
+        dumpSetting(s, p,
+                Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
+                SecureSettingsProto.NFC_PAYMENT_DEFAULT_COMPONENT);
+        dumpSetting(s, p,
+                Settings.Secure.NFC_PAYMENT_FOREGROUND,
+                SecureSettingsProto.NFC_PAYMENT_FOREGROUND);
+        dumpSetting(s, p,
+                Settings.Secure.SMS_DEFAULT_APPLICATION,
+                SecureSettingsProto.SMS_DEFAULT_APPLICATION);
+        dumpSetting(s, p,
+                Settings.Secure.DIALER_DEFAULT_APPLICATION,
+                SecureSettingsProto.DIALER_DEFAULT_APPLICATION);
+        dumpSetting(s, p,
+                Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
+                SecureSettingsProto.EMERGENCY_ASSISTANCE_APPLICATION);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+                SecureSettingsProto.ASSIST_STRUCTURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
+                SecureSettingsProto.ASSIST_SCREENSHOT_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+                SecureSettingsProto.ASSIST_DISCLOSURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
+                SecureSettingsProto.ENABLED_NOTIFICATION_ASSISTANT);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+                SecureSettingsProto.ENABLED_NOTIFICATION_LISTENERS);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+                SecureSettingsProto.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES);
+        dumpSetting(s, p,
+                Settings.Secure.SYNC_PARENT_SOUNDS,
+                SecureSettingsProto.SYNC_PARENT_SOUNDS);
+        dumpSetting(s, p,
+                Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
+                SecureSettingsProto.IMMERSIVE_MODE_CONFIRMATIONS);
+        dumpSetting(s, p,
+                Settings.Secure.PRINT_SERVICE_SEARCH_URI,
+                SecureSettingsProto.PRINT_SERVICE_SEARCH_URI);
+        dumpSetting(s, p,
+                Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
+                SecureSettingsProto.PAYMENT_SERVICE_SEARCH_URI);
+        dumpSetting(s, p,
+                Settings.Secure.SKIP_FIRST_USE_HINTS,
+                SecureSettingsProto.SKIP_FIRST_USE_HINTS);
+        dumpSetting(s, p,
+                Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS,
+                SecureSettingsProto.UNSAFE_VOLUME_MUSIC_ACTIVE_MS);
+        dumpSetting(s, p,
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                SecureSettingsProto.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+        dumpSetting(s, p,
+                Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
+                SecureSettingsProto.TV_INPUT_HIDDEN_INPUTS);
+        dumpSetting(s, p,
+                Settings.Secure.TV_INPUT_CUSTOM_LABELS,
+                SecureSettingsProto.TV_INPUT_CUSTOM_LABELS);
+        dumpSetting(s, p,
+                Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED,
+                SecureSettingsProto.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED);
+        dumpSetting(s, p,
+                Settings.Secure.SLEEP_TIMEOUT,
+                SecureSettingsProto.SLEEP_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.Secure.DOUBLE_TAP_TO_WAKE,
+                SecureSettingsProto.DOUBLE_TAP_TO_WAKE);
+        dumpSetting(s, p,
+                Settings.Secure.ASSISTANT,
+                SecureSettingsProto.ASSISTANT);
+        dumpSetting(s, p,
+                Settings.Secure.CAMERA_GESTURE_DISABLED,
+                SecureSettingsProto.CAMERA_GESTURE_DISABLED);
+        dumpSetting(s, p,
+                Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
+                SecureSettingsProto.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED);
+        dumpSetting(s, p,
+                Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+                SecureSettingsProto.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
+                SecureSettingsProto.NIGHT_DISPLAY_ACTIVATED);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_AUTO_MODE,
+                SecureSettingsProto.NIGHT_DISPLAY_AUTO_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
+                SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_START_TIME);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
+                SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_END_TIME);
+        dumpSetting(s, p,
+                Settings.Secure.BRIGHTNESS_USE_TWILIGHT,
+                SecureSettingsProto.BRIGHTNESS_USE_TWILIGHT);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_VR_LISTENERS,
+                SecureSettingsProto.ENABLED_VR_LISTENERS);
+        dumpSetting(s, p,
+                Settings.Secure.VR_DISPLAY_MODE,
+                SecureSettingsProto.VR_DISPLAY_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.CARRIER_APPS_HANDLED,
+                SecureSettingsProto.CARRIER_APPS_HANDLED);
+        dumpSetting(s, p,
+                Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH,
+                SecureSettingsProto.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
+                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
+                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
+                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
+                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_LAST_RUN);
+        dumpSetting(s, p,
+                Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
+                SecureSettingsProto.SYSTEM_NAVIGATION_KEYS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.DOWNLOADS_BACKUP_ENABLED,
+                SecureSettingsProto.DOWNLOADS_BACKUP_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.DOWNLOADS_BACKUP_ALLOW_METERED,
+                SecureSettingsProto.DOWNLOADS_BACKUP_ALLOW_METERED);
+        dumpSetting(s, p,
+                Settings.Secure.DOWNLOADS_BACKUP_CHARGING_ONLY,
+                SecureSettingsProto.DOWNLOADS_BACKUP_CHARGING_ONLY);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DOWNLOADS_DAYS_TO_RETAIN,
+                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_DOWNLOADS_DAYS_TO_RETAIN);
+        dumpSetting(s, p,
+                Settings.Secure.QS_TILES,
+                SecureSettingsProto.QS_TILES);
+        dumpSetting(s, p,
+                Settings.Secure.DEMO_USER_SETUP_COMPLETE,
+                SecureSettingsProto.DEMO_USER_SETUP_COMPLETE);
+        dumpSetting(s, p,
+                Settings.Secure.WEB_ACTION_ENABLED,
+                SecureSettingsProto.WEB_ACTION_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.DEVICE_PAIRED,
+                SecureSettingsProto.DEVICE_PAIRED);
+    }
+
+    private static void dumpProtoSystemSettingsLocked(
+            @NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+        dumpSetting(s, p,
+                Settings.System.END_BUTTON_BEHAVIOR,
+                SystemSettingsProto.END_BUTTON_BEHAVIOR);
+        dumpSetting(s, p,
+                Settings.System.ADVANCED_SETTINGS,
+                SystemSettingsProto.ADVANCED_SETTINGS);
+        dumpSetting(s, p,
+                Settings.System.BLUETOOTH_DISCOVERABILITY,
+                SystemSettingsProto.BLUETOOTH_DISCOVERABILITY);
+        dumpSetting(s, p,
+                Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+                SystemSettingsProto.BLUETOOTH_DISCOVERABILITY_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.System.FONT_SCALE,
+                SystemSettingsProto.FONT_SCALE);
+        dumpSetting(s, p,
+                Settings.System.SYSTEM_LOCALES,
+                SystemSettingsProto.SYSTEM_LOCALES);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_OFF_TIMEOUT,
+                SystemSettingsProto.SCREEN_OFF_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS,
+                SystemSettingsProto.SCREEN_BRIGHTNESS);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
+                SystemSettingsProto.SCREEN_BRIGHTNESS_FOR_VR);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                SystemSettingsProto.SCREEN_BRIGHTNESS_MODE);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
+                SystemSettingsProto.SCREEN_AUTO_BRIGHTNESS_ADJ);
+        dumpSetting(s, p,
+                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+                SystemSettingsProto.MODE_RINGER_STREAMS_AFFECTED);
+        dumpSetting(s, p,
+                Settings.System.MUTE_STREAMS_AFFECTED,
+                SystemSettingsProto.MUTE_STREAMS_AFFECTED);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_ON,
+                SystemSettingsProto.VIBRATE_ON);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_INPUT_DEVICES,
+                SystemSettingsProto.VIBRATE_INPUT_DEVICES);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_RING,
+                SystemSettingsProto.VOLUME_RING);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_SYSTEM,
+                SystemSettingsProto.VOLUME_SYSTEM);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_VOICE,
+                SystemSettingsProto.VOLUME_VOICE);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_MUSIC,
+                SystemSettingsProto.VOLUME_MUSIC);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_ALARM,
+                SystemSettingsProto.VOLUME_ALARM);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_NOTIFICATION,
+                SystemSettingsProto.VOLUME_NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_BLUETOOTH_SCO,
+                SystemSettingsProto.VOLUME_BLUETOOTH_SCO);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_MASTER,
+                SystemSettingsProto.VOLUME_MASTER);
+        dumpSetting(s, p,
+                Settings.System.MASTER_MONO,
+                SystemSettingsProto.MASTER_MONO);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_IN_SILENT,
+                SystemSettingsProto.VIBRATE_IN_SILENT);
+        dumpSetting(s, p,
+                Settings.System.APPEND_FOR_LAST_AUDIBLE,
+                SystemSettingsProto.APPEND_FOR_LAST_AUDIBLE);
+        dumpSetting(s, p,
+                Settings.System.RINGTONE,
+                SystemSettingsProto.RINGTONE);
+        dumpSetting(s, p,
+                Settings.System.RINGTONE_CACHE,
+                SystemSettingsProto.RINGTONE_CACHE);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_SOUND,
+                SystemSettingsProto.NOTIFICATION_SOUND);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_SOUND_CACHE,
+                SystemSettingsProto.NOTIFICATION_SOUND_CACHE);
+        dumpSetting(s, p,
+                Settings.System.ALARM_ALERT,
+                SystemSettingsProto.ALARM_ALERT);
+        dumpSetting(s, p,
+                Settings.System.ALARM_ALERT_CACHE,
+                SystemSettingsProto.ALARM_ALERT_CACHE);
+        dumpSetting(s, p,
+                Settings.System.MEDIA_BUTTON_RECEIVER,
+                SystemSettingsProto.MEDIA_BUTTON_RECEIVER);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_REPLACE,
+                SystemSettingsProto.TEXT_AUTO_REPLACE);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_CAPS,
+                SystemSettingsProto.TEXT_AUTO_CAPS);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_PUNCTUATE,
+                SystemSettingsProto.TEXT_AUTO_PUNCTUATE);
+        dumpSetting(s, p,
+                Settings.System.TEXT_SHOW_PASSWORD,
+                SystemSettingsProto.TEXT_SHOW_PASSWORD);
+        dumpSetting(s, p,
+                Settings.System.SHOW_GTALK_SERVICE_STATUS,
+                SystemSettingsProto.SHOW_GTALK_SERVICE_STATUS);
+        dumpSetting(s, p,
+                Settings.System.TIME_12_24,
+                SystemSettingsProto.TIME_12_24);
+        dumpSetting(s, p,
+                Settings.System.DATE_FORMAT,
+                SystemSettingsProto.DATE_FORMAT);
+        dumpSetting(s, p,
+                Settings.System.SETUP_WIZARD_HAS_RUN,
+                SystemSettingsProto.SETUP_WIZARD_HAS_RUN);
+        dumpSetting(s, p,
+                Settings.System.ACCELEROMETER_ROTATION,
+                SystemSettingsProto.ACCELEROMETER_ROTATION);
+        dumpSetting(s, p,
+                Settings.System.USER_ROTATION,
+                SystemSettingsProto.USER_ROTATION);
+        dumpSetting(s, p,
+                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
+                SystemSettingsProto.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_WHEN_RINGING,
+                SystemSettingsProto.VIBRATE_WHEN_RINGING);
+        dumpSetting(s, p,
+                Settings.System.DTMF_TONE_WHEN_DIALING,
+                SystemSettingsProto.DTMF_TONE_WHEN_DIALING);
+        dumpSetting(s, p,
+                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+                SystemSettingsProto.DTMF_TONE_TYPE_WHEN_DIALING);
+        dumpSetting(s, p,
+                Settings.System.HEARING_AID,
+                SystemSettingsProto.HEARING_AID);
+        dumpSetting(s, p,
+                Settings.System.TTY_MODE,
+                SystemSettingsProto.TTY_MODE);
+        dumpSetting(s, p,
+                Settings.System.SOUND_EFFECTS_ENABLED,
+                SystemSettingsProto.SOUND_EFFECTS_ENABLED);
+        dumpSetting(s, p,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                SystemSettingsProto.HAPTIC_FEEDBACK_ENABLED);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_LIGHT_PULSE,
+                SystemSettingsProto.NOTIFICATION_LIGHT_PULSE);
+        dumpSetting(s, p,
+                Settings.System.POINTER_LOCATION,
+                SystemSettingsProto.POINTER_LOCATION);
+        dumpSetting(s, p,
+                Settings.System.SHOW_TOUCHES,
+                SystemSettingsProto.SHOW_TOUCHES);
+        dumpSetting(s, p,
+                Settings.System.WINDOW_ORIENTATION_LISTENER_LOG,
+                SystemSettingsProto.WINDOW_ORIENTATION_LISTENER_LOG);
+        dumpSetting(s, p,
+                Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
+                SystemSettingsProto.LOCKSCREEN_SOUNDS_ENABLED);
+        dumpSetting(s, p,
+                Settings.System.LOCKSCREEN_DISABLED,
+                SystemSettingsProto.LOCKSCREEN_DISABLED);
+        dumpSetting(s, p,
+                Settings.System.SIP_RECEIVE_CALLS,
+                SystemSettingsProto.SIP_RECEIVE_CALLS);
+        dumpSetting(s, p,
+                Settings.System.SIP_CALL_OPTIONS,
+                SystemSettingsProto.SIP_CALL_OPTIONS);
+        dumpSetting(s, p,
+                Settings.System.SIP_ALWAYS,
+                SystemSettingsProto.SIP_ALWAYS);
+        dumpSetting(s, p,
+                Settings.System.SIP_ADDRESS_ONLY,
+                SystemSettingsProto.SIP_ADDRESS_ONLY);
+        dumpSetting(s, p,
+                Settings.System.POINTER_SPEED,
+                SystemSettingsProto.POINTER_SPEED);
+        dumpSetting(s, p,
+                Settings.System.LOCK_TO_APP_ENABLED,
+                SystemSettingsProto.LOCK_TO_APP_ENABLED);
+        dumpSetting(s, p,
+                Settings.System.EGG_MODE,
+                SystemSettingsProto.EGG_MODE);
+        dumpSetting(s, p,
+                Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
+                SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 3e62158..25e1f16 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -17,6 +17,7 @@
 package com.android.providers.settings;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.backup.BackupManager;
@@ -61,9 +62,11 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.ByteStringUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
@@ -76,17 +79,26 @@
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
 
 import static android.os.Process.ROOT_UID;
-import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.SHELL_UID;
+import static android.os.Process.SYSTEM_UID;
+
 
 /**
  * <p>
@@ -162,6 +174,7 @@
     public static final int SETTINGS_TYPE_GLOBAL = 0;
     public static final int SETTINGS_TYPE_SYSTEM = 1;
     public static final int SETTINGS_TYPE_SECURE = 2;
+    public static final int SETTINGS_TYPE_SSAID = 3;
 
     public static final int SETTINGS_TYPE_MASK = 0xF0000000;
     public static final int SETTINGS_TYPE_SHIFT = 28;
@@ -249,6 +262,9 @@
             case SETTINGS_TYPE_SYSTEM: {
                 return "SETTINGS_SYSTEM";
             }
+            case SETTINGS_TYPE_SSAID: {
+                return "SETTINGS_SSAID";
+            }
             default: {
                 return "UNKNOWN";
             }
@@ -262,6 +278,7 @@
 
     @Override
     public boolean onCreate() {
+        Settings.setInSystemServer();
         synchronized (mLock) {
             mUserManager = UserManager.get(getContext());
             mPackageManager = AppGlobals.getPackageManager();
@@ -591,6 +608,22 @@
         return cacheDir;
     }
 
+    /**
+     * Dump all settings as a proto buf.
+     *
+     * @param fd The file to dump to
+     */
+    void dumpProto(@NonNull FileDescriptor fd) {
+        ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+        synchronized (mLock) {
+            SettingsProtoDumpUtil.dumpProtoLocked(mSettingsRegistry, proto);
+
+        }
+
+        proto.flush();
+    }
+
     public void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mLock) {
             final long identity = Binder.clearCallingIdentity();
@@ -653,7 +686,7 @@
             pw.print(" value:"); pw.print(toDumpString(setting.getValue()));
             if (setting.getDefaultValue() != null) {
                 pw.print(" default:"); pw.print(setting.getDefaultValue());
-                pw.print(" defaultSystemSet:"); pw.print(setting.isDefaultSystemSet());
+                pw.print(" defaultSystemSet:"); pw.print(setting.isDefaultFromSystem());
             }
             if (setting.getTag() != null) {
                 pw.print(" tag:"); pw.print(setting.getTag());
@@ -704,6 +737,13 @@
                             UserHandle.getUserId(uid));
                 }
             }
+
+            @Override
+            public void onUidRemoved(int uid) {
+                synchronized (mLock) {
+                    mSettingsRegistry.onUidRemovedLocked(uid);
+                }
+            }
         };
 
         // package changes
@@ -813,7 +853,8 @@
             SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
                     SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
 
-            List<String> names = settingsState.getSettingNamesLocked();
+            List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_GLOBAL,
+                    UserHandle.USER_SYSTEM);
 
             final int nameCount = names.size();
 
@@ -836,6 +877,9 @@
             Slog.v(LOG_TAG, "getGlobalSetting(" + name + ")");
         }
 
+        // Ensure the caller can access the setting.
+        enforceSettingReadable(name, SETTINGS_TYPE_GLOBAL, UserHandle.getCallingUserId());
+
         // Get the value.
         synchronized (mLock) {
             return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL,
@@ -938,8 +982,7 @@
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
         synchronized (mLock) {
-            List<String> names = mSettingsRegistry.getSettingsNamesLocked(
-                    SETTINGS_TYPE_SECURE, callingUserId);
+            List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SECURE, callingUserId);
 
             final int nameCount = names.size();
 
@@ -957,8 +1000,15 @@
                     continue;
                 }
 
-                Setting setting = mSettingsRegistry.getSettingLocked(
-                        SETTINGS_TYPE_SECURE, owningUserId, name);
+                // As of Android O, the SSAID is read from an app-specific entry in table
+                // SETTINGS_FILE_SSAID, unless accessed by a system process.
+                final Setting setting;
+                if (isNewSsaidSetting(name)) {
+                    setting = getSsaidSettingLocked(owningUserId);
+                } else {
+                    setting = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, owningUserId,
+                            name);
+                }
                 appendSettingToCursor(result, setting);
             }
 
@@ -974,6 +1024,9 @@
         // Resolve the userId on whose behalf the call is made.
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
 
+        // Ensure the caller can access the setting.
+        enforceSettingReadable(name, SETTINGS_TYPE_SECURE, callingUserId);
+
         // Determine the owning user as some profile settings are cloned from the parent.
         final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name);
 
@@ -986,11 +1039,43 @@
 
         // Get the value.
         synchronized (mLock) {
+            // As of Android O, the SSAID is read from an app-specific entry in table
+            // SETTINGS_FILE_SSAID, unless accessed by a system process.
+            if (isNewSsaidSetting(name)) {
+                return getSsaidSettingLocked(owningUserId);
+            }
+
             return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE,
                     owningUserId, name);
         }
     }
 
+    private boolean isNewSsaidSetting(String name) {
+        return Settings.Secure.ANDROID_ID.equals(name)
+                && UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID;
+    }
+
+    private Setting getSsaidSettingLocked(int owningUserId) {
+        // Get uid of caller (key) used to store ssaid value
+        String name = Integer.toString(
+                UserHandle.getUid(owningUserId, UserHandle.getAppId(Binder.getCallingUid())));
+
+        if (DEBUG) {
+            Slog.v(LOG_TAG, "getSsaidSettingLocked(" + name + "," + owningUserId + ")");
+        }
+
+        // Retrieve the ssaid from the table if present.
+        final Setting ssaid = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId,
+                name);
+
+        // Lazy initialize ssaid if not yet present in ssaid table.
+        if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) {
+            return mSettingsRegistry.generateSsaidLocked(getCallingPackage(), owningUserId);
+        }
+
+        return ssaid;
+    }
+
     private boolean insertSecureSetting(String name, String value, String tag,
             boolean makeDefault, int requestingUserId, boolean forceNotify) {
         if (DEBUG) {
@@ -1104,8 +1189,7 @@
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
         synchronized (mLock) {
-            List<String> names = mSettingsRegistry.getSettingsNamesLocked(
-                    SETTINGS_TYPE_SYSTEM, callingUserId);
+            List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_SYSTEM, callingUserId);
 
             final int nameCount = names.size();
 
@@ -1136,6 +1220,9 @@
         // Resolve the userId on whose behalf the call is made.
         final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
 
+        // Ensure the caller can access the setting.
+        enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, callingUserId);
+
         // Determine the owning user as some profile settings are cloned from the parent.
         final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name);
 
@@ -1354,9 +1441,15 @@
                 && (parentId = getGroupParentLocked(userId)) != userId) {
             // The setting has a dependency and the profile has a parent
             String dependency = sSystemCloneFromParentOnDependency.get(setting);
-            Setting settingObj = getSecureSetting(dependency, userId);
-            if (settingObj != null && settingObj.getValue().equals("1")) {
-                return parentId;
+            // Lookup the dependency setting as ourselves, some callers may not have access to it.
+            final long token = Binder.clearCallingIdentity();
+            try {
+                Setting settingObj = getSecureSetting(dependency, userId);
+                if (settingObj != null && settingObj.getValue().equals("1")) {
+                    return parentId;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
         return resolveOwningUserIdLocked(userId, sSystemCloneToManagedSettings, setting);
@@ -1424,6 +1517,55 @@
         }
     }
 
+    private Set<String> getEphemeralAccessibleSettings(int settingsType) {
+        switch (settingsType) {
+            case SETTINGS_TYPE_GLOBAL:
+                return Settings.Global.EPHEMERAL_SETTINGS;
+            case SETTINGS_TYPE_SECURE:
+                return Settings.Secure.EPHEMERAL_SETTINGS;
+            case SETTINGS_TYPE_SYSTEM:
+                return Settings.System.EPHEMERAL_SETTINGS;
+            default:
+                throw new IllegalArgumentException("Invalid settings type: " + settingsType);
+        }
+    }
+
+    private List<String> getSettingsNamesLocked(int settingsType, int userId) {
+        ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId);
+        if (ai.isEphemeralApp()) {
+            return new ArrayList<String>(getEphemeralAccessibleSettings(settingsType));
+        } else {
+            return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId);
+        }
+    }
+
+    private void enforceSettingReadable(String settingName, int settingsType, int userId) {
+        if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
+            return;
+        }
+        ApplicationInfo ai = getCallingApplicationInfoOrThrow(userId);
+        if (!ai.isEphemeralApp()) {
+            return;
+        }
+        if (!getEphemeralAccessibleSettings(settingsType).contains(settingName)) {
+            throw new SecurityException("Setting " + settingName + " is not accessible from"
+                    + " ephemeral package " + getCallingPackage());
+        }
+    }
+
+    private ApplicationInfo getCallingApplicationInfoOrThrow(int userId) {
+        ApplicationInfo ai = null;
+        try {
+            ai = mPackageManager.getApplicationInfo(getCallingPackage(), 0 , userId);
+        } catch (RemoteException ignored) {
+        }
+        if (ai == null) {
+            throw new IllegalStateException("Failed to lookup info for package "
+                    + getCallingPackage());
+        }
+        return ai;
+    }
+
     private PackageInfo getCallingPackageInfoOrThrow(int userId) {
         try {
             PackageInfo packageInfo = mPackageManager.getPackageInfo(
@@ -1493,7 +1635,7 @@
         value = value.substring(1);
 
         Setting settingValue = getSecureSetting(
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId);
+                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED, owningUserId);
         if (settingValue == null) {
             return false;
         }
@@ -1818,6 +1960,9 @@
         private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml";
         private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml";
         private static final String SETTINGS_FILE_SECURE = "settings_secure.xml";
+        private static final String SETTINGS_FILE_SSAID = "settings_ssaid.xml";
+
+        private static final String SSAID_USER_KEY = "userkey";
 
         private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
 
@@ -1832,6 +1977,133 @@
             mGenerationRegistry = new GenerationRegistry(mLock);
             mBackupManager = new BackupManager(getContext());
             migrateAllLegacySettingsIfNeeded();
+            syncSsaidTableOnStart();
+        }
+
+        private void generateUserKeyLocked(int userId) {
+            // Generate a random key for each user used for creating a new ssaid.
+            final byte[] keyBytes = new byte[32];
+            final SecureRandom rand = new SecureRandom();
+            rand.nextBytes(keyBytes);
+
+            // Convert to string for storage in settings table.
+            final String userKey = ByteStringUtils.toHexString(keyBytes);
+
+            // Store the key in the ssaid table.
+            final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
+            final boolean success = ssaidSettings.insertSettingLocked(SSAID_USER_KEY, userKey, null,
+                    true, SettingsState.SYSTEM_PACKAGE_NAME);
+
+            if (!success) {
+                throw new IllegalStateException("Ssaid settings not accessible");
+            }
+        }
+
+        private byte[] getLengthPrefix(byte[] data) {
+            return ByteBuffer.allocate(4).putInt(data.length).array();
+        }
+
+        public Setting generateSsaidLocked(String packageName, int userId) {
+            final PackageInfo packageInfo;
+            try {
+                packageInfo = mPackageManager.getPackageInfo(packageName,
+                        PackageManager.GET_SIGNATURES, userId);
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Package info doesn't exist");
+            }
+
+            // Read the user's key from the ssaid table.
+            Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
+            if (userKeySetting == null || userKeySetting.isNull()
+                    || userKeySetting.getValue() == null) {
+                // Lazy initialize and store the user key.
+                generateUserKeyLocked(userId);
+                userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
+                if (userKeySetting == null || userKeySetting.isNull()
+                        || userKeySetting.getValue() == null) {
+                    throw new IllegalStateException("User key not accessible");
+                }
+            }
+            final String userKey = userKeySetting.getValue();
+
+            // Convert the user's key back to a byte array.
+            final byte[] keyBytes = ByteStringUtils.fromHexToByteArray(userKey);
+
+            // Validate that the key is of expected length.
+            // Keys are currently 32 bytes, but were once 16 bytes during Android O development.
+            if (keyBytes == null || (keyBytes.length != 16 && keyBytes.length != 32)) {
+                throw new IllegalStateException("User key invalid");
+            }
+
+            final Mac m;
+            try {
+                m = Mac.getInstance("HmacSHA256");
+                m.init(new SecretKeySpec(keyBytes, m.getAlgorithm()));
+            } catch (NoSuchAlgorithmException e) {
+                throw new IllegalStateException("HmacSHA256 is not available", e);
+            } catch (InvalidKeyException e) {
+                throw new IllegalStateException("Key is corrupted", e);
+            }
+
+            // Mac the package name and each of the signatures.
+            byte[] packageNameBytes = packageInfo.packageName.getBytes(StandardCharsets.UTF_8);
+            m.update(getLengthPrefix(packageNameBytes), 0, 4);
+            m.update(packageNameBytes);
+            for (int i = 0; i < packageInfo.signatures.length; i++) {
+                byte[] sig = packageInfo.signatures[i].toByteArray();
+                m.update(getLengthPrefix(sig), 0, 4);
+                m.update(sig);
+            }
+
+            // Convert result to a string for storage in settings table. Only want first 64 bits.
+            final String ssaid = ByteStringUtils.toHexString(m.doFinal()).substring(0, 16)
+                    .toLowerCase(Locale.US);
+
+            // Save the ssaid in the ssaid table.
+            final String uid = Integer.toString(packageInfo.applicationInfo.uid);
+            final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
+            final boolean success = ssaidSettings.insertSettingLocked(uid, ssaid, null, true,
+                    packageName);
+
+            if (!success) {
+                throw new IllegalStateException("Ssaid settings not accessible");
+            }
+
+            return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid);
+        }
+
+        public void syncSsaidTableOnStart() {
+            synchronized (mLock) {
+                // Verify that each user's packages and ssaid's are in sync.
+                for (UserInfo user : mUserManager.getUsers(true)) {
+                    // Get all uids for the user's packages.
+                    final List<PackageInfo> packages;
+                    try {
+                        packages = mPackageManager.getInstalledPackages(0, user.id).getList();
+                    } catch (RemoteException e) {
+                        throw new IllegalStateException("Package manager not available");
+                    }
+                    final Set<String> appUids = new HashSet<>();
+                    for (PackageInfo info : packages) {
+                        appUids.add(Integer.toString(info.applicationInfo.uid));
+                    }
+
+                    // Get all uids currently stored in the user's ssaid table.
+                    final Set<String> ssaidUids = new HashSet<>(
+                            getSettingsNamesLocked(SETTINGS_TYPE_SSAID, user.id));
+                    ssaidUids.remove(SSAID_USER_KEY);
+
+                    // Perform a set difference for the appUids and ssaidUids.
+                    ssaidUids.removeAll(appUids);
+
+                    // If there are ssaidUids left over they need to be removed from the table.
+                    final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID,
+                            user.id);
+                    for (String uid : ssaidUids) {
+                        ssaidSettings.deleteSettingLocked(uid);
+                    }
+                }
+            }
         }
 
         public List<String> getSettingsNamesLocked(int type, int userId) {
@@ -1884,6 +2156,10 @@
             final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
             ensureSettingsStateLocked(systemKey);
 
+            // Ensure secure settings loaded.
+            final int ssaidKey = makeKey(SETTINGS_TYPE_SSAID, userId);
+            ensureSettingsStateLocked(ssaidKey);
+
             // Upgrade the settings to the latest version.
             UpgradeController upgrader = new UpgradeController(userId);
             upgrader.upgradeIfNeededLocked();
@@ -1936,6 +2212,23 @@
                 }
             }
 
+            // Nuke ssaid settings.
+            final int ssaidKey = makeKey(SETTINGS_TYPE_SSAID, userId);
+            final SettingsState ssaidSettingsState = mSettingsStates.get(ssaidKey);
+            if (ssaidSettingsState != null) {
+                if (permanently) {
+                    mSettingsStates.remove(ssaidKey);
+                    ssaidSettingsState.destroyLocked(null);
+                } else {
+                    ssaidSettingsState.destroyLocked(new Runnable() {
+                        @Override
+                        public void run() {
+                            mSettingsStates.remove(ssaidKey);
+                        }
+                    });
+                }
+            }
+
             // Nuke generation tracking data
             mGenerationRegistry.onUserRemoved(userId);
         }
@@ -1979,6 +2272,8 @@
             if (settingsState == null) {
                 return null;
             }
+
+            // getSettingLocked will return non-null result
             return settingsState.getSettingLocked(name);
         }
 
@@ -2040,7 +2335,7 @@
                         Setting setting = settingsState.getSettingLocked(name);
                         if (!SettingsState.isSystemPackage(getContext(),
                                 setting.getPackageName())) {
-                            if (setting.isDefaultSystemSet()) {
+                            if (setting.isDefaultFromSystem()) {
                                 if (settingsState.resetSettingLocked(name, packageName)) {
                                     notifyForSettingsChange(key, name);
                                 }
@@ -2054,7 +2349,7 @@
                 case Settings.RESET_MODE_TRUSTED_DEFAULTS: {
                     for (String name : settingsState.getSettingNamesLocked()) {
                         Setting setting = settingsState.getSettingLocked(name);
-                        if (setting.isDefaultSystemSet()) {
+                        if (setting.isDefaultFromSystem()) {
                             if (settingsState.resetSettingLocked(name, packageName)) {
                                 notifyForSettingsChange(key, name);
                             }
@@ -2079,6 +2374,12 @@
             }
         }
 
+        public void onUidRemovedLocked(int uid) {
+            final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID,
+                    UserHandle.getUserId(uid));
+            ssaidSettings.deleteSettingLocked(Integer.toString(uid));
+        }
+
         private SettingsState peekSettingsStateLocked(int key) {
             SettingsState settingsState = mSettingsStates.get(key);
             if (settingsState != null) {
@@ -2300,6 +2601,10 @@
             return getTypeFromKey(key) == SETTINGS_TYPE_SECURE;
         }
 
+        private boolean isSsaidSettingsKey(int key) {
+            return getTypeFromKey(key) == SETTINGS_TYPE_SSAID;
+        }
+
         private File getSettingsFile(int key) {
             if (isGlobalSettingsKey(key)) {
                 final int userId = getUserIdFromKey(key);
@@ -2313,6 +2618,10 @@
                 final int userId = getUserIdFromKey(key);
                 return new File(Environment.getUserSystemDirectory(userId),
                         SETTINGS_FILE_SECURE);
+            } else if (isSsaidSettingsKey(key)) {
+                final int userId = getUserIdFromKey(key);
+                return new File(Environment.getUserSystemDirectory(userId),
+                        SETTINGS_FILE_SSAID);
             } else {
                 throw new IllegalArgumentException("Invalid settings key:" + key);
             }
@@ -2336,7 +2645,8 @@
         private int getMaxBytesPerPackageForType(int type) {
             switch (type) {
                 case SETTINGS_TYPE_GLOBAL:
-                case SETTINGS_TYPE_SECURE: {
+                case SETTINGS_TYPE_SECURE:
+                case SETTINGS_TYPE_SSAID: {
                     return SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED;
                 }
 
@@ -2374,7 +2684,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 136;
+            private static final int SETTINGS_VERSION = 137;
 
             private final int mUserId;
 
@@ -2447,6 +2757,10 @@
                 return getSettingsLocked(SETTINGS_TYPE_SECURE, userId);
             }
 
+            private SettingsState getSsaidSettingsLocked(int userId) {
+                return getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
+            }
+
             private SettingsState getSystemSettingsLocked(int userId) {
                 return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId);
             }
@@ -2776,6 +3090,53 @@
                     currentVersion = 136;
                 }
 
+                if (currentVersion == 136) {
+                    // Version 136: Store legacy SSAID for all apps currently installed on the
+                    // device as first step in migrating SSAID to be unique per application.
+
+                    final boolean isUpgrade;
+                    try {
+                        isUpgrade = mPackageManager.isUpgrade();
+                    } catch (RemoteException e) {
+                        throw new IllegalStateException("Package manager not available");
+                    }
+                    // Only retain legacy ssaid if the device is performing an OTA. After wiping
+                    // user data or first boot on a new device should use new ssaid generation.
+                    if (isUpgrade) {
+                        // Retrieve the legacy ssaid from the secure settings table.
+                        final Setting legacySsaidSetting = getSettingLocked(SETTINGS_TYPE_SECURE,
+                                userId, Settings.Secure.ANDROID_ID);
+                        if (legacySsaidSetting == null || legacySsaidSetting.isNull()
+                                || legacySsaidSetting.getValue() == null) {
+                            throw new IllegalStateException("Legacy ssaid not accessible");
+                        }
+                        final String legacySsaid = legacySsaidSetting.getValue();
+
+                        // Fill each uid with the legacy ssaid to be backwards compatible.
+                        final List<PackageInfo> packages;
+                        try {
+                            packages = mPackageManager.getInstalledPackages(0, userId).getList();
+                        } catch (RemoteException e) {
+                            throw new IllegalStateException("Package manager not available");
+                        }
+
+                        final SettingsState ssaidSettings = getSsaidSettingsLocked(userId);
+                        for (PackageInfo info : packages) {
+                            // Check if the UID already has an entry in the table.
+                            final String uid = Integer.toString(info.applicationInfo.uid);
+                            final Setting ssaid = ssaidSettings.getSettingLocked(uid);
+
+                            if (ssaid.isNull() || ssaid.getValue() == null) {
+                                // Android Id doesn't exist for this package so create it.
+                                ssaidSettings.insertSettingLocked(uid, legacySsaid, null, true,
+                                        info.packageName);
+                            }
+                        }
+                    }
+
+                    currentVersion = 137;
+                }
+
                 if (currentVersion != newVersion) {
                     Slog.wtf("SettingsProvider", "warning: upgrading settings database to version "
                             + newVersion + " left it at "
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index fecc938..2d59324 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -65,6 +65,7 @@
         }
 
         int opti = 0;
+        boolean dumpAsProto = false;
         while (opti < args.length) {
             String opt = args[opti];
             if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
@@ -74,16 +75,22 @@
             if ("-h".equals(opt)) {
                 MyShellCommand.dumpHelp(pw, true);
                 return;
+            } else if ("--proto".equals(opt)) {
+                dumpAsProto = true;
             } else {
                 pw.println("Unknown argument: " + opt + "; use -h for help");
             }
         }
 
-        long caller = Binder.clearCallingIdentity();
+        final long ident = Binder.clearCallingIdentity();
         try {
-            mProvider.dumpInternal(fd, pw, args);
+            if (dumpAsProto) {
+                mProvider.dumpProto(fd);
+            } else {
+                mProvider.dumpInternal(fd, pw, args);
+            }
         } finally {
-            Binder.restoreCallingIdentity(caller);
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -449,8 +456,9 @@
         static void dumpHelp(PrintWriter pw, boolean dumping) {
             if (dumping) {
                 pw.println("Settings provider dump options:");
-                pw.println("  [-h]");
+                pw.println("  [-h] [--proto]");
                 pw.println("  -h: print this help.");
+                pw.println("  --proto: dump as protobuf.");
             } else {
                 pw.println("Settings provider (settings) commands:");
                 pw.println("  help");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 8f37b98..a74be35 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -16,6 +16,9 @@
 
 package com.android.providers.settings;
 
+import static android.os.Process.FIRST_APPLICATION_UID;
+
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -30,6 +33,8 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.providers.settings.GlobalSettingsProto;
+import android.providers.settings.SettingsOperationProto;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
@@ -38,10 +43,14 @@
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
+
 import libcore.io.IoUtils;
 import libcore.util.Objects;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -56,8 +65,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.os.Process.FIRST_APPLICATION_UID;
-
 /**
  * This class contains the state for one type of settings. It is responsible
  * for saving the state asynchronously to an XML file after a mutation and
@@ -404,6 +411,38 @@
         }
     }
 
+    /**
+     * Dump historical operations as a proto buf.
+     *
+     * @param proto The proto buf stream to dump to
+     */
+    void dumpProtoHistoricalOperations(@NonNull ProtoOutputStream proto) {
+        synchronized (mLock) {
+            if (mHistoricalOperations == null) {
+                return;
+            }
+
+            final int operationCount = mHistoricalOperations.size();
+            for (int i = 0; i < operationCount; i++) {
+                int index = mNextHistoricalOpIdx - 1 - i;
+                if (index < 0) {
+                    index = operationCount + index;
+                }
+                HistoricalOperation operation = mHistoricalOperations.get(index);
+                long settingsOperationToken = proto.start(GlobalSettingsProto.HISTORICAL_OP);
+                proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp);
+                proto.write(SettingsOperationProto.OPERATION, operation.mOperation);
+                if (operation.mSetting != null) {
+                    // Only add the name of the setting, since we don't know the historical package
+                    // and values for it so they would be misleading to add here (all we could
+                    // add is what the current data is).
+                    proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName());
+                }
+                proto.end(settingsOperationToken);
+            }
+        }
+    }
+
     public void dumpHistoricalOperations(PrintWriter pw) {
         synchronized (mLock) {
             if (mHistoricalOperations == null) {
@@ -544,7 +583,7 @@
 
                 writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
                         setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
-                        setting.getTag(), setting.isDefaultSystemSet());
+                        setting.getTag(), setting.isDefaultFromSystem());
 
                 if (DEBUG_PERSISTENCE) {
                     Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" + setting.getValue());
@@ -763,7 +802,7 @@
         private String id;
         private String tag;
         // Whether the default is set by the system
-        private boolean defaultSystemSet;
+        private boolean defaultFromSystem;
 
         public Setting(Setting other) {
             name = other.name;
@@ -771,7 +810,7 @@
             defaultValue = other.defaultValue;
             packageName = other.packageName;
             id = other.id;
-            defaultSystemSet = other.defaultSystemSet;
+            defaultFromSystem = other.defaultFromSystem;
             tag = other.tag;
         }
 
@@ -798,7 +837,7 @@
             this.defaultValue = defaultValue;
             this.packageName = packageName;
             this.id = id;
-            this.defaultSystemSet = fromSystem;
+            this.defaultFromSystem = fromSystem;
         }
 
         public String getName() {
@@ -825,8 +864,8 @@
             return packageName;
         }
 
-        public boolean isDefaultSystemSet() {
-            return defaultSystemSet;
+        public boolean isDefaultFromSystem() {
+            return defaultFromSystem;
         }
 
         public String getId() {
@@ -854,22 +893,22 @@
             }
 
             String defaultValue = this.defaultValue;
-            boolean defaultSystemSet = this.defaultSystemSet;
+            boolean defaultFromSystem = this.defaultFromSystem;
             if (setDefault) {
                 if (!Objects.equal(value, this.defaultValue)
-                        && (!defaultSystemSet || callerSystem)) {
+                        && (!defaultFromSystem || callerSystem)) {
                     defaultValue = value;
                     // Default null means no default, so the tag is irrelevant
                     // since it is used to reset a settings subset their defaults.
                     // Also it is irrelevant if the system set the canonical default.
                     if (defaultValue == null) {
                         tag = null;
-                        defaultSystemSet = false;
+                        defaultFromSystem = false;
                     }
                 }
-                if (!defaultSystemSet && value != null) {
+                if (!defaultFromSystem && value != null) {
                     if (callerSystem) {
-                        defaultSystemSet = true;
+                        defaultFromSystem = true;
                     }
                 }
             }
@@ -879,11 +918,11 @@
                     && Objects.equal(defaultValue, this.defaultValue)
                     && Objects.equal(packageName, this.packageName)
                     && Objects.equal(tag, this.tag)
-                    && defaultSystemSet == this.defaultSystemSet) {
+                    && defaultFromSystem == this.defaultFromSystem) {
                 return false;
             }
 
-            init(name, value, tag, defaultValue, packageName, defaultSystemSet,
+            init(name, value, tag, defaultValue, packageName, defaultFromSystem,
                     String.valueOf(mNextId++));
             return true;
         }
@@ -892,7 +931,7 @@
             return "Setting{name=" + name + " value=" + value
                     + (defaultValue != null ? " default=" + defaultValue : "")
                     + " packageName=" + packageName + " tag=" + tag
-                    + " defaultSystemSet=" + defaultSystemSet + "}";
+                    + " defaultFromSystem=" + defaultFromSystem + "}";
         }
     }
 
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index 918410e..d039f03 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -9,7 +9,7 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files) \
     ../src/com/android/providers/settings/SettingsState.java
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
 
 LOCAL_PACKAGE_NAME := SettingsProviderTest
 
@@ -17,4 +17,4 @@
 
 LOCAL_CERTIFICATE := platform
 
-include $(BUILD_PACKAGE)
\ No newline at end of file
+include $(BUILD_PACKAGE)
diff --git a/packages/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk
index 0424eb0..acd552d 100644
--- a/packages/Shell/tests/Android.mk
+++ b/packages/Shell/tests/Android.mk
@@ -12,6 +12,8 @@
     android-support-test \
     mockito-target-minus-junit4 \
     ub-uiautomator \
+    junit \
+    legacy-android-test \
 
 LOCAL_PACKAGE_NAME := ShellTests
 LOCAL_INSTRUMENTATION_FOR := Shell
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index ff76c56..4b8734f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -60,6 +60,7 @@
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+    <uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" />
     <uses-permission android:name="android.permission.CONTROL_VPN" />
     <uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
     <!-- Physical hardware -->
@@ -130,6 +131,9 @@
     <!-- Assist -->
     <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
 
+    <!-- Doze mode temp whitelisting for notification dispatching. -->
+    <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
+
     <!-- Listen for keyboard attachment / detachment -->
     <uses-permission android:name="android.permission.TABLET_MODE" />
 
@@ -289,7 +293,7 @@
                   android:resumeWhilePausing="true"
                   android:screenOrientation="behind"
                   android:resizeableActivity="true"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
                   android:theme="@style/RecentsTheme.Wallpaper">
             <intent-filter>
                 <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
@@ -496,7 +500,6 @@
                   android:label="@string/accessibility_desc_work_lock"
                   android:permission="android.permission.MANAGE_USERS"
                   android:exported="false"
-                  android:launchMode="singleTop"
                   android:excludeFromRecents="true"
                   android:stateNotNeeded="true"
                   android:resumeWhilePausing="true"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java
index a9d1fa9..152dbc5 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginFragment.java
@@ -14,17 +14,13 @@
 
 package com.android.systemui.plugins;
 
-import android.annotation.Nullable;
 import android.app.Fragment;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 
 public abstract class PluginFragment extends Fragment implements Plugin {
 
-    private static final String KEY_PLUGIN_PACKAGE = "plugin_package_name";
     private Context mPluginContext;
 
     @Override
@@ -33,45 +29,17 @@
     }
 
     @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (savedInstanceState != null) {
-            Context sysuiContext = getContext();
-            Context pluginContext = recreatePluginContext(sysuiContext, savedInstanceState);
-            onCreate(sysuiContext, pluginContext);
-        }
-        if (mPluginContext == null) {
-            throw new RuntimeException("PluginFragments must call super.onCreate("
-                    + "Context sysuiContext, Context pluginContext)");
-        }
+    public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
+        return super.getLayoutInflater(savedInstanceState).cloneInContext(getContext());
     }
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putString(KEY_PLUGIN_PACKAGE, getContext().getPackageName());
     }
 
-    private Context recreatePluginContext(Context sysuiContext, Bundle savedInstanceState) {
-        final String pkg = savedInstanceState.getString(KEY_PLUGIN_PACKAGE);
-        try {
-            ApplicationInfo appInfo = sysuiContext.getPackageManager().getApplicationInfo(pkg, 0);
-            return PluginManager.getInstance(sysuiContext).getContext(appInfo, pkg);
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException("Plugin with invalid package? " + pkg, e);
-        }
-    }
-
-    @Override
-    public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
-        return super.getLayoutInflater(savedInstanceState).cloneInContext(mPluginContext);
-    }
-
-    /**
-     * Should only be called after {@link Plugin#onCreate(Context, Context)}.
-     */
     @Override
     public Context getContext() {
-        return mPluginContext != null ? mPluginContext : super.getContext();
+        return mPluginContext;
     }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
index 47b97bd..d71b6bd 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -51,6 +51,9 @@
     private static final String TAG = "PluginInstanceManager";
     private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
 
+    // must be one of the channels created in NotificationChannels.java
+    private static final String NOTIFICATION_CHANNEL_ID = "ALR";
+
     private final Context mContext;
     private final PluginListener<T> mListener;
     private final String mAction;
@@ -177,8 +180,12 @@
                     if (DEBUG) Log.d(TAG, "onPluginConnected");
                     PluginPrefs.setHasPlugins(mContext);
                     PluginInfo<T> info = (PluginInfo<T>) msg.obj;
-                    info.mPlugin.onCreate(mContext, info.mPluginContext);
-                    mListener.onPluginConnected(info.mPlugin);
+                    if (!(msg.obj instanceof PluginFragment)) {
+                        // Only call onDestroy for plugins that aren't fragments, as fragments
+                        // will get the onCreate as part of the fragment lifecycle.
+                        info.mPlugin.onCreate(mContext, info.mPluginContext);
+                    }
+                    mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
                     break;
                 case PLUGIN_DISCONNECTED:
                     if (DEBUG) Log.d(TAG, "onPluginDisconnected");
@@ -308,7 +315,7 @@
                             .setSmallIcon(icon)
                             .setWhen(0)
                             .setShowWhen(false)
-                            .setPriority(Notification.PRIORITY_MAX)
+                            .setChannel(NOTIFICATION_CHANNEL_ID)
                             .setVisibility(Notification.VISIBILITY_PUBLIC)
                             .setColor(mContext.getColor(color));
                     String label = cls;
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java
index b2f92d6..b488d2a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginListener.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.plugins;
 
+import android.content.Context;
+
 /**
  * Interface for listening to plugins being connected.
  */
@@ -24,7 +26,7 @@
      * It may also be called in the future if the plugin package changes
      * and needs to be reloaded.
      */
-    void onPluginConnected(T plugin);
+    void onPluginConnected(T plugin, Context pluginContext);
 
     /**
      * Called when a plugin has been uninstalled/updated and should be removed
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index a9874fc..e21a282 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -37,7 +37,7 @@
 
     // This should be incremented any time this class or ActivityStarter or BaseStatusBarHeader
     // change in incompatible ways.
-    public static final int VERSION = 4;
+    public static final int VERSION = 5;
 
     String TAG = "QS";
 
@@ -105,24 +105,8 @@
         public abstract void setExpansion(float headerExpansionFraction);
         public abstract void setListening(boolean listening);
         public abstract void updateEverything();
-        public abstract void setActivityStarter(ActivityStarter activityStarter);
         public abstract void setCallback(Callback qsPanelCallback);
         public abstract View getExpandView();
     }
 
-    /**
-     * An interface to start activities. This is used to as a callback from the views to
-     * {@link PhoneStatusBar} to allow custom handling for starting the activity, i.e. dismissing the
-     * Keyguard.
-     */
-    public static interface ActivityStarter {
-
-        void startPendingIntentDismissingKeyguard(PendingIntent intent);
-        void startActivity(Intent intent, boolean dismissShade);
-        void startActivity(Intent intent, boolean dismissShade, Callback callback);
-
-        interface Callback {
-            void onActivityStarted(int resultCode);
-        }
-    }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java
new file mode 100644
index 0000000..c0a48a8
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java
@@ -0,0 +1,74 @@
+
+package com.android.systemui.plugins.statusbar;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.view.View;
+
+import java.util.ArrayList;
+
+import com.android.systemui.plugins.Plugin;
+
+public interface NotificationMenuRowProvider extends Plugin {
+
+    public static final String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_MENU_ROW";
+
+    public static final int VERSION = 1;
+
+    /**
+     * Returns a list of items to populate the menu 'behind' a notification.
+     */
+    public ArrayList<MenuItem> getMenuItems(Context context);
+
+    public interface OnMenuClickListener {
+        public void onMenuClicked(View row, int x, int y, MenuItem menu);
+
+        public void onMenuReset(View row);
+    }
+
+    public interface GutsInteractionListener {
+        public void onInteraction(View view);
+
+        public void closeGuts(View view);
+    }
+
+    public interface GutsContent {
+        public void setInteractionListener(GutsInteractionListener listener);
+
+        public View getContentView();
+
+        public boolean handleCloseControls();
+    }
+
+    public interface SnoozeGutsContent extends GutsContent {
+        public void setSnoozeListener(SnoozeListener listener);
+
+        public void setStatusBarNotification(StatusBarNotification sbn);
+    }
+
+    public interface SnoozeListener {
+        public void snoozeNotification(StatusBarNotification sbn, long snoozeUntil);
+    }
+
+    public static class MenuItem {
+        public Drawable icon;
+        public String menuDescription;
+        public View menuView;
+        public GutsContent gutsContent;
+
+        public MenuItem(Drawable i, String s, GutsContent content) {
+            icon = i;
+            menuDescription = s;
+            gutsContent = content;
+        }
+
+        public View getGutsView() {
+            return gutsContent.getContentView();
+        }
+
+        public boolean onTouch(View v, int x, int y) {
+            return false;
+        }
+    }
+}
diff --git a/packages/SystemUI/res/anim/ic_dnd_disable_alpha_animation.xml b/packages/SystemUI/res/anim/ic_dnd_disable_alpha_animation.xml
deleted file mode 100644
index 21caab4..0000000
--- a/packages/SystemUI/res/anim/ic_dnd_disable_alpha_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="1.0"
-        android:valueTo="0.3"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/linear" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_dnd_total_silence_disable_alpha_animation.xml b/packages/SystemUI/res/anim/ic_dnd_total_silence_disable_alpha_animation.xml
deleted file mode 100644
index 21caab4..0000000
--- a/packages/SystemUI/res/anim/ic_dnd_total_silence_disable_alpha_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="1.0"
-        android:valueTo="0.3"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/linear" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_hotspot_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_hotspot_disable_animation_root.xml
deleted file mode 100644
index 770c401..0000000
--- a/packages/SystemUI/res/anim/ic_hotspot_disable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="1.0"
-        android:valueTo="0.3"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_hotspot_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_hotspot_enable_animation_root.xml
deleted file mode 100644
index 387ca29..0000000
--- a/packages/SystemUI/res/anim/ic_hotspot_enable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_root.xml
deleted file mode 100644
index 770c401..0000000
--- a/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="1.0"
-        android:valueTo="0.3"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_root.xml
deleted file mode 100644
index 387ca29..0000000
--- a/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_root.xml
deleted file mode 100644
index 770c401..0000000
--- a/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="1.0"
-        android:valueTo="0.3"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_root.xml
deleted file mode 100644
index 387ca29..0000000
--- a/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_root.xml
deleted file mode 100644
index 770c401..0000000
--- a/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="1.0"
-        android:valueTo="0.3"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_root.xml
deleted file mode 100644
index 387ca29..0000000
--- a/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_root.xml
deleted file mode 100644
index 770c401..0000000
--- a/packages/SystemUI/res/anim/ic_signal_location_disable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="1.0"
-        android:valueTo="0.3"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_root.xml
deleted file mode 100644
index 387ca29..0000000
--- a/packages/SystemUI/res/anim/ic_signal_location_enable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_animation_root.xml
deleted file mode 100644
index 72834c9..0000000
--- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_animation_root.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="1.0"
-        android:valueTo="0.3"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon.xml b/packages/SystemUI/res/drawable-nodpi/icon.xml
index 5e08fcb..abafb68 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2016 The Android Open Source Project
+Copyright (C) 2017 The Android Open Source Project
 
    Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -19,20 +19,22 @@
         android:viewportWidth="48.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="#00796B"
-        android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/>
+        android:pathData="M25.0,25.0m-20.5,0.0a20.5,20.5,0,1,1,41.0,0.0a20.5,20.5,0,1,1,-41.0,0.0"
+        android:fillAlpha="0.066"
+        android:fillColor="#000000"/>
     <path
-        android:fillColor="#00796B"
-        android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/>
+        android:pathData="M24.0,24.0m-20.0,0.0a20.0,20.0,0,1,1,40.0,0.0a20.0,20.0,0,1,1,-40.0,0.0"
+        android:fillColor="#FFC107"/>
     <path
-        android:fillColor="#40000000"
-        android:pathData="M44.0,35.5l-12.0,-12.0l0.0,-4.0z"/>
+        android:pathData="M44,24.2010101 L33.9004889,14.101499 L14.101499,33.9004889 L24.2010101,44 C29.2525804,43.9497929 34.2887564,41.9975027 38.1431296,38.1431296 C41.9975027,34.2887564 43.9497929,29.2525804 44,24.2010101 Z"
+        android:fillColor="#FE9F00"/>
     <path
-        android:fillColor="#40000000"
-        android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/>
+        android:pathData="M24.0,24.0m-14.0,0.0a14.0,14.0,0,1,1,28.0,0.0a14.0,14.0,0,1,1,-28.0,0.0"
+        android:fillColor="#FED44F"/>
     <path
-        android:fillColor="#4DB6AC"
-        android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/>
+        android:pathData="M37.7829445,26.469236 L29.6578482,18.3441397 L18.3441397,29.6578482 L26.469236,37.7829445 C29.1911841,37.2979273 31.7972024,36.0037754 33.9004889,33.9004889 C36.0037754,31.7972024 37.2979273,29.1911841 37.7829445,26.469236 Z"
+        android:fillColor="#FFC107"/>
+    <path
+        android:pathData="M24.0,24.0m-8.0,0.0a8.0,8.0,0,1,1,16.0,0.0a8.0,8.0,0,1,1,-16.0,0.0"
+        android:fillColor="#FFFFFF"/>
 </vector>
-
-
diff --git a/packages/SystemUI/res/drawable/ic_access_alarms_small.xml b/packages/SystemUI/res/drawable/ic_access_alarms_small.xml
index 994274a..b94cc8e 100644
--- a/packages/SystemUI/res/drawable/ic_access_alarms_small.xml
+++ b/packages/SystemUI/res/drawable/ic_access_alarms_small.xml
@@ -18,9 +18,9 @@
     android:height="16dp"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0"
-    android:tint="?android:attr/colorControlNormal">
+    android:tint="?android:attr/textColorTertiary">
 
     <path
-        android:fillColor="#64ffffff"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M22.0,5.7l-4.6,-3.9l-1.3,1.5l4.6,3.9L22.0,5.7zM7.9,3.4L6.6,1.9L2.0,5.7l1.3,1.5L7.9,3.4zM12.5,8.0L11.0,8.0l0.0,6.0l4.7,2.9l0.8,-1.2l-4.0,-2.4L12.5,8.0zM12.0,4.0c-5.0,0.0 -9.0,4.0 -9.0,9.0c0.0,5.0 4.0,9.0 9.0,9.0s9.0,-4.0 9.0,-9.0C21.0,8.0 17.0,4.0 12.0,4.0zM12.0,20.0c-3.9,0.0 -7.0,-3.1 -7.0,-7.0c0.0,-3.9 3.1,-7.0 7.0,-7.0c3.9,0.0 7.0,3.1 7.0,7.0C19.0,16.9 15.9,20.0 12.0,20.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_dnd_disable_animation.xml b/packages/SystemUI/res/drawable/ic_dnd_disable_animation.xml
index 13ed767..d755481 100644
--- a/packages/SystemUI/res/drawable/ic_dnd_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_dnd_disable_animation.xml
@@ -23,7 +23,4 @@
     <target
         android:name="bar01_0"
         android:animation="@anim/ic_dnd_disable_bar01_0_animation" />
-    <target
-        android:name="ic_dnd_disable"
-        android:animation="@anim/ic_dnd_disable_alpha_animation" />
 </animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_dnd_total_silence_disable_animation.xml b/packages/SystemUI/res/drawable/ic_dnd_total_silence_disable_animation.xml
index c0b2d69..f796d62 100644
--- a/packages/SystemUI/res/drawable/ic_dnd_total_silence_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_dnd_total_silence_disable_animation.xml
@@ -23,7 +23,4 @@
     <target
         android:name="outer_ring_merged"
         android:animation="@anim/ic_dnd_total_silence_disable_outer_ring_merged_animation" />
-    <target
-        android:name="ic_dnd_total_silence_disable"
-        android:animation="@anim/ic_dnd_total_silence_disable_alpha_animation" />
 </animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_disable_animation.xml b/packages/SystemUI/res/drawable/ic_hotspot_disable_animation.xml
index 694c23f..e446a32 100644
--- a/packages/SystemUI/res/drawable/ic_hotspot_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_hotspot_disable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_hotspot_disable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_hotspot_disable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_hotspot_disable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_enable.xml b/packages/SystemUI/res/drawable/ic_hotspot_enable.xml
index 31e7fe1..7a99630 100644
--- a/packages/SystemUI/res/drawable/ic_hotspot_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_hotspot_enable.xml
@@ -16,12 +16,10 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="root"
-    android:alpha="0.3"
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportWidth="48" >
     <group
         android:name="ic_hotspot"
         android:translateX="23.97354"
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_enable_animation.xml b/packages/SystemUI/res/drawable/ic_hotspot_enable_animation.xml
index c5187dd..945e42f 100644
--- a/packages/SystemUI/res/drawable/ic_hotspot_enable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_hotspot_enable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_hotspot_enable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_hotspot_enable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_hotspot_enable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
index 83f46e5..7641998 100644
--- a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
+++ b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
@@ -19,9 +19,7 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:alpha="0.25"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportWidth="48" >
     <group
         android:name="ic_hotspot"
         android:translateX="23.97354"
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_disable_animation.xml b/packages/SystemUI/res/drawable/ic_invert_colors_disable_animation.xml
index 476c00d..aeda0a5 100644
--- a/packages/SystemUI/res/drawable/ic_invert_colors_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_disable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_invert_colors_disable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_invert_colors_disable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_invert_colors_disable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_enable_animation.xml b/packages/SystemUI/res/drawable/ic_invert_colors_enable_animation.xml
index 879066c..85928ac 100644
--- a/packages/SystemUI/res/drawable/ic_invert_colors_enable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_enable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_invert_colors_enable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_invert_colors_enable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_invert_colors_enable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_left.xml b/packages/SystemUI/res/drawable/ic_left.xml
new file mode 100644
index 0000000..cea4cfc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_left.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.41,16.09l-4.58,-4.59 4.58,-4.59L14.0,5.5l-6.0,6.0 6.0,6.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_menu.xml b/packages/SystemUI/res/drawable/ic_menu.xml
new file mode 100644
index 0000000..994bc5e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_menu.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.0,18.0l18.0,0.0l0.0,-2.0L3.0,16.0l0.0,2.0zm0.0,-5.0l18.0,0.0l0.0,-2.0L3.0,11.0l0.0,2.0zm0.0,-7.0l0.0,2.0l18.0,0.0L21.0,6.0L3.0,6.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_battery_saver.xml b/packages/SystemUI/res/drawable/ic_qs_battery_saver.xml
new file mode 100644
index 0000000..7b29740
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_battery_saver.xml
@@ -0,0 +1,28 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:autoMirrored="true"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:pathData="M5,3
+        l3.5,0 l0,-1.5 l7,0 l0,1.5 l3.5,0 l0,19.5 l-14,0z
+        M10.5,8.5 l0,3 l-3,0 l0,3 l3,0 l0,3 l3,0 l0,-3 l3,0 l0,-3 l-3,0 l0,-3 z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
index 36eb418..3148a2c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_off.xml
@@ -21,6 +21,6 @@
         android:tint="?android:attr/colorControlNormal">
 
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M26.0,11.8l3.8,3.8l-3.2,3.2l2.8,2.8l6.0,-6.0L24.0,4.2l-2.0,0.0l0.0,10.1l4.0,4.0L26.0,11.8zM10.8,8.2L8.0,11.0l13.2,13.2L10.0,35.3l2.8,2.8L22.0,29.0l0.0,15.2l2.0,0.0l8.6,-8.6l4.6,4.6l2.8,-2.8L10.8,8.2zM26.0,36.5L26.0,29.0l3.8,3.8L26.0,36.5z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml b/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
index 0fdbe1f..06a0886 100644
--- a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_off.xml
@@ -17,10 +17,9 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="48.0">
 
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0zM2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
index d2e9eb2..794eb9e 100644
--- a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml
@@ -17,8 +17,7 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="48.0">
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_circle.xml b/packages/SystemUI/res/drawable/ic_qs_circle.xml
index 57223cf..990cec6 100644
--- a/packages/SystemUI/res/drawable/ic_qs_circle.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_circle.xml
@@ -14,14 +14,13 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="64dp"
-        android:height="64dp"
+        android:width="40dp"
+        android:height="40dp"
         android:viewportWidth="2.2"
         android:viewportHeight="2.2">
 
     <path
-        android:strokeColor="#4DFFFFFF"
-        android:strokeWidth=".05"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M.1,1.1
         c0,.55  .45,1   1,1
         c.55,0  1,-.45  1,-1
diff --git a/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml
index d11b6f4..6264484 100644
--- a/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="40.0"
-        android:viewportHeight="40.0">
+        android:width="17dp"
+        android:height="17.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M19.0,6.41L17.59,5.0 12.0,10.59 6.41,5.0 5.0,6.41 10.59,12.0 5.0,17.59 6.41,19.0 12.0,13.41 17.59,19.0 19.0,17.59 13.41,12.0z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_off.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_off.xml
index 164a557..9168dbc 100644
--- a/packages/SystemUI/res/drawable/ic_qs_dnd_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_dnd_off.xml
@@ -17,9 +17,7 @@
     android:height="64dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
-    android:alpha=".3"
-    android:width="64dp"
-    android:tint="?android:attr/colorControlNormal" >
+    android:width="64dp" >
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
index 7e2eca8..f4c20a9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
@@ -17,8 +17,7 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="48.0"
-        android:viewportHeight="48.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="48.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M24.0,4.0C12.95,4.0 4.0,12.95 4.0,24.0s8.95,20.0 20.0,20.0 20.0,-8.95 20.0,-20.0S35.05,4.0 24.0,4.0zm10.0,22.0L14.0,26.0l0.0,-4.0l20.0,0.0l0.0,4.0z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml
index 5f4fa11..fb26c09 100644
--- a/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml
@@ -17,8 +17,7 @@
         android:width="64dp"
         android:height="64dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_minus.xml b/packages/SystemUI/res/drawable/ic_qs_minus.xml
index 147a94b..6a3410a 100644
--- a/packages/SystemUI/res/drawable/ic_qs_minus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_minus.xml
@@ -17,8 +17,7 @@
     android:height="24.0dp"
     android:viewportHeight="48.0"
     android:viewportWidth="48.0"
-    android:width="24.0dp"
-    android:tint="?android:attr/colorControlNormal" >
+    android:width="24.0dp" >
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml
new file mode 100644
index 0000000..558f3d0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml
@@ -0,0 +1,31 @@
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M4 20h16V4H4v16z" />
+    <path
+        android:fillColor="#4DFFFFFF"
+        android:pathData="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1 .9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0
+18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6 .35 -1 .98-1 1.72 0 1.1 .9 2 2
+2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml b/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml
new file mode 100644
index 0000000..becb18a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml
@@ -0,0 +1,31 @@
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M4 20h16V4H4v16z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1 .9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0
+18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6 .35 -1 .98-1 1.72 0 1.1 .9 2 2
+2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml b/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
index b99dc03..aaca663 100644
--- a/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_night_display_off.xml
@@ -17,12 +17,10 @@
     android:width="64dp"
     android:height="64dp"
     android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:alpha="0.3"
-    android:tint="?android:attr/colorControlNormal">
+    android:viewportHeight="24">
 
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="#FFF"
         android:pathData="M6,12c0,5.5,4.5,10,10,10c1,0,2-0.2,3-0.5c-4.1-1.3-7-5.1-7-9.5s2.9-8.3,7-9.5C18.1,2.2,17.1,2,16,2C10.5,2,6,6.5,6,12z" />
 
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml b/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
index d875592..aaca663 100644
--- a/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_night_display_on.xml
@@ -17,11 +17,10 @@
     android:width="64dp"
     android:height="64dp"
     android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:tint="?android:attr/colorControlNormal">
+    android:viewportHeight="24">
 
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="#FFF"
         android:pathData="M6,12c0,5.5,4.5,10,10,10c1,0,2-0.2,3-0.5c-4.1-1.3-7-5.1-7-9.5s2.9-8.3,7-9.5C18.1,2.2,17.1,2,16,2C10.5,2,6,6.5,6,12z" />
 
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_no_sim.xml b/packages/SystemUI/res/drawable/ic_qs_no_sim.xml
index 2d831b2..69869fe 100644
--- a/packages/SystemUI/res/drawable/ic_qs_no_sim.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_no_sim.xml
@@ -17,10 +17,9 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
 
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M19.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-7.0,0.0L7.7,5.3L19.0,16.7L19.0,5.0zM3.7,3.9L2.4,5.2L5.0,7.8L5.0,19.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c0.4,0.0 0.7,-0.1 1.0,-0.3l1.9,1.9l1.3,-1.3L3.7,3.9z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_plus.xml b/packages/SystemUI/res/drawable/ic_qs_plus.xml
index 1f254d1..393f51c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_plus.xml
@@ -17,8 +17,7 @@
     android:height="24.0dp"
     android:viewportHeight="48.0"
     android:viewportWidth="48.0"
-    android:width="24.0dp"
-    android:tint="?android:attr/colorControlNormal" >
+    android:width="24.0dp" >
 
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_0.xml b/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
index 0673848..b78d3bf 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_0.xml
@@ -18,8 +18,7 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillAlpha="0.3"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_1.xml b/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
index fbf9e71..e055de7 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_1.xml
@@ -18,8 +18,7 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M10.0,14.6l-8.0,8.0l8.0,0.0l0,-8z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml b/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
index 3a55623..195849a 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="16.0dp"
+        android:width="32.0dp"
         android:height="32dp"
         android:viewportWidth="12.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="12.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M3.500000,11.000000L1.800000,11.000000L1.800000,4.400000L0.200000,5.100000L0.200000,3.700000l3.100000,-1.300000l0.200000,0.000000L3.500000,11.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_2.xml b/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
index e9f5a0b..8a48817 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_2.xml
@@ -18,8 +18,7 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M14.0,10.6l-12.0,12.0l12.0,0.0L14.0,10.6z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_3.xml b/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
index 769d648..39cc94c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_3.xml
@@ -18,8 +18,7 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillAlpha="0.3"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
index ddd8065..68c4307 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="17.333334dp"
+        android:width="32dp"
         android:height="32dp"
         android:viewportWidth="13.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="13.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.000000,6.000000l0.800000,0.000000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000s0.200000,-0.500000 0.200000,-0.900000c0.000000,-0.300000 -0.100000,-0.600000 -0.200000,-0.800000S3.200000,3.700000 2.900000,3.700000C2.700000,3.700000 2.500000,3.800000 2.300000,4.000000S2.100000,4.400000 2.100000,4.700000L0.500000,4.700000C0.500000,4.000000 0.700000,3.400000 1.100000,3.000000s1.000000,-0.600000 1.700000,-0.600000c0.800000,0.000000 1.400000,0.200000 1.900000,0.600000s0.700000,1.000000 0.700000,1.800000c0.000000,0.400000 -0.100000,0.700000 -0.300000,1.100000S4.600000,6.500000 4.300000,6.600000C4.700000,6.800000 5.000000,7.100000 5.200000,7.400000s0.300000,0.700000 0.300000,1.200000c0.000000,0.800000 -0.200000,1.400000 -0.700000,1.800000s-1.100000,0.700000 -1.900000,0.700000c-0.700000,0.000000 -1.300000,-0.200000 -1.800000,-0.600000s-0.700000,-1.000000 -0.700000,-1.800000L2.000000,8.700000C2.000000,9.000000 2.100000,9.300000 2.300000,9.500000s0.400000,0.300000 0.600000,0.300000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000S3.900000,9.000000 3.900000,8.600000c0.000000,-0.500000 -0.100000,-0.800000 -0.300000,-1.000000S3.200000,7.300000 2.800000,7.300000L2.000000,7.300000L2.000000,6.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
index 1bec1b8..012e95e 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4.xml
@@ -18,8 +18,7 @@
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M14.1,14.1l7.9,0.0 0.0,-11.5 -20.0,20.0 12.1,0.0z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
index 8e1f8eb..61ecc9c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="16.0dp"
+        android:width="32.0dp"
         android:height="32dp"
         android:viewportWidth="12.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="12.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M4.600000,7.800000l0.700000,0.000000l0.000000,1.300000L4.600000,9.100000L4.600000,11.000000L3.000000,11.000000L3.000000,9.200000L0.100000,9.200000L0.000000,8.100000L3.000000,2.500000l1.700000,0.000000L4.700000,7.800000zM1.600000,7.800000L3.000000,7.800000l0.000000,-3.000000L2.900000,5.000000L1.600000,7.800000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
index e0c6b68..54c52bf 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
+        android:width="32.0dp"
+        android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M4.6,7.8l0.7,0.0l0.0,1.3L4.6,9.1L4.6,11.0L3.0,11.0L3.0,9.2L0.1,9.2L0.0,8.2l3.0,-5.7l1.7,0.0L4.6,7.8L4.6,7.8zM1.7,7.8L3.0,7.8l0.0,-3.0L2.9,5.0L1.7,7.8z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml b/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml
index 1c068e5..96e2fd4 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_carrier_network_change.xml
@@ -17,8 +17,7 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:name="dot1"
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml
index 0a85392..dd5843d 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml
@@ -18,12 +18,11 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M21.799999,22.299999l-1.199999,-1.299999 0.000000,0.000000 -9.600000,-10.000000 0.000000,0.000000 -6.400000,-6.700000 -1.300000,1.300000 6.400000,6.700000 -8.700000,8.700000 16.900000,0.000000 2.600000,2.700001z"/>
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M21.000000,1.000000l-8.600000,8.600000 8.600000,9.100000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
index 4c90421..dd0f271 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="6.6666665dp"
+        android:width="32dp"
         android:height="32dp"
         android:viewportWidth="5.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="5.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M4.400000,7.300000L1.700000,7.300000l0.000000,2.400000l3.300000,0.000000L5.000000,11.000000L0.000000,11.000000L0.000000,2.500000l4.900000,0.000000l0.000000,1.300000L1.700000,3.800000l0.000000,2.100000l2.800000,0.000000L4.500000,7.300000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_0.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_0.xml
index db4df76..326373d 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_0.xml
@@ -18,8 +18,7 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml
index 6e5439d..8baa4eb 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_1.xml
@@ -18,8 +18,7 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.0,22.0l20.0,0.0 0.0,-20.0z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_2.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_2.xml
index 194edc3..bf19a71 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_2.xml
@@ -18,8 +18,7 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_3.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_3.xml
index b57af5c..01839e85 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_3.xml
@@ -18,8 +18,7 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_full_4.xml b/packages/SystemUI/res/drawable/ic_qs_signal_full_4.xml
index 7d754a8..48151ad 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_full_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_full_4.xml
@@ -18,8 +18,7 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
index 64aadf9..3b47c0d 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="9.333333dp"
+        android:width="32dp"
         android:height="32dp"
         android:viewportWidth="7.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="7.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M6.500000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S0.700000,9.000000 0.700000,7.900000L0.700000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000s1.200000,-0.800000 2.100000,-0.800000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000L4.700000,5.200000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S4.000000,3.700000 3.600000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S2.300000,5.000000 2.300000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L4.700000,7.800000L3.500000,7.800000L3.500000,6.600000l2.900000,0.000000L6.400000,9.900000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
index 31918a9..d694e61 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="8.0dp"
+        android:width="32.0dp"
         android:height="32dp"
         android:viewportWidth="6.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="6.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M6.000000,11.000000L4.400000,11.000000L4.400000,7.500000L1.700000,7.500000L1.700000,11.000000L0.000000,11.000000L0.000000,2.500000l1.700000,0.000000l0.000000,3.700000l2.700000,0.000000L4.400000,2.500000L6.000000,2.500000L6.000000,11.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
index 4122b76..236fdac 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
@@ -17,8 +17,7 @@
         android:width="6.0dp"
         android:height="32dp"
         android:viewportWidth="6.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M6.000000,15.700000l-3.000000,5.599999 -3.000000,-5.599999z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
index 8766075..a381d03e 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="17.333334dp"
+        android:width="32dp"
         android:height="32dp"
         android:viewportWidth="13.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="13.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.000000,9.700000l2.000000,0.000000L4.000000,11.000000L0.300000,11.000000L0.300000,2.500000L2.000000,2.500000L2.000000,9.700000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
index 5ff7d85..3bed28a 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
@@ -14,11 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
+        android:width="32.0dp"
+        android:height="32.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.0,9.7l2.0,0.0L4.0,11.0L0.4,11.0L0.4,2.5L2.0,2.5L2.0,9.7z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml b/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml
index 4e65004..c8c857c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_no_signal.xml
@@ -18,9 +18,8 @@
         android:width="32dp"
         android:height="32dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M2.000000,22.000000l20.000000,0.000000L22.000000,2.000000L2.000000,22.000000zM20.000000,20.000000L6.800000,20.000000L20.000000,6.800000L20.000000,20.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
index a3823ae..c510972 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
@@ -17,8 +17,7 @@
         android:width="6.0dp"
         android:height="32dp"
         android:viewportWidth="6.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M0.000000,13.700000l3.000000,-5.700000 3.000000,5.700000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_r.xml b/packages/SystemUI/res/drawable/ic_qs_signal_r.xml
deleted file mode 100644
index 2c10dc3f..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_r.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="8.0dp"
-        android:height="32dp"
-        android:viewportWidth="6.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M2.800000,7.900000l-1.000000,0.000000L1.800000,11.000000L0.200000,11.000000L0.200000,2.500000l2.700000,0.000000c0.900000,0.000000 1.500000,0.200000 2.000000,0.700000s0.700000,1.100000 0.700000,1.900000c0.000000,0.600000 -0.100000,1.100000 -0.300000,1.500000S4.800000,7.200000 4.400000,7.400000l1.500000,3.500000L5.900000,11.000000L4.100000,11.000000L2.800000,7.900000zM1.800000,6.500000l1.100000,0.000000c0.400000,0.000000 0.600000,-0.100000 0.800000,-0.400000S4.000000,5.600000 4.000000,5.200000c0.000000,-0.400000 -0.100000,-0.800000 -0.300000,-1.000000S3.300000,3.800000 2.900000,3.800000L1.800000,3.800000L1.800000,6.500000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_vpn.xml
index e7ef02a..cf24e12 100644
--- a/packages/SystemUI/res/drawable/ic_qs_vpn.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_vpn.xml
@@ -19,6 +19,6 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M12.700000,10.000000c-0.800000,-2.300000 -3.000000,-4.000000 -5.700000,-4.000000c-3.300000,0.000000 -6.000000,2.700000 -6.000000,6.000000s2.700000,6.000000 6.000000,6.000000c2.600000,0.000000 4.800000,-1.700000 5.700000,-4.000000L17.000000,14.000000l0.000000,4.000000l4.000000,0.000000l0.000000,-4.000000l2.000000,0.000000l0.000000,-4.000000L12.700000,10.000000zM7.000000,14.000000c-1.100000,0.000000 -2.000000,-0.900000 -2.000000,-2.000000c0.000000,-1.100000 0.900000,-2.000000 2.000000,-2.000000s2.000000,0.900000 2.000000,2.000000C9.000000,13.100000 8.100000,14.000000 7.000000,14.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
index fe963b1..e6f9292 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_0.xml
@@ -17,8 +17,7 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M13.8,12.2l5.7,0.0L23.6,7.0C23.2,6.7 18.7,3.0 12.0,3.0C5.3,3.0 0.8,6.7 0.4,7.0L12.0,21.5l0.0,0.0l0.0,0.0l1.8,-2.2L13.8,12.2z"
         android:fillAlpha="0.3"
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
index 82d2be2..d423ccb 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_1.xml
@@ -17,8 +17,7 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M13.8,13.2c-0.1,0.0 -0.3,-0.1 -0.4,-0.1c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,0.0 -0.6,-0.1 -0.9,-0.1c0.0,0.0 0.0,0.0 -0.1,0.0c0.0,0.0 0.0,0.0 0.0,0.0s0.0,0.0 0.0,0.0c0.0,0.0 0.0,0.0 -0.1,0.0c-0.3,0.0 -0.6,0.0 -0.9,0.1c-0.1,0.0 -0.3,0.0 -0.4,0.1c-0.2,0.0 -0.3,0.1 -0.5,0.1c-0.2,0.0 -0.3,0.1 -0.5,0.1c-0.1,0.0 -0.1,0.0 -0.2,0.1c-1.6,0.5 -2.7,1.3 -2.8,1.5l5.3,6.6l0.0,0.0l0.0,0.0l0.0,0.0l0.0,0.0l1.8,-2.2L13.700002,13.2z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
index f30ba76..1982130 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_2.xml
@@ -17,8 +17,7 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M13.8,12.2l4.9,0.0c-1.0,-0.7 -3.4,-2.2 -6.7,-2.2c-4.1,0.0 -6.9,2.2 -7.2,2.5l7.2,9.0l0.0,0.0l0.0,0.0l1.8,-2.2L13.800001,12.2z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
index 8a17083..b350111 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_3.xml
@@ -17,8 +17,7 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M13.8,12.2l5.7,0.0l1.0,-1.2C20.0,10.6 16.8,8.0 12.0,8.0s-8.0,2.6 -8.5,3.0L12.0,21.5l0.0,0.0l0.0,0.0l1.8,-2.2L13.8,12.2z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
index fcd57d0..136a004 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_4.xml
@@ -17,8 +17,7 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:pathData="M13.8,12.2l5.7,0.0L23.6,7.0C23.2,6.7 18.7,3.0 12.0,3.0C5.3,3.0 0.8,6.7 0.4,7.0L12.0,21.5l0.0,0.0l0.0,0.0l1.8,-2.2L13.8,12.2z"
         android:fillColor="#FFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
index bbff2e1..bed37b1 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
@@ -17,12 +17,11 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M17.500000,16.500000L5.800000,3.400000c0.000000,0.000000 0.000000,0.000000 0.000000,0.000000l-2.700000,-3.000000L1.600000,1.800000l2.200000,2.500000c-2.000000,1.000000 -3.200000,2.000000 -3.400000,2.200000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l3.200000,-3.900000l2.400000,2.700000l1.500000,-1.400000L17.500000,16.500000L17.500000,16.500000z"/>
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000c-1.900000,0.000000 -3.600000,0.300000 -5.200000,0.700000L18.700001,15.000000L25.600000,6.500000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
index 071892a..2dcdb71 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
@@ -17,8 +17,7 @@
         android:width="26.0dp"
         android:height="24.0dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M21.0,8.5
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
index c0e1037..1bc7438 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_0.xml
@@ -17,8 +17,7 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
index f609295..5856115 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_1.xml
@@ -17,8 +17,7 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.100000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.500000,6.500000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
index f44b303..4a5e1f8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_2.xml
@@ -17,8 +17,7 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
index 850f5b9..965442d 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_3.xml
@@ -17,8 +17,7 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#4DFFFFFF"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
index 8ccc1fd9..b29d3f9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_full_4.xml
@@ -17,8 +17,7 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_no_network.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_no_network.xml
index 45cfc1c..e59e7f4 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_no_network.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_no_network.xml
@@ -17,9 +17,8 @@
         android:width="32.0dp"
         android:height="29.5dp"
         android:viewportWidth="26.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorControlNormal">
+        android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="#FFFFFFFF"
         android:pathData="M13.000000,2.000000C7.700000,2.000000 3.700000,3.900000 0.400000,6.400000L13.000000,22.000000L25.600000,6.500000C22.299999,4.000000 18.299999,2.000000 13.000000,2.000000zM13.000000,18.600000L3.300000,7.000000l0.000000,0.000000l0.000000,0.000000C6.000000,5.300000 8.700000,4.000000 13.000000,4.000000s7.000000,1.400000 9.700000,3.000000l0.000000,0.000000l0.000000,0.000000L13.000000,18.600000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_remove.xml b/packages/SystemUI/res/drawable/ic_remove.xml
new file mode 100644
index 0000000..75b2793
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_remove.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.0,13.0L5.0,13.0l0.0,-2.0l14.0,0.0l0.0,2.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_right.xml b/packages/SystemUI/res/drawable/ic_right.xml
new file mode 100644
index 0000000..35699f73
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_right.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10.0,5.75l6.0,6.0 -6.0,6.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_settings_20dp.xml b/packages/SystemUI/res/drawable/ic_settings_20dp.xml
index 45ee94f..3170f86 100644
--- a/packages/SystemUI/res/drawable/ic_settings_20dp.xml
+++ b/packages/SystemUI/res/drawable/ic_settings_20dp.xml
@@ -17,8 +17,7 @@
     android:width="20dp"
     android:height="20dp"
     android:viewportWidth="24.0"
-    android:viewportHeight="24.0"
-    android:tint="?android:attr/colorControlNormal">
+    android:viewportHeight="24.0">
     <path
         android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z"
         android:fillColor="#ffffffff" />
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane_disable.xml b/packages/SystemUI/res/drawable/ic_signal_airplane_disable.xml
index 2ff0de4..09a67e1 100644
--- a/packages/SystemUI/res/drawable/ic_signal_airplane_disable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane_disable.xml
@@ -20,8 +20,7 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportWidth="48" >
     <group
         android:name="ic_signal_airplane"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane_disable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_airplane_disable_animation.xml
index 4fe585c..56ae4aa 100644
--- a/packages/SystemUI/res/drawable/ic_signal_airplane_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane_disable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_signal_airplane_disable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_signal_airplane_disable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_signal_airplane_disable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane_enable.xml b/packages/SystemUI/res/drawable/ic_signal_airplane_enable.xml
index 9987279..b7ac0134 100644
--- a/packages/SystemUI/res/drawable/ic_signal_airplane_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane_enable.xml
@@ -16,12 +16,10 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="root"
-    android:alpha="0.3"
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportWidth="48" >
     <group
         android:name="ic_signal_airplane"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane_enable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_airplane_enable_animation.xml
index d64b199..87dfba9 100644
--- a/packages/SystemUI/res/drawable/ic_signal_airplane_enable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane_enable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_signal_airplane_enable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_signal_airplane_enable_animation_root" />
-    <target
         android:name="ic_signal_airplane"
         android:animation="@anim/ic_signal_airplane_enable_animation_ic_signal_airplane" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml
index 542797a..35844b7 100644
--- a/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml
@@ -20,8 +20,7 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportWidth="48" >
     <group
         android:name="ic_signal_flashlight"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_disable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable_animation.xml
index 61e5287..e228b7c 100644
--- a/packages/SystemUI/res/drawable/ic_signal_flashlight_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_signal_flashlight_disable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_signal_flashlight_disable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_signal_flashlight_disable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml
index a5ba05b..c2215f1 100644
--- a/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml
@@ -16,12 +16,10 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="root"
-    android:alpha="0.3"
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportWidth="48" >
     <group
         android:name="ic_signal_flashlight"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_enable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable_animation.xml
index 4bd9188..220c65e 100644
--- a/packages/SystemUI/res/drawable/ic_signal_flashlight_enable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_signal_flashlight_enable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_signal_flashlight_enable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_signal_flashlight_enable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_disable.xml b/packages/SystemUI/res/drawable/ic_signal_location_disable.xml
index e36f270..439851d 100644
--- a/packages/SystemUI/res/drawable/ic_signal_location_disable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_location_disable.xml
@@ -20,8 +20,7 @@
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportWidth="48" >
     <group
         android:name="ic_signal_location"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_disable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_location_disable_animation.xml
index a598b2d..0e9d1cb 100644
--- a/packages/SystemUI/res/drawable/ic_signal_location_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_location_disable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_signal_location_disable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_signal_location_disable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_signal_location_disable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_enable.xml b/packages/SystemUI/res/drawable/ic_signal_location_enable.xml
index 46a72bc..d4b8673 100644
--- a/packages/SystemUI/res/drawable/ic_signal_location_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_location_enable.xml
@@ -16,12 +16,10 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="root"
-    android:alpha="0.3"
     android:height="48dp"
     android:width="48dp"
     android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportWidth="48" >
     <group
         android:name="ic_signal_location"
         android:translateX="21.9995"
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_enable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_location_enable_animation.xml
index 127af29..9f1d917 100644
--- a/packages/SystemUI/res/drawable/ic_signal_location_enable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_location_enable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_signal_location_enable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_signal_location_enable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_signal_location_enable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_signal_workmode_disable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_workmode_disable_animation.xml
index a15a127..9c23126 100644
--- a/packages/SystemUI/res/drawable/ic_signal_workmode_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_workmode_disable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_signal_workmode_disable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_signal_workmode_disable_animation_root" />
-    <target
         android:name="mask"
         android:animation="@anim/ic_signal_workmode_disable_animation_mask" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_signal_workmode_enable.xml b/packages/SystemUI/res/drawable/ic_signal_workmode_enable.xml
index ad14505..f962594 100644
--- a/packages/SystemUI/res/drawable/ic_signal_workmode_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_workmode_enable.xml
@@ -16,7 +16,6 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="root"
-    android:alpha="0.3"
     android:height="42dp"
     android:width="42dp"
     android:viewportHeight="42"
diff --git a/packages/SystemUI/res/drawable/ic_signal_workmode_enable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_workmode_enable_animation.xml
index 41d2b42..04ddfad 100644
--- a/packages/SystemUI/res/drawable/ic_signal_workmode_enable_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_workmode_enable_animation.xml
@@ -17,9 +17,6 @@
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/ic_signal_workmode_enable" >
     <target
-        android:name="root"
-        android:animation="@anim/ic_signal_workmode_enable_animation_root" />
-    <target
         android:name="ic_signal_briefcase"
         android:animation="@anim/ic_signal_workmode_enable_animation_ic_signal_briefcase" />
     <target
diff --git a/packages/SystemUI/res/drawable/ic_snooze.xml b/packages/SystemUI/res/drawable/ic_snooze.xml
new file mode 100644
index 0000000..b0b03a9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_snooze.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"
+        android:fillColor="#757575"/>
+    <path
+        android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"
+        android:fillColor="#757575"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_accessibility.xml b/packages/SystemUI/res/drawable/ic_volume_accessibility.xml
new file mode 100644
index 0000000..657efaa
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_accessibility.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32dp"
+        android:height="32dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M20.5,6c-2.61,0.7 -5.67,1 -8.5,1s-5.89,-0.3 -8.5,-1L3,8c1.86,0.5 4,0.83 6,1v13h2v-6h2v6h2V9c2,-0.17 4.14,-0.5 6,-1l-0.5,-2zM12,6c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/minor_a_b.xml b/packages/SystemUI/res/drawable/minor_a_b.xml
index 0622aac..4bb330c 100644
--- a/packages/SystemUI/res/drawable/minor_a_b.xml
+++ b/packages/SystemUI/res/drawable/minor_a_b.xml
@@ -17,12 +17,10 @@
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="minor_a_b"
-    android:alpha="0.3"
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportHeight="8" >
     <group
         android:name="dot_02"
         android:translateX="3.25"
diff --git a/packages/SystemUI/res/drawable/minor_b_a.xml b/packages/SystemUI/res/drawable/minor_b_a.xml
index ecb4341..f61deec 100644
--- a/packages/SystemUI/res/drawable/minor_b_a.xml
+++ b/packages/SystemUI/res/drawable/minor_b_a.xml
@@ -17,12 +17,10 @@
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="minor_b_a"
-    android:alpha="0.3"
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportHeight="8" >
     <group
         android:name="dot_02"
         android:translateX="8"
diff --git a/packages/SystemUI/res/drawable/minor_b_c.xml b/packages/SystemUI/res/drawable/minor_b_c.xml
index 7f59e34..867af6d 100644
--- a/packages/SystemUI/res/drawable/minor_b_c.xml
+++ b/packages/SystemUI/res/drawable/minor_b_c.xml
@@ -17,12 +17,10 @@
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="minor_b_c"
-    android:alpha="0.3"
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportHeight="8" >
     <group
         android:name="dot_02"
         android:translateX="8"
diff --git a/packages/SystemUI/res/drawable/minor_c_b.xml b/packages/SystemUI/res/drawable/minor_c_b.xml
index 97309bc..b2e33cf 100644
--- a/packages/SystemUI/res/drawable/minor_c_b.xml
+++ b/packages/SystemUI/res/drawable/minor_c_b.xml
@@ -17,12 +17,10 @@
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:name="minor_c_b"
-    android:alpha="0.3"
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8"
-    android:tint="?android:attr/colorControlNormal" >
+    android:viewportHeight="8" >
     <group
         android:name="dot_02"
         android:translateX="12.75"
diff --git a/packages/SystemUI/res/drawable/qs_dual_tile_caret.xml b/packages/SystemUI/res/drawable/qs_dual_tile_caret.xml
new file mode 100644
index 0000000..0b2bd22
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dual_tile_caret.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_ic_wifi_lock.xml b/packages/SystemUI/res/drawable/qs_ic_wifi_lock.xml
index 2ac223b..b7da30b 100644
--- a/packages/SystemUI/res/drawable/qs_ic_wifi_lock.xml
+++ b/packages/SystemUI/res/drawable/qs_ic_wifi_lock.xml
@@ -17,7 +17,8 @@
     android:width="24.0dp"
     android:height="24.0dp"
     android:viewportWidth="72.0"
-    android:viewportHeight="72.0">
+    android:viewportHeight="72.0"
+    android:tint="?android:attr/textColorPrimary">
     <group
         android:translateX="52.0"
         android:translateY="42.0" >
diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml b/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
similarity index 62%
copy from packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
copy to packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
index 85573e0..ea03a50 100644
--- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
+++ b/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,11 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+  <solid android:color="#61FFFFFF" />
+  <corners android:radius="@dimen/recents_grid_task_view_rounded_corners_radius"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml b/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml
index 694019e9..ea794d4 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml
@@ -14,9 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="8.5dp"
+        android:autoMirrored="true"
+        android:width="17.0dp"
         android:height="17.0dp"
-        android:viewportWidth="20.0"
+        android:viewportWidth="40.0"
         android:viewportHeight="40.0">
     <path
         android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/stat_sys_managed_profile_disable_animation.xml b/packages/SystemUI/res/drawable/stat_sys_managed_profile_disable_animation.xml
index c6a2fdd..1e41a31 100644
--- a/packages/SystemUI/res/drawable/stat_sys_managed_profile_disable_animation.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_managed_profile_disable_animation.xml
@@ -16,7 +16,6 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="27.0dp"
         android:height="22.0dp"
-        android:alpha="0.3"
         android:viewportWidth="27.0"
         android:viewportHeight="22.0">
     <clip-path
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_roam.xml b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
similarity index 95%
rename from packages/SystemUI/res/drawable/stat_sys_data_fully_connected_roam.xml
rename to packages/SystemUI/res/drawable/stat_sys_roaming.xml
index 363e231..4baa472 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_roam.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
@@ -14,10 +14,10 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="4.25dp"
+        android:width="8.5dp"
         android:height="17dp"
         android:viewportWidth="6.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="12.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M2.800000,7.900000l-1.000000,0.000000L1.800000,11.000000L0.200000,11.000000L0.200000,2.500000l2.700000,0.000000c0.900000,0.000000 1.500000,0.200000 2.000000,0.700000s0.700000,1.100000 0.700000,1.900000c0.000000,0.600000 -0.100000,1.100000 -0.300000,1.500000S4.800000,7.200000 4.400000,7.400000l1.500000,3.500000L5.900000,11.000000L4.100000,11.000000L2.800000,7.900000zM1.800000,6.500000l1.100000,0.000000c0.400000,0.000000 0.600000,-0.100000 0.800000,-0.400000S4.000000,5.600000 4.000000,5.200000c0.000000,-0.400000 -0.100000,-0.800000 -0.300000,-1.000000S3.300000,3.800000 2.900000,3.800000L1.800000,3.800000L1.800000,6.500000z"/>
diff --git a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
index e85b76d..46c761a 100644
--- a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
+++ b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
@@ -27,7 +27,7 @@
     <LinearLayout
         android:id="@+id/date_time_group"
         android:layout_width="wrap_content"
-        android:layout_height="19dp"
+        android:layout_height="wrap_content"
         android:orientation="horizontal"
         android:focusable="true" >
 
diff --git a/packages/SystemUI/res/layout/data_usage.xml b/packages/SystemUI/res/layout/data_usage.xml
index c943f3d..fdc6f14 100644
--- a/packages/SystemUI/res/layout/data_usage.xml
+++ b/packages/SystemUI/res/layout/data_usage.xml
@@ -59,6 +59,7 @@
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:textAppearance="@style/TextAppearance.QS.DataUsage" />
+
     </LinearLayout>
 
     <LinearLayout
@@ -82,4 +83,13 @@
             android:textAppearance="@style/TextAppearance.QS.DataUsage.Secondary" />
     </LinearLayout>
 
-</com.android.systemui.qs.tiles.DataUsageDetailView>
\ No newline at end of file
+    <TextView
+        android:id="@+id/roaming_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingTop="16dp"
+        android:text="@string/accessibility_data_connection_roaming"
+        android:textAppearance="@style/TextAppearance.QS.DataUsage.Secondary"
+        android:visibility="gone" />
+
+</com.android.systemui.qs.tiles.DataUsageDetailView>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index a20ec8e..8b10074 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -43,4 +43,15 @@
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         />
+    <ImageView
+        android:id="@+id/mobile_roaming"
+        android:layout_width="wrap_content"
+        android:layout_height="17dp"
+        android:paddingStart="22dp"
+        android:paddingTop="1.5dp"
+        android:paddingBottom="3dp"
+        android:scaleType="fitCenter"
+        android:src="@drawable/stat_sys_roaming"
+        android:contentDescription="@string/accessibility_data_connection_roaming"
+        android:visibility="gone" />
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 3948dc4..9d8ef83 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -23,125 +23,4 @@
     android:visibility="gone"
     android:clickable="true"
     android:gravity="top|start"
-    android:orientation="vertical"
-    android:paddingStart="@*android:dimen/notification_content_margin_start"
-    android:paddingEnd="8dp"
-    android:background="@color/notification_guts_bg_color"
-    android:theme="@*android:style/Theme.DeviceDefault.Light">
-
-    <!-- header -->
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="20dp"
-        android:paddingEnd="8dp"
-        android:paddingBottom="15dp"
-        android:id="@+id/notification_guts_header">
-        <TextView
-            android:id="@+id/pkgname"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentStart="true"
-            style="@style/TextAppearance.NotificationGuts.Secondary" />
-        <TextView
-            android:id="@+id/channel_name"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentStart="true"
-            android:layout_below="@id/pkgname"
-            style="@style/TextAppearance.NotificationGuts.Header" />
-        <Switch
-            android:id="@+id/channel_enabled_switch"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentRight="true"
-            android:layout_centerVertical="true"
-            android:background="@null" />
-    </RelativeLayout>
-    <!-- Importance radio buttons -->
-    <LinearLayout
-        android:id="@+id/importance"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-        <RadioGroup
-            android:id="@+id/importance_buttons"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:paddingEnd="@*android:dimen/notification_content_margin_end">
-            <RadioButton
-                android:id="@+id/high_importance"
-                android:layout_width="wrap_content"
-                android:layout_height="@dimen/notification_inline_importance_height"
-                style="@style/TextAppearance.NotificationGuts.Radio"
-                android:buttonTint="@color/notification_guts_buttons" />
-            <RadioButton
-                android:id="@+id/default_importance"
-                android:layout_width="wrap_content"
-                android:layout_height="@dimen/notification_inline_importance_height"
-                style="@style/TextAppearance.NotificationGuts.Radio"
-                android:buttonTint="@color/notification_guts_buttons" />
-            <RadioButton
-                android:id="@+id/low_importance"
-                android:layout_width="wrap_content"
-                android:layout_height="@dimen/notification_inline_importance_height"
-                style="@style/TextAppearance.NotificationGuts.Radio"
-                android:buttonTint="@color/notification_guts_buttons" />
-            <RadioButton
-                android:id="@+id/min_importance"
-                android:layout_width="wrap_content"
-                android:layout_height="@dimen/notification_inline_importance_height"
-                style="@style/TextAppearance.NotificationGuts.Radio"
-                android:buttonTint="@color/notification_guts_buttons" />
-        </RadioGroup>
-        <LinearLayout
-            android:id="@+id/importance_buttons_text"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-            <include layout="@layout/notification_guts_importance_text"/>
-            <include layout="@layout/notification_guts_importance_text"/>
-            <include layout="@layout/notification_guts_importance_text"/>
-            <include layout="@layout/notification_guts_importance_text"/>
-        </LinearLayout>
-    </LinearLayout>
-    <!-- Channel Disabled Text -->
-    <TextView
-        android:id="@+id/channel_disabled"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/notification_channel_disabled"
-        style="@style/TextAppearance.NotificationGuts.Secondary" />
-    <!-- Settings and Done buttons -->
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="end"
-        android:paddingTop="16dp"
-        android:paddingBottom="8dp" >
-
-        <TextView
-            android:id="@+id/more_settings"
-            android:text="@string/notification_more_settings"
-            android:layout_width="wrap_content"
-            android:layout_height="36dp"
-            style="@style/TextAppearance.NotificationGuts.Button"
-            android:background="@drawable/btn_borderless_rect"
-            android:gravity="center"
-            android:paddingEnd="8dp"
-            android:paddingStart="8dp"
-            android:focusable="true" />
-
-        <TextView
-            android:id="@+id/done"
-            android:text="@string/notification_done"
-            android:layout_width="wrap_content"
-            android:layout_height="36dp"
-            style="@style/TextAppearance.NotificationGuts.Button"
-            android:background="@drawable/btn_borderless_rect"
-            android:gravity="center"
-            android:layout_marginStart="8dp"
-            android:layout_marginEnd="8dp"
-            android:focusable="true"/>
-    </LinearLayout>
-</com.android.systemui.statusbar.NotificationGuts>
+    android:theme="@*android:style/Theme.DeviceDefault.Light"/>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
new file mode 100644
index 0000000..9770ecc
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2017, The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<com.android.systemui.statusbar.NotificationInfo
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/notification_guts"
+        android:clickable="true"
+        android:gravity="top|start"
+        android:orientation="vertical"
+        android:paddingStart="@*android:dimen/notification_content_margin_start"
+        android:paddingEnd="8dp"
+        android:background="@color/notification_guts_bg_color"
+        android:theme="@*android:style/Theme.DeviceDefault.Light">
+
+    <!-- header -->
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="20dp"
+        android:paddingEnd="8dp"
+        android:paddingBottom="15dp"
+        android:id="@+id/notification_guts_header">
+        <TextView
+            android:id="@+id/pkgname"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentStart="true"
+            style="@style/TextAppearance.NotificationGuts.Secondary" />
+        <TextView
+            android:id="@+id/channel_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentStart="true"
+            android:layout_below="@id/pkgname"
+            style="@style/TextAppearance.NotificationGuts.Header" />
+        <Switch
+            android:id="@+id/channel_enabled_switch"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:background="@null" />
+    </RelativeLayout>
+    <!-- Importance radio buttons -->
+    <LinearLayout
+        android:id="@+id/importance"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <RadioGroup
+            android:id="@+id/importance_buttons"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingEnd="@*android:dimen/notification_content_margin_end">
+            <RadioButton
+                android:id="@+id/high_importance"
+                android:layout_width="wrap_content"
+                android:layout_height="@dimen/notification_inline_importance_height"
+                style="@style/TextAppearance.NotificationGuts.Radio"
+                android:buttonTint="@color/notification_guts_buttons" />
+            <RadioButton
+                android:id="@+id/default_importance"
+                android:layout_width="wrap_content"
+                android:layout_height="@dimen/notification_inline_importance_height"
+                style="@style/TextAppearance.NotificationGuts.Radio"
+                android:buttonTint="@color/notification_guts_buttons" />
+            <RadioButton
+                android:id="@+id/low_importance"
+                android:layout_width="wrap_content"
+                android:layout_height="@dimen/notification_inline_importance_height"
+                style="@style/TextAppearance.NotificationGuts.Radio"
+                android:buttonTint="@color/notification_guts_buttons" />
+            <RadioButton
+                android:id="@+id/min_importance"
+                android:layout_width="wrap_content"
+                android:layout_height="@dimen/notification_inline_importance_height"
+                style="@style/TextAppearance.NotificationGuts.Radio"
+                android:buttonTint="@color/notification_guts_buttons" />
+        </RadioGroup>
+        <LinearLayout
+            android:id="@+id/importance_buttons_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+            <include layout="@layout/notification_guts_importance_text"/>
+            <include layout="@layout/notification_guts_importance_text"/>
+            <include layout="@layout/notification_guts_importance_text"/>
+            <include layout="@layout/notification_guts_importance_text"/>
+        </LinearLayout>
+    </LinearLayout>
+    <!-- Channel Disabled Text -->
+    <TextView
+        android:id="@+id/channel_disabled"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/notification_channel_disabled"
+        style="@style/TextAppearance.NotificationGuts.Secondary" />
+    <!-- Settings and Done buttons -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="end"
+        android:paddingTop="16dp"
+        android:paddingBottom="8dp" >
+
+        <TextView
+            android:id="@+id/more_settings"
+            android:text="@string/notification_more_settings"
+            android:layout_width="wrap_content"
+            android:layout_height="36dp"
+            style="@style/TextAppearance.NotificationGuts.Button"
+            android:background="@drawable/btn_borderless_rect"
+            android:gravity="center"
+            android:paddingEnd="8dp"
+            android:paddingStart="8dp"
+            android:focusable="true" />
+
+        <TextView
+            android:id="@+id/done"
+            android:text="@string/notification_done"
+            android:layout_width="wrap_content"
+            android:layout_height="36dp"
+            style="@style/TextAppearance.NotificationGuts.Button"
+            android:background="@drawable/btn_borderless_rect"
+            android:gravity="center"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            android:focusable="true"/>
+    </LinearLayout>
+</com.android.systemui.statusbar.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/notification_menu_row.xml b/packages/SystemUI/res/layout/notification_menu_row.xml
new file mode 100644
index 0000000..12bcf81
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_menu_row.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2016, The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<com.android.systemui.statusbar.NotificationMenuRow
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:visibility="invisible"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_settings_icon_row.xml b/packages/SystemUI/res/layout/notification_settings_icon_row.xml
deleted file mode 100644
index da3461b90..0000000
--- a/packages/SystemUI/res/layout/notification_settings_icon_row.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright 2016, The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<com.android.systemui.statusbar.NotificationSettingsIconRow
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:visibility="invisible"
-    >
-
-    <com.android.systemui.statusbar.AlphaOptimizedImageView
-        android:id="@+id/gear_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:padding="@dimen/notification_gear_padding"
-        android:src="@drawable/ic_settings"
-        android:tint="@color/notification_gear_color"
-        android:alpha="0"
-        android:background="?android:attr/selectableItemBackgroundBorderless"
-        />
-
-</com.android.systemui.statusbar.NotificationSettingsIconRow>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
new file mode 100644
index 0000000..5bd64de
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2017, The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<com.android.systemui.statusbar.NotificationSnooze
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/snooze_snackbar_min_height"
+    android:id="@+id/notification_snooze"
+    android:clickable="true"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:paddingStart="24dp"
+    android:paddingEnd="24dp"
+    android:background="@color/snooze_snackbar_bg">
+    
+    <TextView
+        android:id="@+id/snooze_option_default"
+        style="@style/TextAppearance.SnoozeSnackBar"
+        android:layout_width="wrap_content"
+       	android:layout_height="match_parent"
+       	android:gravity="center_vertical"
+      	android:drawableTint="@android:color/white"
+       	android:drawableEnd="@drawable/notification_expand_more"/>
+    
+    <android.widget.Space
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        />
+    
+    <TextView
+        android:id="@+id/undo"
+        style="@style/TextAppearance.SnoozeSnackBar.Button"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginEnd="8dp"
+        android:layout_marginStart="8dp"
+        android:background="@drawable/btn_borderless_rect"
+        android:layout_gravity="end"
+        android:text="@string/snooze_undo" />
+    
+    <LinearLayout
+        android:id="@+id/snooze_options"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:orientation="vertical"/>
+    
+</com.android.systemui.statusbar.NotificationSnooze>
diff --git a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
index aaa84fd..ff55f99 100644
--- a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
+++ b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
@@ -18,7 +18,8 @@
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:paddingStart="8dp"
     android:paddingEnd="8dp"
-    android:paddingBottom="16dp" />
+    android:paddingTop="8dp"
+    android:gravity="center" />
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 50ee64a..25d5226 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -20,10 +20,10 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@drawable/qs_detail_background"
-    android:paddingBottom="8dp"
     android:clickable="true"
-    android:visibility="invisible"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingBottom="8dp"
+    android:visibility="invisible">
 
     <com.android.systemui.ResizingSpace
         android:layout_width="match_parent"
@@ -33,17 +33,16 @@
         android:id="@+id/qs_detail_header"
         layout="@layout/qs_detail_header"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"
+        />
 
     <com.android.systemui.statusbar.AlphaOptimizedImageView
         android:id="@+id/qs_detail_header_progress"
-        android:src="@drawable/indeterminate_anim"
-        android:alpha="0"
-        android:background="@color/qs_detail_progress_track"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:scaleType="fitXY"
-        android:translationY="8dp"
+        android:alpha="0"
+        android:background="@color/qs_detail_progress_track"
+        android:src="@drawable/indeterminate_anim"
         />
 
     <com.android.systemui.qs.NonInterceptingScrollView
@@ -51,12 +50,13 @@
         android:layout_height="0dp"
         android:layout_weight="1"
         android:fillViewport="true">
+
         <FrameLayout
             android:id="@android:id/content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"/>
     </com.android.systemui.qs.NonInterceptingScrollView>
 
-    <include layout="@layout/qs_detail_buttons" />
+    <include layout="@layout/qs_detail_buttons"/>
 
 </com.android.systemui.qs.QSDetail>
diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml
index 7876ada..6edf135 100644
--- a/packages/SystemUI/res/layout/qs_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_detail_item.xml
@@ -29,7 +29,8 @@
         android:layout_width="@dimen/qs_detail_item_icon_size"
         android:layout_height="@dimen/qs_detail_item_icon_size"
         android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
-        android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd" />
+        android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
+        android:tint="?android:attr/textColorPrimary"/>
 
     <LinearLayout
         android:layout_width="0dp"
@@ -62,6 +63,7 @@
         android:focusable="true"
         android:scaleType="center"
         android:contentDescription="@*android:string/media_route_controller_disconnect"
+        android:tint="?android:attr/textColorPrimary"
         android:src="@drawable/ic_qs_cancel" />
 
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
index 9a7e1b6..b9cdf28 100644
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ b/packages/SystemUI/res/layout/qs_detail_items.xml
@@ -43,7 +43,8 @@
         <ImageView
             android:id="@android:id/icon"
             android:layout_width="56dp"
-            android:layout_height="56dp"/>
+            android:layout_height="56dp"
+            android:tint="?android:attr/textColorSecondary" />
 
         <TextView
             android:id="@android:id/title"
diff --git a/packages/SystemUI/res/layout/qs_page_indicator.xml b/packages/SystemUI/res/layout/qs_page_indicator.xml
new file mode 100644
index 0000000..02bd31a
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_page_indicator.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.systemui.qs.PageIndicator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/page_indicator"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:layout_gravity="center"
+    android:layout_marginTop="40dp"
+    android:layout_marginBottom="24dp"
+    android:focusable="true"
+    android:gravity="center"
+    android:importantForAccessibility="yes"
+    android:visibility="gone"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index 8b6060f..8ff1d1e 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -28,15 +28,6 @@
         android:layout_height="48dp"
         android:layout_gravity="bottom">
 
-        <com.android.systemui.qs.PageIndicator
-            android:id="@+id/page_indicator"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:gravity="center"
-            android:importantForAccessibility="yes"
-            android:focusable="true" />
-
     </FrameLayout>
 
 </com.android.systemui.qs.PagedTileLayout>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 9e0a6fe..895185b 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -26,10 +26,9 @@
     <com.android.systemui.qs.QSPanel
             android:id="@+id/quick_settings_panel"
             android:background="#0000"
-            android:layout_marginTop="80dp"
+            android:layout_marginTop="52dp"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingBottom="8dp" />
+            android:layout_height="wrap_content" />
 
     <include layout="@layout/quick_status_bar_expanded_header" />
 
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 5b0f0df..a093b87 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -18,11 +18,12 @@
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:orientation="horizontal">
+        android:orientation="horizontal"
+        android:paddingTop="16dp">
      <TextView android:id="@+id/tile_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textColor="?android:attr/textColorSecondary"
+            android:textColor="?android:attr/textColorPrimary"
             android:gravity="center_horizontal"
             android:minLines="2"
             android:padding="0dp"
@@ -36,4 +37,4 @@
             android:layout_marginLeft="@dimen/restricted_padlock_pading"
             android:scaleType="centerInside"
             android:visibility="gone" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index 080f553..6988c76 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -17,6 +17,7 @@
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:layout_height="48dp"
     android:layout_width="match_parent"
+    android:layout_marginBottom="24dp"
     android:paddingLeft="16dp"
     android:paddingRight="16dp"
     style="@style/BrightnessDialogContainer">
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 53acb9f..174776c 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -23,30 +23,35 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/status_bar_header_height"
     android:layout_gravity="@integer/notification_panel_layout_gravity"
-    android:clipChildren="false"
-    android:clipToPadding="false"
     android:baselineAligned="false"
     android:clickable="false"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:paddingTop="0dp"
+    android:paddingEnd="0dp"
+    android:paddingStart="0dp"
     >
 
     <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="48dp"
-        android:gravity="center"
+        android:layout_alignParentEnd="true"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:orientation="horizontal"
-        android:layout_alignParentEnd="true"
-        android:layout_marginTop="4dp"
-        android:layout_marginEnd="12dp">
+        android:gravity="center"
+        android:paddingEnd="4dp"
+        android:orientation="horizontal">
 
-        <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
+        <com.android.systemui.statusbar.phone.MultiUserSwitch
+            android:id="@+id/multi_user_switch"
             android:layout_width="48dp"
             android:layout_height="48dp"
             android:layout_alignParentEnd="true"
-            android:focusable="true"
-            android:background="@drawable/ripple_drawable" >
-            <ImageView android:id="@+id/multi_user_avatar"
+            android:background="@drawable/ripple_drawable"
+            android:focusable="true">
+
+            <ImageView
+                android:id="@+id/multi_user_avatar"
                 android:layout_width="@dimen/multi_user_avatar_expanded_size"
                 android:layout_height="@dimen/multi_user_avatar_expanded_size"
                 android:layout_gravity="center"
@@ -57,13 +62,42 @@
             android:id="@android:id/edit"
             android:layout_width="48dp"
             android:layout_height="48dp"
-            android:clipToPadding="false"
-            android:clickable="true"
-            android:focusable="true"
-            android:src="@drawable/ic_mode_edit"
             android:background="?android:attr/selectableItemBackgroundBorderless"
+            android:clickable="true"
+            android:clipToPadding="false"
             android:contentDescription="@string/accessibility_quick_settings_edit"
-            android:padding="14dp" />
+            android:focusable="true"
+            android:padding="14dp"
+            android:src="@drawable/ic_mode_edit"
+            android:tint="?android:attr/colorForeground"/>
+
+        <LinearLayout
+            android:id="@+id/system_icons_super_container"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/status_bar_header_height"
+            android:layout_alignWithParentIfMissing="true"
+            android:layout_toStartOf="@id/multi_user_switch"
+            android:background="@drawable/ripple_drawable">
+
+            <FrameLayout
+                android:id="@+id/system_icons_container"
+                android:layout_width="wrap_content"
+                android:layout_height="24dp"
+                android:layout_gravity="center_vertical">
+
+                <include layout="@layout/system_icons" />
+            </FrameLayout>
+
+            <TextView
+                android:id="@+id/battery_level"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="@dimen/header_battery_margin_expanded"
+                android:importantForAccessibility="noHideDescendants"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="@dimen/battery_level_text_size"/>
+        </LinearLayout>
 
         <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
             android:id="@+id/settings_button_container"
@@ -78,16 +112,18 @@
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:background="@drawable/ripple_drawable"
+                android:contentDescription="@string/accessibility_quick_settings_settings"
                 android:src="@drawable/ic_settings_20dp"
-                android:contentDescription="@string/accessibility_quick_settings_settings" />
+                android:tint="?android:attr/colorForeground"/>
+
             <com.android.systemui.statusbar.AlphaOptimizedImageView
                 android:id="@+id/tuner_icon"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:paddingStart="36dp"
-                android:alpha="0.3"
-                android:visibility="invisible"
-                android:src="@drawable/tuner" />
+                android:src="@drawable/tuner"
+                android:tint="?android:attr/textColorTertiary"
+                android:visibility="invisible"/>
 
         </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
 
@@ -106,23 +142,26 @@
 
     <TextView
         android:id="@+id/header_emergency_calls_only"
-        android:layout_height="wrap_content"
         android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
-        android:paddingStart="16dp"
-        android:paddingEnd="16dp"
-        android:paddingTop="6dp"
-        android:visibility="gone"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
-        android:text="@*android:string/emergency_calls_only"
-        android:singleLine="true"
+        android:focusable="true"
         android:gravity="center_vertical"
-        android:focusable="true" />
+        android:paddingStart="16dp"
+        android:paddingTop="6dp"
+        android:singleLine="true"
+        android:text="@*android:string/emergency_calls_only"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
+        android:visibility="gone"/>
 
     <include
         android:id="@+id/date_time_alarm_group"
         layout="@layout/status_bar_alarm_group"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="12dp"
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true" />
 
@@ -130,38 +169,41 @@
         android:id="@+id/quick_qs_panel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="52dp"
-        android:layout_marginStart="12dp"
-        android:layout_marginEnd="12dp"
         android:layout_alignParentEnd="true"
+        android:layout_marginTop="54dp"
+        android:layout_marginBottom="24dp"
+        android:layout_alignParentTop="true"
+        android:accessibilityTraversalAfter="@+id/date_time_group"
+        android:accessibilityTraversalBefore="@id/expand_indicator"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:importantForAccessibility="yes"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
         android:focusable="true"
-        android:accessibilityTraversalAfter="@+id/date_time_group"
-        android:accessibilityTraversalBefore="@id/expand_indicator" />
+        android:importantForAccessibility="yes"
+        android:paddingTop="0dp"/>
 
     <com.android.systemui.statusbar.AlphaOptimizedImageView
         android:id="@+id/qs_detail_header_progress"
-        android:src="@drawable/indeterminate_anim"
-        android:alpha="0"
-        android:background="@color/qs_detail_progress_track"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
+        android:alpha="0"
+        android:background="@color/qs_detail_progress_track"
+        android:src="@drawable/indeterminate_anim"
         />
 
     <TextView
         android:id="@+id/header_debug_info"
-        android:visibility="invisible"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
         android:fontFamily="sans-serif-condensed"
+        android:padding="2dp"
+        android:textColor="#00A040"
         android:textSize="11dp"
         android:textStyle="bold"
-        android:textColor="#00A040"
-        android:padding="2dp"
+        android:visibility="invisible"
         />
 
 </com.android.systemui.statusbar.phone.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/recents_grid_task_view.xml b/packages/SystemUI/res/layout/recents_grid_task_view.xml
index 53bec70..1c9b9ac 100644
--- a/packages/SystemUI/res/layout/recents_grid_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_grid_task_view.xml
@@ -18,7 +18,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:focusable="true">
-    <com.android.systemui.recents.views.TaskViewThumbnail
+    <com.android.systemui.recents.views.grid.GridTaskViewThumbnail
         android:id="@+id/task_view_thumbnail"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 5ee242d..789b765 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -23,7 +23,6 @@
     android:layout_gravity="top|center_horizontal">
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/icon"
-        android:contentDescription="@string/recents_app_info_button_label"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml
index ae5136f..8198f03 100644
--- a/packages/SystemUI/res/layout/split_clock_view.xml
+++ b/packages/SystemUI/res/layout/split_clock_view.xml
@@ -28,6 +28,7 @@
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
         android:textSize="@dimen/qs_time_collapsed_size"
+        android:textColor="?android:attr/textColorPrimary"
         />
     <TextClock
         android:id="@+id/am_pm_view"
@@ -36,6 +37,7 @@
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
         android:textSize="@dimen/qs_time_collapsed_size"
+        android:textColor="?android:attr/textColorPrimary"
         android:importantForAccessibility="no"
         />
 
@@ -44,8 +46,9 @@
         android:id="@+id/empty_time_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:visibility="invisible"
+        android:visibility="gone"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:textColor="?android:attr/textColorPrimary"
         />
 </com.android.systemui.statusbar.policy.SplitClockView>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 6784254..63af3e0 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -42,7 +42,7 @@
     <LinearLayout android:id="@+id/status_bar_contents"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingStart="@dimen/status_bar_padding_start"
+        android:paddingStart="6dp"
         android:paddingEnd="8dp"
         android:orientation="horizontal"
         >
diff --git a/packages/SystemUI/res/layout/status_bar_alarm_group.xml b/packages/SystemUI/res/layout/status_bar_alarm_group.xml
index a02e9af..745320e 100644
--- a/packages/SystemUI/res/layout/status_bar_alarm_group.xml
+++ b/packages/SystemUI/res/layout/status_bar_alarm_group.xml
@@ -41,6 +41,7 @@
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:src="@drawable/ic_access_alarms_small"
+            android:tint="?android:attr/textColorPrimary"
             android:paddingStart="6dp"
             android:gravity="center"
             android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index e456984..d62cc18 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -24,10 +24,10 @@
     >
 
     <ViewStub
-        android:layout="@layout/notification_settings_icon_row"
-        android:id="@+id/settings_icon_row_stub"
-        android:inflatedId="@+id/notification_settings_icon_row"
-        android:layout_width="wrap_content"
+        android:layout="@layout/notification_menu_row"
+        android:id="@+id/menu_row_stub"
+        android:inflatedId="@+id/notification_menu_row"
+        android:layout_width="match_parent"
         android:layout_height="match_parent"
         />
 
diff --git a/packages/SystemUI/res/layout/tuner_shortcut_item.xml b/packages/SystemUI/res/layout/tuner_shortcut_item.xml
new file mode 100644
index 0000000..e9eae3b
--- /dev/null
+++ b/packages/SystemUI/res/layout/tuner_shortcut_item.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:paddingStart="4dp"
+    android:paddingEnd="4dp"
+    android:clickable="true"
+    android:gravity="center"
+    android:background="?android:attr/selectableItemBackground">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:padding="12dp" />
+
+    <TextView android:id="@android:id/title"
+        android:layout_height="wrap_content"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:textColor="?android:attr/textColorPrimary" />
+
+    <com.android.systemui.statusbar.phone.ExpandableIndicator
+        android:id="@+id/expand"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:padding="12dp"
+        android:visibility="gone" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml b/packages/SystemUI/res/layout/tuner_shortcut_list.xml
similarity index 62%
copy from packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
copy to packages/SystemUI/res/layout/tuner_shortcut_list.xml
index 85573e0..9aaffb4 100644
--- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
+++ b/packages/SystemUI/res/layout/tuner_shortcut_list.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,11 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
+
+<android.support.v7.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="200dp"
+    android:gravity="center" />
diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml
index ca4f727..2b4a0f5 100644
--- a/packages/SystemUI/res/layout/zen_mode_condition.xml
+++ b/packages/SystemUI/res/layout/zen_mode_condition.xml
@@ -63,6 +63,7 @@
         android:scaleType="center"
         android:layout_toStartOf="@android:id/button2"
         android:contentDescription="@string/accessibility_quick_settings_less_time"
+        android:tint="?android:attr/textColorPrimary"
         android:src="@drawable/ic_qs_minus" />
 
     <ImageView
@@ -75,6 +76,7 @@
         android:scaleType="center"
         android:layout_centerVertical="true"
         android:contentDescription="@string/accessibility_quick_settings_more_time"
+        android:tint="?android:attr/textColorPrimary"
         android:src="@drawable/ic_qs_plus" />
 
 </RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index aa6c620..d145f7e 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Sluitskerm."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Instellings"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Oorsig."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Werksluitskerm"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Maak toe"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi afgeskakel."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> waarskuwing"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Werkmodus"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Aandbeligting"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Geen onlangse items nie"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Jy het alles toegemaak"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programinligting"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Verdeel horisontaal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verdeel vertikaal"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Verdeel gepasmaak"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Maak toe"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Maak oop"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Verdeel skerm na bo"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Verdeel skerm na links"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Verdeel skerm na regs"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Gelaai"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Ontkoppel VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Jou toestel word bestuur deur <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> gebruik <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> om jou toestel te bestuur."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Jou administrateur kan instellings, korporatiewe toegang, programme, data wat met jou toestel geassosieer word en jou toestel se ligginginligting monitor en bestuur."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Kom meer te wete"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Jy is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>, wat jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Maak VPN-instellings oop"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Jou administrateur het netwerkloglêers aangeskakel wat verkeer op jou toestel monitor.\n\nKontak jou administrateur vir meer inligting."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Jy het \'n program toestemming gegee om \'n VPN-verbinding op te stel.\n\nHierdie program kan jou toestel- en netwerkaktiwiteit monitor, insluitend e-posse, programme en webwerwe."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Jou werkprofiel word deur <xliff:g id="ORGANIZATION">%1$s</xliff:g> bestuur.\n\nJou administrateur is in staat om jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, te monitor.\n\nKontak jou administrateur vir meer inligting.\n\nJy is ook aan \'n VPN gekoppel wat jou netwerkaktiwiteit kan monitor."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Jy is gekoppel aan <xliff:g id="APPLICATION">%1$s</xliff:g>, wat jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Jy is gekoppel aan <xliff:g id="APPLICATION">%1$s</xliff:g>, wat jou persoonlike netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Jy is gekoppel aan <xliff:g id="APPLICATION">%1$s</xliff:g>, wat jou persoonlike netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Jou werkprofiel word deur <xliff:g id="ORGANIZATION">%1$s</xliff:g> bestuur. Dit is gekoppel aan <xliff:g id="APPLICATION">%2$s</xliff:g>, wat jou werknetwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor.\n\nKontak jou administrateur vir meer inligting."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Jou werkprofiel word deur <xliff:g id="ORGANIZATION">%1$s</xliff:g> bestuur. Dit is gekoppel aan <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, wat jou werknetwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor.\n\nJy is ook gekoppel aan <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, wat jou persoonlike netwerkaktiwiteit kan monitor."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Toestel sal gesluit bly totdat jy dit handmatig ontsluit"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Kry kennisgewings vinniger"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Vou uit"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Vou in"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skerm is vasgespeld"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Terug en Oorsig om dit te ontspeld."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Dit hou dit in sig totdat jy dit ontspeld. Raak en hou Oorsig om dit te ontspeld."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Het dit"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nee, dankie"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Versteek <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Aan"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Af"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Met kragkennisgewingkontroles kan jy \'n belangrikheidvlak van 0 tot 5 vir \'n program se kennisgewings stel. \n\n"<b>"Vlak 5"</b>" \n- Wys aan die bokant van die kennisgewinglys \n- Laat volskermonderbreking toe \n- Wys altyd opspringkennisgewings \n\n"<b>"Vlak 4"</b>" \n- Verhoed volskermonderbreking \n- Wys altyd opspringkennisgewings \n\n"<b>"Vlak 3"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n\n"<b>"Vlak 2"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n- Moet nooit \'n klank maak of vibreer nie \n\n"<b>"Vlak 1"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n- Moet nooit \'n klank maak of vibreer nie \n- Versteek van sluitskerm en statusbalk \n- Wys aan die onderkant van die kennisgewinglys \n\n"<b>"Vlak 0"</b>" \n- Blokkeer alle kennisgewings van die program af"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Kennisgewings"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Jy sal nie meer hierdie kennisgewings kry nie."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g>-kennisgewings vir"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Laag"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Middelmatig"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Hoog"</string>
+    <string name="high_importance" msgid="730741630855788381">"Dringend"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Geen klank of visuele onderbreking nie"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Wys sonder klank"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Maak geluid"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Maak geluid en spring op op skerm"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Meer instellings"</string>
     <string name="notification_done" msgid="5279426047273930175">"Klaar"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-kennisgewingkontroles"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 210647c..c2bd45f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ማያ ገጽ ቆልፍ።"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ቅንብሮች"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"አጠቃላይ እይታ።"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"የስራ ማያ ገጽ ቁልፍ"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"ዝጋ"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>።"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi ጠፍቷል።"</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"የ<xliff:g id="DATA_LIMIT">%s</xliff:g> ማስጠንቀቂያ"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"የሥራ ሁነታ"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"የምሽት ብርሃን"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"ኤንኤፍሲ"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"ኤንኤፍሲ ተሰናክሏል"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"ኤንኤፍሲ ነቅቷል"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ሁሉንም ነገር አጽድተዋል"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"የመተግበሪያ መረጃ"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"አግድም ክፈል"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ቁልቁል ክፈል"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"በብጁ ክፈል"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"አሰናብት"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"ክፈት"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ማያ ገጽ ወደ ላይ ክፈል"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ማያ ገጽ ወደ ግራ ክፈል"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ማያ ገጽ ወደ ቀኝ ክፈል"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ባትሪ ሞልቷል"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"የVPN ግንኙነት አቋርጥ"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"የእርስዎ መሣሪያ በ<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ነው የሚቀናበረው።"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> የእርስዎን መሣሪያ ለማቀናበር <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>ን ይጠቀማል።"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"የእርስዎ አስተዳዳሪ ከዚህ መሣሪያ ጋር የተጎዳኙ ቅንብሮችን፣ የኮርፖሬት መዳረሻን፣ መተግበሪያዎችን፣ እና የመሣሪያዎን አካባቢ መከታተል እና ማቀናበር ይችላሉ።"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"የእርስዎ አስተዳዳሪ ቅንብሮችን፣ የኮርፖሬት መዳረሻን፣ መተግበሪያዎችን፣ ከዚህ መሣሪያ ጋር የተጎዳኘ ውሂብን እና የመሣሪያዎን አካባቢ መከታተል እና ማቀናበር ይችላሉ።"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"የበለጠ ለመረዳት"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"እርስዎ ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝተዋል።"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"የVPN ቅንብሮችን ይክፈቱ"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"የእርስዎ አስተዳዳሪ የአውታረ መረብ ምዝግብ ማስታወሻ መያዝን አብርተዋል፣ ይህም በመሣሪያዎ ላይ ያለው ትራፊክ ይከታተላል።\n\nተጨማሪ መረጃ ለማግኘት አስተዳዳሪዎን ያነጋግሩ።"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"የእርስዎ አስተዳዳሪ የአውታረ መረብ ምዝግብ ማስታወሻ መያዝን አብርተዋል፣ ይህም በመሣሪያዎ ላይ ያለውን ትራፊክ ይከታተላል።\n\nተጨማሪ መረጃ ለማግኘት አስተዳዳሪዎን ያነጋግሩ።"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"አንድ መተግበሪያ የVPN ግንኙነት እንዲያዋቅር ፍቃድ ሰጥተውታል።\n\nይህ መተግበሪያ ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የመሣሪያዎን እና የአውታረ መረብ እንቅስቃሴዎን መከታተል ይችላል።"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"የስራ መገለጫዎ በ<xliff:g id="ORGANIZATION">%1$s</xliff:g> ነው የሚተዳደረው።\n\nየእርስዎ አስተዳዳሪ ቅንብሮችን፣ የኮርፖሬት መዳረሻን፣ መተግበሪያዎችን፣ ከመሣሪያዎ ጋር የተጎዳኘ ውሂብን እና የመሣሪያዎ የአካባቢ መረጃን መከታተል እና ማቀናበር ይችላል።\n\nተጨማሪ መረጃ ለማግኘት አስተዳዳሪዎን ያነጋግሩ።\n\nእንዲሁም የአውታረ መረብ ግንኙነትዎን መከታተል ከሚችል አንድ VPN ጋር ተገናኝተዋል።"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"የእርስዎ የስራ መገለጫ በ<xliff:g id="ORGANIZATION">%1$s</xliff:g> ነው የሚቀናበረው።\n\nየእርስዎ አስተዳዳሪ ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችን ጨምሮ የአውታረ መረብ እንቅስቃሴዎን መከታተል ይችላል።\n\nተጨማሪ መረጃ ለማግኘት አስተዳዳሪዎን ያነጋግሩ።\n\nእርስዎ እንዲሁም የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችል ቪፒኤን ጋር ተገናኝተዋል።"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"እርስዎ ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="APPLICATION">%1$s</xliff:g> ጋር ተገናኝተዋል።"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"እርስዎ ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="APPLICATION">%1$s</xliff:g> ጋር ተገናኝተዋል።"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"እርስዎ ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን ከሚከታተለው ከ<xliff:g id="APPLICATION">%1$s</xliff:g> ጋር ተገናኝተዋል።"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"የስራ መገለጫዎ በ<xliff:g id="ORGANIZATION">%1$s</xliff:g> ነው እየተዳደረ ያለው። ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="APPLICATION">%2$s</xliff:g> ጋር ተገናኝተዋል።\n\nተጨማሪ መረጃ ለማግኘት አስተዳዳሪዎን ያነጋግሩ።"</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"የእርስዎ የስራ መገለጫ በ<xliff:g id="ORGANIZATION">%1$s</xliff:g> ነው የሚቀናበረው። ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችን ጨምሮ የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="APPLICATION">%2$s</xliff:g> ጋር ተገናጥቷል።\n\nተጨማሪ መረጃ ለማግኘት አስተዳዳሪዎን ያነጋግሩ።"</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"የስራ መገለጫዎ በ<xliff:g id="ORGANIZATION">%1$s</xliff:g> ነው እየተዳደረ ያለው። ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> ጋር ተገናኝተዋል።\n\nእንዲሁም የግል አውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ጋርም ተገናኝተዋል።"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"እራስዎ እስኪከፍቱት ድረስ መሣሪያ እንደተቆለፈ ይቆያል"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"ማሳወቂያዎችን ፈጥነው ያግኙ"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"አስፋ"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ሰብስብ"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"ማያ ገጽ ተሰክቷል"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"ይሄ እስኪነቅሉት ድረስ በእይታ ውስጥ ያስቀምጠዋል። ለመንቀል ተመለስ እና አጠቃላይ ዕይታ የሚለውን ይጫኑ እና ይያዙ።"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"ይሄ እስኪነቅሉት ድረስ በእይታ ውስጥ ያስቀምጠዋል። ለመንቀል አጠቃላይ ዕይታ ተጭነው ይያዙ።"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"ገባኝ"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"አይ፣ አመሰግናለሁ"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ይደበቅ?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"በርቷል"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ጠፍቷል"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"በኃይል ማሳወቂያ መቆጣጠሪያዎች አማካኝነት የአንድ መተግበሪያ ማሳወቂያዎች የአስፈላጊነት ደረጃ ከ0 እስከ 5 ድረስ ማዘጋጀት ይችላሉ። \n\n"<b>"ደረጃ 5"</b>" \n- በማሳወቂያ ዝርዝሩ አናት ላይ አሳይ \n- የሙሉ ማያ ገጽ ማቋረጥን ፍቀድ \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 4"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 3"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- በፍጹም አጮልቀው አይምልከቱ \n\n"<b>"ደረጃ 2"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ እና ንዝረትን በፍጹም አይኑር \n\n"<b>"ደረጃ 1"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ ወይም ንዝረትን በፍጹም አያደርጉ \n- ከመቆለፊያ ገጽ እና የሁኔታ አሞሌ ይደብቁ \n- በማሳወቂያ ዝርዝር ግርጌ ላይ አሳይ \n\n"<b>"ደረጃ 0"</b>" \n- ሁሉንም የመተግበሪያው ማሳወቂያዎች ያግዱ"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"ማሳወቂያዎች"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"እነዚህን ማሳወቂያዎች ከእንግዲህ አያግኙዋቸውም።"</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> ማሳወቂያዎች ለ"</string>
+    <string name="min_importance" msgid="7559703098688382595">"ዝቅተኛ"</string>
+    <string name="low_importance" msgid="6891335321576225228">"መካከለኛ"</string>
+    <string name="default_importance" msgid="6400766013567512061">"ከፍተኛ"</string>
+    <string name="high_importance" msgid="730741630855788381">"አስቸኳይ"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"ምንም ድምፅ ወይም የሚታይ ትርጉም የለም"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"በፀጥታ አሳይ"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"ድምፅ ፍጠር"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"ድምፅ ፍጠር እና በማያ ገጽ ላይ ብቅ በል"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"ተጨማሪ ቅንብሮች"</string>
     <string name="notification_done" msgid="5279426047273930175">"ተከናውኗል"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ ቁጥጥሮች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ed64c41..2de58fe 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -189,8 +189,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"شاشة التأمين."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"الإعدادات"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"النظرة عامة."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"شاشة تأمين بيانات العمل"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"إغلاق"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"‏تم إيقاف Wifi."</string>
@@ -330,6 +329,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"تحذير <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"وضع العمل"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"إضاءة ليلية"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"‏الاتصال القريب المدى (NFC)"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"تم تعطيل الاتصال القريب المدى"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"تم تمكين الاتصال القريب المدى"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"لقد محوتَ كل شيء"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"معلومات التطبيق"</string>
@@ -343,6 +345,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسيم أفقي"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"تقسيم رأسي"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"تقسيم مخصص"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"تجاهل"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"فتح"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"تقسيم الشاشة بمحاذاة الجزء العلوي"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"تقسيم الشاشة بمحاذاة اليسار"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"تقسيم الشاشة بمحاذاة اليمين"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"تم الشحن"</string>
@@ -423,20 +430,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"‏قطع الاتصال بشبكة VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"تتم إدارة جهازك بواسطة <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"تستخدم <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> تطبيق <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> لإدارة جهازك."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"يمكن للمشرف مراقبة الإعدادات وإدارتها والدخول إلى المؤسسة والتطبيقات والبيانات المقترنة بجهازك ومعلومات موقع الجهاز."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"يمكن للمشرف مراقبة الإعدادات وإدارتها والدخول إلى المؤسسة والتطبيقات والبيانات المرتبطة بجهازك ومعلومات موقع الجهاز."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"مزيد من المعلومات"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"لقد اتصلت بتطبيق <xliff:g id="VPN_APP">%1$s</xliff:g>، الذي يمكن أن يراقب نشاط الشبكة، بما في ذلك رسائل البريد الإلكتروني والتطبيقات والمواقع الإلكترونية."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"‏فتح إعدادات الشبكة الظاهرية الخاصة (VPN)"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات على جهازك.\n\nللحصول على المزيد من المعلومات، اتصل بالمشرف."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات على جهازك.\n\nللحصول على المزيد من المعلومات، اتصل بالمشرف."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"‏لقد منحت تطبيقًا الإذن لإعداد اتصال شبكة ظاهرية خاصة (VPN).\n\nيمكن لهذا التطبيق مراقبة أنشطتك على الجهاز والشبكة، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"‏تتم إدارة ملفك الشخصي للعمل عن طريق <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nبإمكان المشرف مراقبة أنشطتك على الشبكة، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب.\n\nللمزيد من المعلومات، اتصل بالمشرف.\n\nأنت متصل أيضًا بشبكة ظاهرية خاصة (VPN)، يمكنها مراقبة أنشطتك على الشبكة."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"تتم إدارة ملفك الشخصي للعمل بواسطة <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nويمكن للمشرف مراقبة نشاط الشبكة، بما في ذلك رسائل البريد الإلكتروني والتطبيقات والمواقع الإلكترونية.\n\nللحصول على المزيد من المعلومات، اتصل بالمشرف.\n\nوتجدر الإشارة إلى أنك متصل أيضًا بشبكة ظاهرية خاصة يمكن أن تراقب نشاط الشبكة."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"شبكة ظاهرية خاصة"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"أنت متصل بـ <xliff:g id="APPLICATION">%1$s</xliff:g>، الذي يمكنه مراقبة أنشطتك على الشبكة، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"أنت متصل بـ <xliff:g id="APPLICATION">%1$s</xliff:g>، الذي يمكنه مراقبة أنشطتك الشخصية على الشبكة، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"أنت متصل بـ <xliff:g id="APPLICATION">%1$s</xliff:g>، الذي يمكنه مراقبة أنشطتك الشخصية على الشبكة، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"تتم إدارة ملفك الشخصي للعمل عن طريق <xliff:g id="ORGANIZATION">%1$s</xliff:g>. وهذا الملف الشخصي للعمل متصل بـ <xliff:g id="APPLICATION">%2$s</xliff:g>، الذي يمكنه مراقبة أنشطتك على شبكة العمل، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب.\n\nللمزيد من المعلومات، اتصل بالمشرف."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"تتم إدارة ملفك الشخصي للعمل بواسطة <xliff:g id="ORGANIZATION">%1$s</xliff:g>، وهو متصل بتطبيق <xliff:g id="APPLICATION">%2$s</xliff:g>، الذي يمكن أن يراقب نشاطك على شبكة العمل، بما في ذلك رسائل البريد الإلكتروني والتطبيقات والمواقع الإلكترونية.\n\nللحصول على المزيد من المعلومات، اتصل بالمشرف."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"تتم إدارة ملفك الشخصي للعمل عن طريق <xliff:g id="ORGANIZATION">%1$s</xliff:g>. وهذا الملف الشخصي للعمل متصل بـ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>، الذي يمكنه مراقبة أنشطتك على شبكة العمل، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب.\n\nأنت متصل أيضًا بـ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>، الذي يمكنه مراقبة أنشطتك الشخصية على الشبكة."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"سيظل الجهاز مقفلاً إلى أن يتم إلغاء قفله يدويًا"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"الحصول على الإشعارات بشكل أسرع"</string>
@@ -448,10 +455,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"توسيع"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"تصغير"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"تم تثبيت الشاشة"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"يؤدي هذا إلى استمرار عرض الشاشة المُختارة إلى أن تتم إزالة تثبيتها. المس مع الاستمرار الزرين \"رجوع\" و\"نظرة عامة\" لإزالة التثبيت."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"يؤدي هذا إلى استمرار عرض الشاشة المُختارة إلى أن تتم إزالة تثبيتها. المس مع الاستمرار زر \"نظرة عامة\" لإزالة التثبيت."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"حسنًا"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"لا، شكرًا"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"هل تريد إخفاء <xliff:g id="TILE_LABEL">%1$s</xliff:g>؟"</string>
@@ -518,28 +523,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"تشغيل"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"إيقاف"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك تعيين مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة التأمين وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"الإشعارات"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"لن تتلقى هذه الإشعارات بعد الآن."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"إشعارات <xliff:g id="APP">%s</xliff:g> عن"</string>
+    <string name="min_importance" msgid="7559703098688382595">"منخفض الأهمية"</string>
+    <string name="low_importance" msgid="6891335321576225228">"متوسط الأهمية"</string>
+    <string name="default_importance" msgid="6400766013567512061">"أهمية عالية"</string>
+    <string name="high_importance" msgid="730741630855788381">"عاجل"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"من دون تنبيه صوتي أو مرئي"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"عرض بدون تنبيه صوتي"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"إصدار تنبيه صوتي"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"إصدار تنبيه صوتي والظهور بسرعة على الشاشة"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"المزيد من الإعدادات"</string>
     <string name="notification_done" msgid="5279426047273930175">"تم"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"عناصر التحكم في إشعارات <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 70e768c..a60ee26 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Ekranı kilidləyin."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Ayarlar"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"İcmal"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Ekran kilidi"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Qapadın"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi deaktivdir."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> xəbərdarlığı"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"İş rejimi"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Gecə işığı"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Son elementlər yoxdur"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hərşeyi təmizlədiniz"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Tətbiq haqqında"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Üfüqi Böl"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Şaquli Böl"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Fərdi Böl"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Rədd edin"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Açın"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ekranı yuxarıdan ayırın"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ekranı soldan ayırın"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ekranı sağdan ayırın"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Dolub"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN-i bağlantıdan ayırın"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Cihaz <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> tərəfindən idarə olunur."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> cihazınızı idarə etmək üçün <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> istifadə edir."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administratorunuz ayarlara, korporativ girişə, tətbiqlərə, cihaz ilə əlaqədar dataya və cihazın məkan məlumatına nəzarət və idarə edə bilər."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" ("</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Ətraflı məlumat"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g> tətbiqinə qoşulmusunuz və o, e-məktublar, tətbiq və veb saytlar daxil olmaqla şəbəkə fəaliyyətinizə nəzarət edə bilər."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" ("</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN Ayarlarını açın"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Admin, cihazdakı trafikə nəzarət edən şəbəkə loqlarını aktiv etdi.\n\nƏtraflı məlumat üçün admin ilə əlaqə saxlayın."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN bağlantısı quraşdırmağa icazə vermisiniz.\n\nBu tətbiq cihazınızı və şəbəkə fəaliyyətinizi, həmçinin, e-məktubları, tətbiq və veb saytları izləyə bilər."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Sizin iş profile tərəfindən idarə olunur <xliff:g id="ORGANIZATION">%1$s</xliff:g> . \n\n Sizin administrator e-poçt, apps, və web o cümlədən şəbəkə fəaliyyəti monitorinq qadirdir. \n\n Daha ətraflı məlumat üçün, administratora müraciət. \n\n Siz həmçinin şəbəkə fəaliyyətinə nəzarət edə bilərsiniz bir VPN, bağlı olduğunuz."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN (Virtual Şəxsi Şəbəkələr)"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"<xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqinə qoşulmusunuz və o, e-məktublar, tətbiq və veb saytlar daxil olmaqla şəbəkə fəaliyyətinizə nəzarət edə bilər."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqinə qoşulmusunuz və o, e-məktublar, tətbiq və veb saytlar daxil olmaqla şəxsi şəbəkə fəaliyyətinizə nəzarət edə bilər."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqinə qoşulmusunuz və o, e-məktublar, tətbiq və veb saytlar daxil olmaqla şəxsi şəbəkə fəaliyyətinizə nəzarət edə bilər."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"İş profiliniz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tərəfindən idarə olunur. <xliff:g id="APPLICATION">%2$s</xliff:g> tətbiqinə qoşuludur və iş şəbəkə fəaliyyətinizə nəzarət edə bilər, bura e-məktubıar, tətbiq və veb saytlar daxildir.\n\nƏtraflı məlumat üçün administratorunuz ilə əlaqə saxlayın."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"İş profiliniz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tərəfindən idarə olunur. <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> tətbiqinə qoşuludur və iş şəbəkə fəaliyyətinizi idarə edə bilər, bura e-məktubıar, tətbiq və veb saytlar daxildir\n\nSiz, həmçinin, <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> tətbiqinə də qoşulsunuz və o, şəxsi şəbəkə fəaliyyətinizə nəzarət edə bilər."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Device will stay locked until you manually unlock"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Bildirişləri daha sürətlə əldə edin"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Genişləndirin"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Yığcamlaşdırın"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekrana sancaq taxıldı"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri və İcmal düymələrinə basıb saxlayın."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri düyməsinə basıb saxlayın."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Anladım!"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Yox, çox sağ olun"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlədilsin?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Aktiv"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Deaktiv"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Enerji bildiriş nəzarəti ilə, tətbiq bildirişləri üçün əhəmiyyət səviyyəsini 0-dan 5-ə kimi ayarlaya bilərsiniz. \n\n"<b>"Səviyyə 5"</b>" \n- Bildiriş siyahısının yuxarı hissəsində göstərin \n- Tam ekran kəsintisinə icazə verin \n- Hər zaman izləyin \n\n"<b>"Səviyyə 4"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Hər zaman izləyin \n\n"<b>"Level 3"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Heç vaxt izləməyin \n\n"<b>"Level 2"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Heç vaxt izləməyin \n- Heç vaxt səsliyə və ya vibrasiyaya qoymayın \n\n"<b>"Səviyyə 1"</b>" \n- Prevent full screen interruption \n- Heç vaxt izləməyin \n- Heç vaxt səsliyə və ya vibrasiyaya qoymayın \n- Ekran kilidi və ya status panelindən gizlədin \n- Bildiriş siyahısının yuxarı hissəsində göstərin \n\n"<b>"Səviyyə 0"</b>" \n- Bütün bildirişləri tətbiqdən blok edin"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirişlər"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Bu bildirişlər daha sizə göndərilməyəcək."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> bildirişləri:"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Az əhəmiyyətli"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Orta əhəmiyyətli"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Vacib"</string>
+    <string name="high_importance" msgid="730741630855788381">"Çox vacib"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Səs və ya vizual kəsintisiz"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Sakit səsli"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Səsli"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Səsli və ekranda pəncərə ilə"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Daha çox ayar"</string>
     <string name="notification_done" msgid="5279426047273930175">"Hazırdır"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildiriş nəzarəti"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 924ec8a..4738057 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -186,8 +186,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Zaključani ekran."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Podešavanja"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Pregled."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Zaključani ekran za posao"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Zatvori"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi je isključen."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozorenje za <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Režim rada"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noćno svetlo"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je onemogućen"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je omogućen"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Obrisali ste sve"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podeli horizontalno"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podeli vertikalno"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođeno deljenje"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Odbaci"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Otvori"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podeli ekran nagore"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podeli ekran nalevo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podeli ekran nadesno"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjena je"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Prekini vezu sa VPN-om"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Uređajem upravlja <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> koristi <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> za upravljanje uređajem."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administrator može da nadgleda podešavanja, korporativni pristup, aplikacije, podatke povezane sa uređajem i informacije o lokaciji uređaja, kao i da upravlja njima."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Administrator može da nadgleda podešavanja, korporativni pristup, aplikacije, podatke povezane sa uređajem i informacije o lokaciji uređaja, kao i da upravlja njima."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Saznajte više"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Povezani ste sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Otvorite podešavanja VPN-a"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administrator je uključio evidentiranje mreže, koje prati saobraćaj na uređaju.\n\nKontaktirajte administratora za više informacija."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administrator je uključio evidentiranje mreže, koje prati saobraćaj na uređaju.\n\nKontaktirajte administratora za više informacija."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Dali ste dozvolu aplikaciji da podešava VPN vezu.\n\nTa aplikacija može da nadgleda aktivnosti na uređaju i mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Profilom za Work upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nViše informacija potražite od administratora.\n\nPovezani ste i na VPN, koji može da nadgleda aktivnosti na ličnoj mreži."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> upravlja profilom za Work.\n\nAdministrator može da prati aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nKontaktirajte administratora za više informacija.\n\nPovezani ste i sa VPN-om, koji može da prati aktivnosti na mreži."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Povezani ste sa aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Povezani ste sa aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može da nadgleda aktivnosti na ličnoj mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Povezani ste sa aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može da nadgleda aktivnosti na ličnoj mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Profilom za Work upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je sa aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g>, koja može da nadgleda aktivnosti na poslovnoj mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nViše informacija potražite od administratora."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> upravlja profilom za Work. On je povezan sa aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g>, koja može da prati aktivnosti na poslovnoj mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nKontaktirajte administratora za više informacija."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Profilom za Work upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je sa aplikacijom <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, koja može da nadgleda aktivnosti na poslovnoj mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nPovezani ste i sa aplikacijom <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, koja može da nadgleda aktivnosti na ličnoj mreži."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Uređaj će ostati zaključan dok ga ne otključate ručno"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Brže dobijajte obaveštenja"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Proširi"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skupi"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekran je zakačen"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Na ovaj način se ovo stalno prikazuje dok ga ne otkačite. Dodirnite i zadržite Nazad i Pregled da biste ga otkačili."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Na ovaj način se ovo stalno prikazuje dok ga ne otkačite. Dodirnite i zadržite Pregled da biste ga otkačili."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Važi"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, hvala"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li da sakrijete <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Uključeno"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Isključeno"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Pomoću naprednih kontrola za obaveštenja možete da podesite nivo važnosti od 0. do 5. za obaveštenja aplikacije. \n\n"<b>"5. nivo"</b>" \n– Prikazuju se u vrhu liste obaveštenja \n- Dozvoli prekid režima celog ekrana \n– Uvek zaviruj \n\n"<b>"4. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Uvek zaviruj \n\n"<b>"3. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n\n"<b>"2. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n– Nikada ne proizvodi zvuk ili vibraciju \n\n"<b>"1. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n– Nikada ne proizvodi zvuk ili vibraciju \n– Sakrij na zaključanom ekranu i statusnoj traci \n– Prikazuju se u dnu liste obaveštenja \n\n"<b>"0. nivo"</b>" \n– Blokiraj sva obaveštenja iz aplikacije"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Obaveštenja"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Više nećete da dobijate ova obaveštenja."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Obaveštenja aplikacije <xliff:g id="APP">%s</xliff:g> za"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Nisko"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Srednje"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Visoko"</string>
+    <string name="high_importance" msgid="730741630855788381">"Hitno"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Bez zvučnog signala ili vizuelnog obaveštenja"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Prikazuje se bez zvučnog signala"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Emituje se zvučni signal"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Emituje se zvučni signal i prikazuje se na ekranu"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Još podešavanja"</string>
     <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole obaveštenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 4b9cc5a..efbc1d2 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -189,8 +189,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Экран блакіроўкі."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Налады"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Агляд."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Экран блакіроўкі дзейнасці"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Закрыць"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi выключаны."</string>
@@ -328,6 +327,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Папярэджанне: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Рэжым працы"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Начная падсветка"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC адключаны"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC уключаны"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Няма нядаўніх элементаў"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Вы ачысцілі усё"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Звесткі аб праграме"</string>
@@ -341,6 +343,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Падзяліць гарызантальна"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Падзяліць вертыкальна"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Падзяліць іншым чынам"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Адхіліць"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Адкрыць"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Падзяліць экран зверху"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Падзяліць экран злева"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Падзяліць экран справа"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Зараджаны"</string>
@@ -421,20 +428,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Адлучыць VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Ваша прылада знаходзіцца пад кіраваннем <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> выкарыстоўвае <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> для кіравання вашай прыладай."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Адміністратар можа маніторыць налады, карп. доступ, прагр., даныя, звяз. з прыл., і звесткі пра месцазн. прылады і кіраваць гэтым."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Адмін-р можа сачыць за наладамі, карп. доступам, прагр., данымі, звяз. з прыл., звесткамі пра месцазн. прылады і кіраваць гэтым."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Даведацца больш"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Вы падключаны да праграмы <xliff:g id="VPN_APP">%1$s</xliff:g>, якая можа сачыць за вашай сеткавай дзейнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" ,"</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Адкрыйце налады VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Ваш адміністратар уключыў вядзенне журнала сеткі, з дапамогай якога адсочваецца трафік на вашай прыладзе.\n\nДля атрымання дадатковай інфармацыі звярніцеся да адміністратара."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Ваш адміністратар уключыў вядзенне журнала сеткі, з дапамогай якога адсочваецца трафік на вашай прыладзе.\n\nДля атрымання дадатковай інфармацыі звярніцеся да адміністратара."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Вы далі праграме дазвол на наладжванне злучэння VPN.\n\nГэта праграма можа сачыць за актыўнасцю вашай прылады і вашай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Ваш працоўны профіль знаходзіцца пад кіраваннем <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nВаш адміністратар можа сачыць і кіраваць наладамі, доступам да карпаратыўных рэсурсаў, праграмамі, данымі, звязанымі з вашай прыладай, і звесткамі пра месцазнаходжанне прылады.\n\nДля атрымання дадатковай інфармацыі звярніцеся да адміністратара.\n\nВы таксама падключаны да VPN, які можа сачыць за вашай сеткавай актыўнасцю."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Ваш працоўны профіль знаходзіцца пад кіраваннем <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nВаш адміністратар можа сачыць за вашай сеткавай дзейнасцю, уключаючы электронную пошту, праграмы і вэб-сайты.\n\nДля атрымання дадатковай інфармацыі звярніцеся да адміністратара.\n\nВы таксама падключаны да сеткі VPN, якая можа сачыць за вашай сеткавай дзейнасцю."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Вы падлучаны да праграмы <xliff:g id="APPLICATION">%1$s</xliff:g>, якая можа сачыць за вашай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Вы падлучаны да праграмы <xliff:g id="APPLICATION">%1$s</xliff:g>, якая сачыць за вашай асабістай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Вы падключаны да праграмы <xliff:g id="APPLICATION">%1$s</xliff:g>, якая можа сачыць за вашай асабістай сеткавай дзейнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Ваш працоўны профіль знаходзіцца пад кіраваннем <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ён падлучаны да праграмы <xliff:g id="APPLICATION">%2$s</xliff:g>, якая можа сачыць за вашай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты.\n\nДля атрымання дадатковай інфармацыі звярніцеся да адміністратара."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Ваш працоўны профіль знаходзіцца пад кіраваннем <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ён падключаны да праграмы <xliff:g id="APPLICATION">%2$s</xliff:g>, якая можа сачыць за вашай сеткавай дзейнасцю, уключаючы электронную пошту, праграмы і вэб-сайты.\n\nДля атрымання дадатковай інфармацыі звярніцеся да адміністратара."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Ваш працоўны профіль знаходзіцца пад кіраваннем <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ён падлучаны да праграмы <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, якая можа сачыць за вашай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты.\n\nВы таксама падлучаны да праграмы <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, якая можа сачыць за вашай асабістай сеткавай актыўнасцю."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Прылада будзе заставацца заблакіраванай, пакуль вы не разблакіруеце яе ўручную"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Атрымлівайце апавяшчэнні хутчэй"</string>
@@ -446,10 +453,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Разгарнуць"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Згарнуць"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Экран замацаваны"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Будзе паказвацца, пакуль не адмацуеце. Каб адмацаваць, краніце і ўтрымлівайце кнопкі \"Назад\" і \"Агляд\"."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Будзе паказвацца, пакуль не адмацуеце. Каб адмацаваць, краніце і ўтрымлівайце кнопку \"Агляд\"."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Зразумела"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Не, дзякуй"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Схаваць <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -516,28 +521,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Уключана"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Выключана"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"З дапамогай пашыранага кіравання апавяшчэннямі вы можаце задаваць узровень важнасці апавяшчэнняў праграмы ад 0 да 5. \n\n"<b>"Узровень 5"</b>" \n- Паказваць уверсе спіса апавяшчэнняў \n- Дазваляць перапыняць рэжым поўнага экрана \n- Заўсёды дазваляць кароткі паказ \n\n"<b>"Узровень 4"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Заўсёды дазваляць кароткі паказ \n\n"<b>"Узровень 3"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n\n"<b>"Узровень 2"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n- Ніколі не прайграваць гук і не вібрыраваць \n\n"<b>"Узровень 1"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n- Ніколі не прайграваць гук і не вібрыраваць \n- Хаваць з экрана блакіроўкі і панэлі стану \n- Паказваць унізе спіса апавяшчэнняў \n\n"<b>"Узровень 0"</b>" \n- Блакіраваць усе апавяшчэнні ад праграмы"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Апавяшчэнні"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Вы больш не будзеце атрымліваць гэтыя апавяшчэнні."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Апавяшчэнні праграмы <xliff:g id="APP">%s</xliff:g> для"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Нізкая важнасць"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Сярэдняя важнасць"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Высокая важнасць"</string>
+    <string name="high_importance" msgid="730741630855788381">"Тэрміновае"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Без гуку ці візуальнага перапынення"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Паказваць бязгучна"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Прайграваць гук"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Прайграваць гук і паказваць на экране"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Дадатковыя налады"</string>
     <string name="notification_done" msgid="5279426047273930175">"Гатова"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Элементы кантролю апавяшчэнняў <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index e8b4c16..8cb61d9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Заключване на екрана."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Настройки"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Общ преглед."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Заключен екран на служебния профил"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Затваряне"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Функцията за Wi-Fi се изключи."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Работен режим"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Нощно осветление"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"КБП"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"КБП е деактивирана"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"КБП е активирана"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Няма скорошни елементи"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Изчистихте всичко"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информация за приложението"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хоризонтално разделяне"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Вертикално разделяне"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Персонализирано разделяне"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Отхвърляне"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Отваряне"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Разделяне на екрана нагоре"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Разделяне на екрана наляво"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Разделяне на екрана надясно"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заредена"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Прекратяване на връзката с VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Устройството ви се управлява от <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> използва <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>, за да управлява устройството ви."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Администраторът ви може да набл. и управл. настройките, корпор. достъп, прилож., данните, свързани с у-вото, както и информ. за местоп. на у-вото ви."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Администраторът ви може да набл. и управл. настройките, корпор. достъп, прилож., данните, свързани с у-вото, както и информ. за местоп. му."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Научете повече"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Установена е връзка с приложението <xliff:g id="VPN_APP">%1$s</xliff:g>, което може да наблюдава активността ви в мрежата, вкл. имейли, приложения и уебсайтове."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Отваряне на настройките за VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Администраторът ви е включил функцията за регистриране на мрежовата активност, която следи трафика на устройството ви.\n\nЗа повече информация се свържете с администратора си."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Администраторът ви е включил функцията за регистриране на мрежовата активност, която следи трафика на устройството ви.\n\nЗа повече информация се свържете с администратора си."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Разрешихте на приложение да настрои връзка с виртуална частна мрежа (VPN).\n\nТова приложение може да наблюдава активността ви на устройството и в мрежата, включително имейли, приложения и уебсайтове."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Служебният ви потребителски профил се управлява от <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nАдминистраторът ви може да наблюдава активността ви в мрежата, включително имейли, приложения и уебсайтове.\n\nЗа още информация се свържете с администратора си.\n\nСъщо така е установена връзка с виртуална частна мрежа (VPN) и активността ви в нея може да се наблюдава."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Служебният ви потребителски профил се управлява от <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nАдминистраторът ви може да наблюдава активността ви в мрежата, включително имейли, приложения и уебсайтове.\n\nЗа повече информация се свържете с администратора си.\n\nСъщо така е установена връзка с виртуална частна мрежа (VPN) и активността ви в нея може да се наблюдава."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Установена е връзка с приложението <xliff:g id="APPLICATION">%1$s</xliff:g>, което може да наблюдава активността ви в мрежата, включително имейли, приложения и уебсайтове."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Установена е връзка с приложението <xliff:g id="APPLICATION">%1$s</xliff:g>, което може да наблюдава личната ви активност в мрежата, включително имейли, приложения и уебсайтове."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Установена е връзка с приложението <xliff:g id="APPLICATION">%1$s</xliff:g>, което може да наблюдава личната ви активност в мрежата, включително имейли, приложения и уебсайтове."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Служебният ви потребителски профил се управлява от <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Той е свързан с приложението <xliff:g id="APPLICATION">%2$s</xliff:g>, което може да наблюдава служебната ви активност в мрежата, включително имейли, приложения и уебсайтове.\n\nЗа още информация се свържете с администратора си."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Служебният ви потребителски профил се управлява от <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Той е свързан с приложението <xliff:g id="APPLICATION">%2$s</xliff:g>, което може да наблюдава служебната ви активност в мрежата, включително имейли, приложения и уебсайтове.\n\nЗа повече информация се свържете с администратора си."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Служебният ви потребителски профил се управлява от <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Той е свързан с приложението <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, което може да наблюдава служебната ви активност в мрежата, включително имейли, приложения и уебсайтове.\n\nУстановена е връзка и с приложението <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, което може да наблюдава личната ви активност в мрежата."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Устройството ще остане заключено, докато не го отключите ръчно"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Получавайте известия по-бързо"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Разгъване"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Свиване"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Екранът е фиксиран"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Екранът ще се показва, докато не го освободите с докосване и задържане на бутона за връщане назад и този за общ преглед."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Екранът ще се показва, докато не го освободите с докосване и задържане на бутона за общ преглед."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Разбрах"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Не, благодаря"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Да се скрие ли „<xliff:g id="TILE_LABEL">%1$s</xliff:g>“?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Вкл."</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Изкл."</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"С помощта на контролите за известията можете да зададете ниво на важност от 0 до 5 за известията от дадено приложение. \n\n"<b>"Ниво 5"</b>" \n– Показване най-горе в списъка с известия. \n– Разрешаване на прекъсването на цял екран. \n– Известията винаги се показват мимолетно. \n\n"<b>"Ниво 4"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията винаги се показват мимолетно. \n\n"<b>"Ниво 3"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n\n"<b>"Ниво 2"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n– Без издаване на звуков сигнал и вибриране. \n\n"<b>"Ниво 1"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n– Без издаване на звуков сигнал и вибриране. \n– Скриване от заключения екран и лентата на състоянието. \n– Показване най-долу в списъка с известия. \n\n"<b>"Ниво 0"</b>" \n– Блокиране на всички известия от приложението."</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Известия"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Вече няма да получавате тези известия."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Известия от <xliff:g id="APP">%s</xliff:g> за"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Малка"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Средна"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Голяма"</string>
+    <string name="high_importance" msgid="730741630855788381">"Неотложна"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Без звук или визуално прекъсване"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Показване без звук"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Възпроизвеждане на звук"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Възпроизвеждане на звук и показване на изскачащ прозорец на екрана"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Още настройки"</string>
     <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Контроли за известията от <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 2e543b1..ef0063f 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"লক স্ক্রীন।"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"সেটিংস"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"এক নজরে৷"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"কর্মস্থলের স্ক্রীন লক"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"বন্ধ করুন"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>।"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"WiFi বন্ধ হয়েছে।"</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সতর্কতা"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"কাজের মোড"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"নাইট লাইট"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC অক্ষম করা আছে"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC সক্ষম করা আছে"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"কোনো সাম্প্রতিক আইটেম নেই"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"আপনি সবকিছু সাফ করেছেন"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"অ্যাপ্লিকেশানের তথ্য"</string>
@@ -335,6 +337,16 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"অনুভূমিক স্প্লিট"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"উল্লম্ব স্প্লিট"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"কাস্টম স্প্লিট করুন"</string>
+    <!-- no translation found for recents_accessibility_dismissed (2354459747918667050) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_open (1651449827614876864) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_top (9056056469282256287) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_left (8987144699630620019) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_right (275069779299592867) -->
+    <skip />
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"চার্জ হয়েছে"</string>
@@ -415,20 +427,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN এর সংযোগ বিচ্ছিন্ন করুন"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"আপনার ডিভাইসটি <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> এর দ্বারা পরিচালিত৷"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> আপনার ডিভাইস পরিচালনা করার জন্য <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ব্যবহার করে৷"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"আপনার প্রশাসক আপনার ডিভাইসের অবস্থান তথ্য সহ এই ডিভাইসের সেটিংস, কর্পোরেট অ্যাক্সেস, অ্যাপ্স, ডেটা নিরীক্ষণ ও পরিচালনা করতে পারেন৷"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"আপনার প্রশাসক আপনার ডিভাইসের অবস্থান তথ্য সহ এই ডিভাইসের সেটিংস, কর্পোরেট অ্যাক্সেস, অ্যাপ্স, ডেটা নিরীক্ষণ ও পরিচালনা করতে পারেন।"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"আরো জানুন"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করবে৷"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN সেটিংস খুলুন"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"আপনার প্রশাসক \'নেটওয়ার্ক লগিং\' চালু করেছেন, যা আপনার ডিভাইসের ট্রাফিক মনিটর করে৷\n\nআরো তথ্যের জন্য আপনার প্রশাসকের সাথে যোগাযোগ করুন৷"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"আপনার প্রশাসক নেটওয়ার্ক লগিং চালু করেছেন, যা আপনার ডিভাইসের ট্রাফিক নিরীক্ষণ করে।\n\nআরো তথ্যের জন্য আপনার প্রশাসকের সাথে যোগাযোগ করুন।"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"আপনি VPN সংযোগ সেট আপ করার জন্য একটি অ্যাপ্লিকেশানকে অনুমতি দিন৷\n\nএই অ্যাপ্লিকেশানটি ইমেল, অ্যাপ্লিকেশান ও ওয়েবসাইটগুলি সহ আপনার ডিভাইস এবং নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করতে পারে।"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কাজের প্রোফাইল পরিচালনা করে৷\n\nআপনার প্রশাসক ইমেল, অ্যাপ্লিকেশান ও ওয়েবসাইটগুলি সহ আপনার নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করতে সক্ষম৷\n\nআরো তথ্যের জন্য, আপনার প্রশাসকের সাথে যোগাযোগ করুন৷\n\nএছাড়াও আপনি একটি VPN, এর সাথে সংযুক্ত রয়েছেন যা আপনার নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করতে পারে৷"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"আপনার কর্মস্থলের প্রোফাইলটি <xliff:g id="ORGANIZATION">%1$s</xliff:g> দ্বারা পরিচালিত হয়।\n\nআপনার প্রশাসক আপনার ইমেল, অ্যাপ্স ও ওয়েবসাইট সহ কর্মস্থলের নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারেন।\n\nআরো তথ্যের জন্য আপনার প্রশাসকের সঙ্গে যোগাযোগ করুন।\n\nএছাড়া আপনি একটি VPN এর সাথেও সংযুক্ত যা আপনার নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারে।"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> -এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ্লিকেশান এবং ওয়েবসাইটগুলি সমেত আপনার নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারে৷"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> -এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ্লিকেশান এবং ওয়েবসাইটগুলি সমেত আপনার ব্যক্তিগত নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারে৷"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> এর সাথে সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার ব্যক্তিগত নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করবে৷"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কাজের প্রোফাইল পরিচালনা করে৷ এটি <xliff:g id="APPLICATION">%2$s</xliff:g> -এ সংযুক্ত রয়েছে যা আপনার ইমেল, অ্যাপ্লিকেশান ও ওয়েবসাইটগুলি সহ আপনার কাজের নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করতে পারে৷\n\nআরো তথ্যের জন্য, আপনার প্রশাসকের সাথে যোগাযোগ করুন৷"</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"আপনার কর্মস্থলের প্রোফাইলটি <xliff:g id="ORGANIZATION">%1$s</xliff:g> দ্বারা পরিচালিত হয়। সেটি <xliff:g id="APPLICATION">%2$s</xliff:g> এর সাথে সংযুক্ত যা আপনার ইমেল, অ্যাপ্স ও ওয়েবসাইট সহ কর্মস্থলের নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারে।\n\nআরো তথ্যের জন্য আপনার প্রশাসকের সঙ্গে যোগাযোগ করুন।"</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কাজের প্রোফাইল পরিচালনা করে৷ এটি <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> -এ সংযুক্ত রয়েছে যা আপনার ইমেল, অ্যাপ্লিকেশান ও ওয়েবসাইটগুলি সহ আপনার কাজের নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করতে পারে৷\n\nএছাড়াও আপনি <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন যা আপনার ব্যক্তিগত নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করতে পারে৷"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"আপনি নিজে আনলক না করা পর্যন্ত ডিভাইসটি লক হয়ে থাকবে"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"বিজ্ঞপ্তিগুলি আরো দ্রুত পান"</string>
@@ -440,10 +452,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"প্রসারিত করুন"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"সঙ্কুচিত করুন"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"স্ক্রীন পিন করা হয়েছে"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"এটি আপনি আনপিন না করা পর্যন্ত এটিকে প্রদর্শিত করবে৷ আনপিন করতে ফিরুন এবং ওভারভিউ স্পর্শ করে ধরে থাকুন।"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"এটি আপনি আনপিন না করা পর্যন্ত এটিকে প্রদর্শিত করবে৷ আনপিন করতে ওভারভিউ স্পর্শ করে ধরে থাকুন৷"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"বুঝেছি"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"না থাক"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> লুকাবেন?"</string>
@@ -510,28 +520,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"চালু আছে"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"বন্ধ আছে"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"পাওয়ার বিজ্ঞপ্তির নিয়ন্ত্রণগুলি ব্যহবার করে, আপনি কোনো অ্যাপ্লিকেশানের বিজ্ঞপ্তির জন্য ০ থেকে ৫ পর্যন্ত একটি গুরুত্বের লেভেলকে সেট করতে পারবেন৷ \n\n"<b>"লেভেল ৫"</b>" \n- বিজ্ঞপ্তি তালিকার শীর্ষে দেখায় \n- পূর্ণ স্ক্রীনের বাধাকে অনুমতি দেয় \n- সর্বদা স্ক্রীনে উপস্থিত হয় \n\n"<b>"লেভেল ৪"</b>" \n- পূর্ণ স্ক্রীনের বাধাকে আটকায় \n- সর্বদা স্ক্রীনে উপস্থিত হয় \n\n"<b>"লেভেল ৩"</b>" \n- পূর্ণ স্ক্রীনের বাধাকে আটকায় \n- কখনই স্ক্রীনে উপস্থিত হয় না \n\n"<b>"লেভেল ২"</b>" \n- পূর্ণ স্ক্রীনের বাধাকে আটকায় \n- কখনই স্ক্রীনে উপস্থিত হয় না \n- কখনই শব্দ এবং কম্পন করে না \n\n"<b>"লেভেল ১"</b>" \n- পূর্ণ স্ক্রীনের বাধাকে আটকায় \n- কখনই স্ক্রীনে উপস্থিত হয় না \n- কখনই শব্দ এবং কম্পন করে না \n- লক স্ক্রীন এবং স্থিতি দন্ড থেকে লুকায় \n- বিজ্ঞপ্তি তালিকার নীচের দিকে দেখায় \n\n"<b>"লেভেল ০"</b>" \n- অ্যাপ্লিকেশান থেকে সমস্ত বিজ্ঞপ্তিকে অবরূদ্ধ করে"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"বিজ্ঞপ্তি"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"আপনি আর এই বিজ্ঞপ্তিগুলি পাবেন না।"</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"এর জন্য <xliff:g id="APP">%s</xliff:g> বিজ্ঞপ্তি"</string>
+    <string name="min_importance" msgid="7559703098688382595">"নিম্ন"</string>
+    <string name="low_importance" msgid="6891335321576225228">"মাঝারি"</string>
+    <string name="default_importance" msgid="6400766013567512061">"উচ্চ"</string>
+    <string name="high_importance" msgid="730741630855788381">"জরুরি"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"কোনো শব্দ বা ভিজ্যুয়াল বাধা নেই"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"নিঃশব্দে দেখান"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"শব্দ করে"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"শব্দ করে ও স্ক্রীনে ভেসে ওঠে"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"আরো সেটিংস"</string>
     <string name="notification_done" msgid="5279426047273930175">"সম্পন্ন"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> বিজ্ঞপ্তির নিয়ন্ত্রণগুলি"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f6dcf68..b694b4b 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -186,8 +186,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Zaključan ekran."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Postavke"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Pregled."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Zaključan ekran radnog profila"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Zatvori"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi je isključen."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Poslovni režim"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noćno svjetlo"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je onemogućen"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je omogućen"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Sve ste obrisali"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podjela po horizontali"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podjela po vertikali"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođena podjela"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Odbaci"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Otvori"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podijeli ekran nagore"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podijeli ekran nalijevo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podijeli ekran nadesno"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Prekini VPN vezu"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Vašim uređajem upravlja aplikacija <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> koristi aplikaciju <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> za upravljanje vašim uređajem."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Vaš administrator može pratiti postavke, korporativni pristup, aplikacije, podatke povezane s vašim uređajem i informacije o lokaciji vašeg uređaja."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Vaš administrator može pratiti postavke, korporativni pristup, aplikacije, podatke povezane s vašim uređajem i informacije o lokaciji vašeg uređaja."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Saznajte više"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Povezani ste s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može pratiti vašu aktivnost na mreži, uključujući e-poruke i web lokacije."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Postavke otvorene VPN mreže"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Vaš administrator je uključio zapisivanje na mreži, čime se prati saobraćaj na vašem uređaju.\n\nZa više informacija obratite se administratoru."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Vaš administrator je uključio zapisivanje na mreži, čime se prati saobraćaj na vašem uređaju.\n\nZa više informacija, obratite se administratoru."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Jednoj aplikaciji ste dali odobrenje da uspostavi VPN vezu.\n\nTa aplikacija može pratiti vašu aktivnost na uređaju i mreži, uključujući e-poštu, aplikacije i web-lokacije."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Vašim profilom za posao upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nVaš administrator može pratiti vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije.\n\nZa više informacija kontaktirajte svog administratora.\n\nPovezani ste i na VPN, koji može pratiti vašu aktivnost na mreži."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Vašim radnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nVaš administrator može pratiti vašu aktivnost na radnoj mreži, uključujući e-poruke, aplikacije i web lokacije.\n\nZa više informacija, obratite se administratoru.\n\nPovezani ste i na VPN, koji može pratiti vašu aktivnost na mreži."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Povezani ste sa aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može pratiti vašu aktivnost na mreži, uključujući e-mailove, aplikacije i web-lokacije."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Povezani ste sa aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može pratiti vašu aktivnost na privatnoj mreži, uključujući e-mailove, aplikacije i web-lokacije."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Povezani ste na aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može pratiti vaše privatne aktivnosti na mreži, uključujući e-poštu, aplikacije i web stranice."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Profilom za posao upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je sa aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g>, koja može pratiti vašu aktivnost na radnoj mreži, uključujući e-poštu, aplikacije i web-lokacije.\n\nZa više informacija kontaktirajte svog administratora."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Vašim radnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je s aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g>, koja može pratiti vašu aktivnost na radnoj mreži, uključujući e-poruke, aplikacije i web lokacije.\n\nZa više informacija, obratite se svom administratoru."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Profilom za posao upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je sa aplikacijom <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, koja može pratiti vašu aktivnost na radnoj mreži, uključujući e-poštu, aplikacije i web-lokacije.\n\nPovezani ste i sa aplikacijom <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, koja može pratiti vašu aktivnost na privatnoj mreži."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Uređaj će ostati zaključan dok ga ručno ne otključate"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Brže primaj obavještenja"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Proširi"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skupi"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekran je prikačen"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Ekran ostaje prikazan ovako dok ga ne otkačite. Da ga otkačite, dodirnite i držite dugme Nazad."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ekran ostaje prikzan ovako dok ga ne otkačite. Da ga otkačite, dodirnite i držite dugme Pregled."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Jasno mi je"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, hvala"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li sakriti <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -514,28 +519,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Uključeno"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Isključeno"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Uz kontrolu obavještenja o napajanju, možete postaviti nivo značaja obavještenja iz aplikacije, i to od nivoa 0 do 5. \n\n"<b>"Nivo 5"</b>" \n- Prikaži na vrhu liste obavještenja \n- Dopusti prekid prikaza cijelog ekrana \n- Uvijek izviruj \n\n"<b>"Nvio 4"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Uvijek izviruj \n\n"<b>"Nivo 3"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikad ne izviruj \n\n"<b>"Nivo 2"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikad ne izviruj \n- Nikada ne puštaj zvuk ili vibraciju \n\n"<b>"Nivo 1"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikada ne izviruj \n- Nikada ne puštaj zvuk ili vibraciju \n- Sakrij sa ekrana za zaključavanje i statusne trake \n- Prikaži na dnu liste obavještenja \n\n"<b>"Nivo 0"</b>" \n- Blokiraj sva obavještenja iz aplikacije"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Obavještenja"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Nećete više primati ova obavještenja."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Obavještenje aplikacije <xliff:g id="APP">%s</xliff:g> za"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Niska"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Srednja"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Visoka"</string>
+    <string name="high_importance" msgid="730741630855788381">"Hitno"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Bez ometanja zvukom ili prikazivanjem"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Prikaži bez zvuka"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Pusti zvuk"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Pusti zvuk i prikaži na ekranu"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
     <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole <xliff:g id="APP_NAME">%1$s</xliff:g> obavještenja"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 93e9142..fc61ab0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Pantalla de bloqueig"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Configuració"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Visió general"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Pantalla de bloqueig per a la feina"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Tanca"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"La xarxa Wi-Fi està desactivada."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertiment: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode de feina"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Llum nocturna"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"L\'NFC està desactivada"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"L\'NFC està activada"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"No hi ha cap element recent"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ho has esborrat tot"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informació de l\'aplicació"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisió horitzontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisió vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisió personalitzada"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Ignora"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Obre"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Divideix la pantalla cap amunt"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Divideix la pantalla cap a l\'esquerra"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Divideix la pantalla cap a la dreta"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Desconnecta la VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> gestiona el teu dispositiu."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> utilitza <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> per gestionar el teu dispositiu."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"L\'administrador pot supervisar i gestionar la configuració, l\'accés corporatiu, les aplicacions, la informació d\'ubicació del dispositiu i les dades associades."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"L\'administrador pot supervisar i gestionar la configuració, l\'accés corporatiu, les aplicacions, la ubicació i les dades del dispositiu."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Més informació"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Estàs connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pot supervisar la teva activitat a la xarxa, com els correus electrònics, les aplicacions i els llocs web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Obre la configuració de la VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"L\'administrador ha activat el registre de xarxa, que supervisa el trànsit del teu dispositiu.\n\nPer obtenir més informació, contacta amb l\'administrador."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"L\'administrador ha activat el registre de xarxa, que supervisa el trànsit del teu dispositiu.\n\nPer obtenir més informació, contacta amb l\'administrador."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Has donat permís a una aplicació per configurar una connexió VPN.\n\nAquesta aplicació pot supervisar el dispositiu i l\'activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> gestiona el perfil de Work.\n\nL\'administrador pot supervisar l\'activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web.\n\nPer obtenir més informació, contacta amb l\'administrador.\n\nA més, estàs connectat a una VPN, que pot supervisar l\'activitat a la xarxa."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> gestiona el teu perfil professional.\n\nL\'administrador pot supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web.\n\nPer obtenir més informació, contacta amb l\'administrador.\n\nA més, estàs connectat a una VPN, que també pot supervisar la teva activitat a la xarxa."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Estàs connectat a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pot supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Estàs connectat a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pot supervisar la teva activitat personal a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Estàs connectat a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pot supervisar la teva activitat personal a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> gestiona el teu perfil professional. Aquest perfil està connectat a <xliff:g id="APPLICATION">%2$s</xliff:g>, que pot supervisar la teva activitat professional a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web.\n\nPer obtenir més informació, contacta amb l\'administrador."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> gestiona el teu perfil professional. Aquest perfil està connectat a <xliff:g id="APPLICATION">%2$s</xliff:g>, que pot supervisar la teva activitat professional a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web.\n\nPer obtenir més informació, contacta amb l\'administrador."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> gestiona el teu perfil professional. Aquest perfil està connectat a <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, que pot supervisar la teva activitat professional a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web.\n\nA més, estàs connectat a <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, que també pot supervisar la teva activitat personal a la xarxa."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"El dispositiu continuarà bloquejat fins que no el desbloquegis manualment."</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Obtén notificacions més ràpidament"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Amplia"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Replega"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"La pantalla està fixada"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Aquest element es continuarà mostrant fins que deixis de fixar-lo. Per fer-ho, toca i mantén premudes les opcions Enrere i Visió general."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Aquest element es continuarà mostrant fins que deixis de fixar-lo. Per fer-ho, toca i mantén premuda l\'opció Visió general."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"D\'acord"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"No, gràcies"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vols amagar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Activat"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desactivat"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Amb els controls de notificació millorats, pots establir un nivell d\'importància d\'entre 0 i 5 per a les notificacions d\'una aplicació. \n\n"<b>"Nivell 5"</b>" \n- Mostra les notificacions a la part superior de la llista \n- Permet la interrupció de la pantalla completa \n- Permet sempre la previsualització \n\n"<b>"Nivell 4"</b>" \n- No permet la interrupció de la pantalla completa \n- Permet sempre la previsualització \n\n"<b>"Nivell 3"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n\n"<b>"Nivell 2"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n- Les notificacions no poden emetre sons ni vibracions \n\n"<b>"Nivell 1"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n- No activa mai el so ni la vibració \n- Amaga les notificacions de la pantalla de bloqueig i de la barra d\'estat \n- Mostra les notificacions a la part inferior de la llista \n\n"<b>"Nivell 0"</b>" \n- Bloqueja totes les notificacions de l\'aplicació"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificacions"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Ja no rebràs aquestes notificacions."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notificacions de l\'aplicació <xliff:g id="APP">%s</xliff:g> per a"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Baixa"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Mitjana"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Alta"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgent"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Sense so ni interrupcions visuals"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Es mostren de manera silenciosa"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Amb so"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Amb so i amb una finestra emergent"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Més opcions"</string>
     <string name="notification_done" msgid="5279426047273930175">"Fet"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Controls de notificació de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 350db19..9cddbc3 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -189,8 +189,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Obrazovka uzamčení"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Nastavení"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Přehled"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Obrazovka uzamčení pracovního profilu"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Zavřít"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Připojení Wi-Fi je vypnuto."</string>
@@ -328,6 +327,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozornění při <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Pracovní režim"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noční režim"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Žádné nedávné položky"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vše je vymazáno"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informace o aplikaci"</string>
@@ -341,6 +346,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vodorovné rozdělení"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikální rozdělení"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Vlastní rozdělení"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Zrušit"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Otevřít"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Rozdělit obrazovku nahoru"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Rozdělit obrazovku vlevo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Rozdělit obrazovku vpravo"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabito"</string>
@@ -421,20 +431,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Odpojit VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Vaše zařízení je spravováno aplikací <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"Organizace <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> používá ke správě tohoto zařízení aplikaci <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administrátor může sledovat a spravovat nastavení, firemní přístup, aplikace, data přidružená k tomuto zařízení a jeho polohu."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Další informace"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Jste připojeni k aplikaci <xliff:g id="VPN_APP">%1$s</xliff:g>, která může sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Otevřít nastavení VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administrátor zapnul protokolování sítě, které monitoruje síťový provoz v zařízení.\n\nDalší informace vám poskytne administrátor."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Udělili jste aplikaci oprávnění k nastavení připojení VPN.\n\nTato aplikace může sledovat vaši aktivitu v zařízení a v síti, včetně e-mailů, aplikací a webů."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Váš pracovní profil spravuje organizace <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrátor může monitorovat vaši síťovou aktivitu, včetně e-mailů, aplikací a webů.\n\nO další informace požádejte svého administrátora.\n\nJste také připojeni k síti VPN, která může sledovat vaši aktivitu v síti."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Jste připojeni k aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g>, která může sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Jste připojeni k aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g>, která může sledovat vaši osobní aktivitu v síti, včetně e-mailů, aplikací a webů."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Jste připojeni k aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g>, která může sledovat vaši osobní aktivitu v síti, včetně e-mailů, aplikací a webů."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Váš pracovní profil spravuje organizace <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Je připojen k aplikaci <xliff:g id="APPLICATION">%2$s</xliff:g>, která může sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů.\n\nO další informace požádejte svého administrátora."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Váš pracovní profil spravuje organizace <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Je připojen k aplikaci <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, která může sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů.\n\nTaké jste připojeni k aplikaci <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, která může sledovat vaši osobní aktivitu v síti."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Zařízení zůstane uzamčeno, dokud je ručně neodemknete"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Čtěte si oznámení rychleji"</string>
@@ -446,10 +460,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Rozbalit"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sbalit"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Obrazovka je připnuta"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Uvolníte jej stisknutím a podržením tlačítek Zpět a Přehled."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Uvolníte jej stisknutím a podržením tlačítka Přehled."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Rozumím"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, děkuji"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skrýt <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -516,28 +528,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Zapnuto"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Vypnuto"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Rozšířené ovládací prvky oznámení umožňují nastavit úroveň důležitosti oznámení aplikace od 0 do 5. \n\n"<b>"Úroveň 5"</b>" \n– Zobrazit na začátku seznamu oznámení \n– Povolit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 4"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 3"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n\n"<b>"Úroveň 2"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat žádný zvukový signál ani nevibrovat \n\n"<b>"Úroveň 1"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat zvukový signál ani nevibrovat \n– Skrýt z obrazovky uzamčení a stavového řádku \n– Zobrazovat na konci seznamu oznámení \n\n"<b>";Úroveň 0"</b>" \n– Blokovat všechna oznámení z aplikace"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Oznámení"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Tato oznámení již nebudete dostávat."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Oznámení aplikace <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Nízká"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Střední"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Vysoká"</string>
+    <string name="high_importance" msgid="730741630855788381">"Naléhavá"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Bez zvukového a vizuálního vyrušení"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Zobrazovat tiše"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Vydat zvukový signál"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Vydat zvukový signál a vyskočit na obrazovku"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Další nastavení"</string>
     <string name="notification_done" msgid="5279426047273930175">"Hotovo"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Nastavení oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 76841dd..3b8783a 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Låseskærm."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Indstillinger"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Oversigt."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Låseskærm til arbejde"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Luk"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi er slået fra."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advarsel ved <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Arbejdstilstand"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nattelys"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Ingen nye elementer"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har ryddet alt"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Oplysninger om applikationen"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Opdel vandret"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Opdel lodret"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Opdel brugerdefineret"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Afvis"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Åbn"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Del skærm øverst"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Del skærm til venstre"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Del skærm til højre"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opladet"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Afbryd VPN-forbindelse"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Din enhed administreres af <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> bruger <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> til at administrere din enhed."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Din administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps og data, der er knyttet til denne enhed, samt enhedens placeringsoplysninger."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Få flere oplysninger"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Du har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. e-mails, apps og websites."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Åbn VPN-indstillinger"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Din administrator har aktiveret netværksregistrering, som overvåger trafik på din enhed.\n\nKontakt din administrator for at få flere oplysninger."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Du gav en app tilladelse til at konfigurere en VPN-forbindelse.\n\nDenne app kan overvåge din enhed og netværksaktivitet, bl.a. e-mails, apps og websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Din arbejdsprofil administreres af <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nDin administrator kan overvåge din netværksaktivitet, bl.a. e-mails, apps og websites.\n\nKontakt din administrator for at få flere oplysninger.\n\nDu er også forbundet til en VPN-forbindelse, som kan overvåge din netværksaktivitet."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Du har forbindelse til <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. e-mails, apps og websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Du har forbindelse til <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan overvåge din private netværksaktivitet, bl.a. e-mails, apps og websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Du har forbindelse til <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan overvåge din private netværksaktivitet, bl.a. e-mails, apps og websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Din arbejdsprofil administreres af <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Den er forbundet til <xliff:g id="APPLICATION">%2$s</xliff:g>, som kan overvåge din arbejdsrelaterede netværksaktivitet, bl.a. e-mails, apps og websites.\n\nKontakt din administrator for at få flere oplysninger."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Din arbejdsprofil administreres af <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Den er forbundet til <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, som kan overvåge din arbejdsrelaterede netværksaktivitet, bl.a. e-mails, apps og websites.\n\nDu er også forbundet til <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, som kan overvåge din private netværksaktivitet."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Enheden vil forblive låst, indtil du manuelt låser den op"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Modtag underretninger hurtigere"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Udvid"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skjul"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skærmen er fastgjort"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Dette fastholder skærmen i visningen, indtil du frigør den. Tryk på Tilbage og Overblik, og hold fingeren nede for at frigøre skærmen."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Dette fastholder skærmen i visningen, indtil du frigør den. Tryk på Tilbage, og hold fingeren nede for at frigøre skærmen."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"OK, det er forstået"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nej tak"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Til"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Fra"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Med kontrolelementer til underretninger om strøm kan du konfigurere et vigtighedsniveau fra 0 til 5 for en apps underretninger. \n\n"<b>"Niveau 5"</b>\n"- Vis øverst på listen over underretninger \n- Tillad afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 4"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 3"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n\n"<b>"Niveau 2"</b>\n"- Ingen afbrydelse af fuld skærm \n Se aldrig smugkig \n- Ingen lyd og vibration \n\n"<b>"Niveau 1"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n- Ingen lyd eller vibration \n- Skjul fra låseskærm og statusbjælke \n- Vis nederst på listen over underretninger \n\n"<b>"Niveau 0"</b>\n"- Bloker alle underretninger fra appen."</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Underretninger"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Du modtager ikke længere disse underretninger."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g>-underretninger til"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Lav"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Middel"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Høj"</string>
+    <string name="high_importance" msgid="730741630855788381">"Haster"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Ingen lyd eller pop op-visning"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Vis lydløst"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Med lyd"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Med lyd og pop op-visning"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Flere indstillinger"</string>
     <string name="notification_done" msgid="5279426047273930175">"Udfør"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrolelementer til underretninger for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 23f0bd0..b2ad211 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Sperrbildschirm"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Einstellungen"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Übersicht"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Sperrbildschirm für Arbeitsprofil"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Schließen"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"WLAN ist deaktiviert."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Warnung für <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Arbeitsmodus"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nachtlicht"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ist deaktiviert"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ist aktiviert"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Keine kürzlich verwendeten Elemente"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du hast alles gelöscht"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-Info"</string>
@@ -331,12 +333,17 @@
     <string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> konnte nicht gestartet werden."</string>
     <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ist im abgesicherten Modus deaktiviert."</string>
-    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Alle löschen"</string>
+    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Alle schließen"</string>
     <string name="recents_incompatible_app_message" msgid="5075812958564082451">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt"</string>
     <string name="recents_drag_hint_message" msgid="2649739267073203985">"Hierher ziehen, um den Bildschirm zu teilen"</string>
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Geteilte Schaltfläche – horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Geteilte Schaltfläche – vertikal"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Geteilte Schaltfläche – benutzerdefiniert"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Schließen"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Öffnen"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Geteilten Bildschirm oben anzeigen"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Geteilten Bildschirm auf linker Seite anzeigen"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Geteilten Bildschirm auf der rechten Seite anzeigen"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Aufgeladen"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN-Verbindung trennen"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Dein Gerät wird von <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> verwaltet."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> verwaltet dein Gerät mit <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Dein Administrator kann Einstellungen, Zugriffsrechte, Apps und Daten deines Geräts und dessen Standortinformationen überwachen."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Dein Administrator kann Einstellungen, Zugriffsrechte, Apps und Daten deines Geräts und dessen Standortinformationen überwachen und verwalten."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Weitere Informationen"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Du bist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden. Die VPN-App kann deine Netzwerkaktivitäten (E-Mails, Apps und Websites) erfassen."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN-Einstellungen öffnen"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Dein Administrator hat die Netzwerkprotokollierung aktiviert. Damit wird der Verkehr auf deinem Gerät erfasst.\n\nWeitere Informationen erhältst du von deinem Administrator."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Dein Administrator hat die Netzwerkprotokollierung aktiviert. Damit wird der Verkehr auf deinem Gerät erfasst.\n\nWeitere Informationen erhältst du von deinem Administrator."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Du hast einer App gestattet, eine VPN-Verbindung einzurichten.\n\nDiese App kann dein Gerät und deine Netzwerkaktivitäten überwachen, einschließlich E-Mails, Apps und Websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Dein Arbeitsprofil wird von <xliff:g id="ORGANIZATION">%1$s</xliff:g> verwaltet.\n\nDein Administrator kann deine Netzwerkaktivität überwachen, einschließlich E-Mails, Apps und Websites.\n\nWeitere Informationen erhältst du bei deinem Administrator.\n\nDu bist außerdem mit einem VPN verbunden, das deine persönliche Netzwerkaktivität überwachen kann."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Dein Arbeitsprofil wird von <xliff:g id="ORGANIZATION">%1$s</xliff:g> verwaltet.\n\nDein Administrator kann deine Netzwerkaktivitäten einschließlich E-Mails, Apps und Websites überwachen.\n\nWeitere Informationen erhältst du von deinem Administrator.\n\nAußerdem bist du mit einem VPN verbunden, das deine Netzwerkaktivitäten erfassen kann."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Du bist mit der App <xliff:g id="APPLICATION">%1$s</xliff:g> verbunden, die deine Netzwerkaktivität überwachen kann, einschließlich E-Mails, Apps und Websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Du bist mit der App <xliff:g id="APPLICATION">%1$s</xliff:g> verbunden, die deine persönliche Netzwerkaktivität überwachen kann, einschließlich E-Mails, Apps und Websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Du bist mit der App \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" verbunden. Diese kann deine persönlichen Netzwerkaktivitäten erfassen, einschließlich E-Mails, Apps und Websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Dein Arbeitsprofil wird von <xliff:g id="ORGANIZATION">%1$s</xliff:g> verwaltet. Das Profil ist mit der App <xliff:g id="APPLICATION">%2$s</xliff:g> verbunden, die deine geschäftlichen Netzwerkaktivitäten überwachen kann, einschließlich E-Mails, Apps und Websites.\n\nWeitere Informationen erhältst du von deinem Administrator."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Dein Arbeitsprofil wird von <xliff:g id="ORGANIZATION">%1$s</xliff:g> verwaltet. Es ist mit der App <xliff:g id="APPLICATION">%2$s</xliff:g> verbunden, die deine berufliche Netzwerkaktivitäten einschließlich E-Mails, Apps und Websites erfassen kann.\n\nWeitere Informationen erhältst du von deinem Administrator."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Dein Arbeitsprofil wird von <xliff:g id="ORGANIZATION">%1$s</xliff:g> verwaltet. Das Profil ist mit der App <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> verbunden, die deine geschäftliche Netzwerkaktivität überwachen kann, einschließlich E-Mails, Apps und Websites.\n\nDu bist außerdem mit der App <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> verbunden, die deine persönliche Netzwerkaktivität überwachen kann."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Das Gerät bleibt gesperrt, bis du es manuell entsperrst."</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Benachrichtigungen schneller erhalten"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Maximieren"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Minimieren"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Bildschirm ist fixiert"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Der Bildschirm bleibt so lange eingeblendet, bis du die Fixierung aufhebst. Berühre und halte dazu \"Zurück\" und \"Übersicht\"."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Der Bildschirm bleibt so lange eingeblendet, bis du die Fixierung aufhebst. Berühre und halte dazu \"Übersicht\"."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nein danke"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ausblenden?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"An"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Aus"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Mit den erweiterten Benachrichtigungseinstellungen kannst du für App-Benachrichtigungen eine Wichtigkeitsstufe von 0 bis 5 festlegen. \n\n"<b>"Stufe 5"</b>" \n- Auf der Benachrichtigungsleiste ganz oben anzeigen \n- Vollbildunterbrechung zulassen \n- Immer kurz einblenden \n\n"<b>"Stufe 4"</b>" \n- Keine Vollbildunterbrechung \n- Immer kurz einblenden \n\n"<b>"Stufe 3"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n\n"<b>"Stufe 2"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n- Weder Ton noch Vibration \n\n"<b>"Stufe 1"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n- Weder Ton noch Vibration \n- Auf Sperrbildschirm und Statusleiste verbergen \n- Auf der Benachrichtigungsleiste ganz unten anzeigen \n\n"<b>"Stufe 0"</b>" \n- Alle Benachrichtigungen der App sperren"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Benachrichtigungen"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Du erhältst diese Benachrichtigungen nicht mehr."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g>-Benachrichtigungen für"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Niedrig"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Mittel"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Hoch"</string>
+    <string name="high_importance" msgid="730741630855788381">"Dringend"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Kein akustisches Signal und keine visuelle Unterbrechung"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Ohne Ton anzeigen"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Akustisches Signal"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Akustisches Signal und Bildschirmbenachrichtigung"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Weitere Einstellungen"</string>
     <string name="notification_done" msgid="5279426047273930175">"Fertig"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-Benachrichtigungseinstellungen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index a11ee0d..94965f1 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Κλείδωμα οθόνης."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Ρυθμίσεις"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Επισκόπηση."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Οθόνη κλειδωμένης εργασίας"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Κλείσιμο"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Το Wi-fi απενεργοποιήθηκε."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Προειδοποίηση για <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Λειτουργία εργασίας"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Νυχτερινός φωτισμός"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Το NFC είναι απενεργοποιημένο"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Το NFC είναι ενεργοποιημένο"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Έχει γίνει διαγραφή όλων των στοιχείων"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Πληροφορίες εφαρμογής"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Οριζόντιος διαχωρισμός"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Κάθετος διαχωρισμός"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Προσαρμοσμένος διαχωρισμός"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Παράβλεψη"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Άνοιγμα"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Διαχωρισμός οθόνης στην κορυφή"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Διαχωρισμός οθόνης στα αριστερά"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Διαχωρισμός οθόνης στα δεξιά"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Φορτίστηκε"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Αποσύνδεση VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Η διαχείριση της συσκευής σας γίνεται από <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> χρησιμοποιεί <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> για τη διαχείριση της συσκευής σας."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Ο διαχειριστής σας μπορεί να παρακολουθεί και να διαχειρίζεται τις ρυθμίσεις, την εταιρική πρόσβαση, τις εφαρμογές και τα δεδομένα που σχετίζονται με τη συσκευή σας, καθώς και τις πληροφορίες τοποθεσίας της συσκευής σας."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Ο διαχειριστής μπορεί να παρακολουθεί και να διαχειρίζεται ρυθμίσεις, εταιρική πρόσβαση, εφαρμογές και δεδομένα που σχετίζονται με τη συσκευή, καθώς και τις πληροφορίες τοποθεσίας."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Μάθετε περισσότερα"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Έχετε συνδεθεί στην εφαρμογή <xliff:g id="VPN_APP">%1$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστοτόπων."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Άνοιγμα Ρυθμίσεων VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Ο διαχειριστής σας έχει ενεργοποιήσει την καταγραφή δικτύου, η οποία παρακολουθεί την επισκεψιμότητα στη συσκευή σας.\n\nΓια περισσότερες πληροφορίες, επικοινωνήστε με τον διαχειριστή σας."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Ο διαχειριστής σας έχει ενεργοποιήσει την καταγραφή δικτύου, η οποία παρακολουθεί την επισκεψιμότητα στη συσκευή σας.\n\nΓια περισσότερες πληροφορίες, επικοινωνήστε με τον διαχειριστή σας."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Παραχωρήσατε σε μια εφαρμογή άδεια για τη ρύθμιση σύνδεσης VPN.\n\nΑυτή η εφαρμογή μπορεί να παρακολουθεί τη δραστηριότητα της συσκευής και του δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστότοπων."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Η διαχείριση του προφίλ εργασίας γίνεται από τον οργανισμό <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nΟ διαχειριστής έχει τη δυνατότητα παρακολούθησης της δραστηριότητας του δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστότοπων.\n\nΓια περισσότερες πληροφορίες, επικοινωνήστε με το διαχειριστή.\n\nΕπίσης, είστε συνδεδεμένοι σε VPN, το οποίο μπορεί να παρακολουθεί τη δραστηριότητα του δικτύου σας."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Η διαχείριση του προφίλ εργασίας γίνεται από τον οργανισμό <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nΟ διαχειριστής έχει τη δυνατότητα παρακολούθησης της δραστηριότητας του δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστότοπων.\n\nΓια περισσότερες πληροφορίες, επικοινωνήστε με τον διαχειριστή.\n\nΕπίσης, είστε συνδεδεμένοι σε VPN, το οποίο μπορεί να παρακολουθεί τη δραστηριότητα του δικτύου σας."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Έχετε συνδεθεί στην εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα του δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστότοπων."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Έχετε συνδεθεί στην εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα του προσωπικού σας δικτύου, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστότοπων."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Έχετε συνδεθεί στην εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα του προσωπικού σας δικτύου, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστοτόπων."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Η διαχείριση του προφίλ εργασίας γίνεται από τον οργανισμό <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Είναι συνδεδεμένο στην εφαρμογή <xliff:g id="APPLICATION">%2$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα του δικτύου εργασίας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστότοπων.\n\nΓια περισσότερες πληροφορίες, επικοινωνήστε με το διαχειριστή."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Η διαχείριση του προφίλ εργασίας γίνεται από τον οργανισμό <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Είναι συνδεδεμένο στην εφαρμογή <xliff:g id="APPLICATION">%2$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα του δικτύου εργασίας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστότοπων.\n\nΓια περισσότερες πληροφορίες, επικοινωνήστε με το διαχειριστή."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Η διαχείριση του προφίλ εργασίας γίνεται από τον οργανισμό <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Είναι συνδεδεμένο στην εφαρμογή <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα του δικτύου εργασίας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστότοπων.\n\nΕπίσης, είστε συνδεδεμένοι στην εφαρμογή <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα του προσωπικού σας δικτύου."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Η συσκευή θα παραμείνει κλειδωμένη έως ότου την ξεκλειδώσετε μη αυτόματα"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Λάβετε ειδοποιήσεις γρηγορότερα"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Ανάπτυξη"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Σύμπτυξη"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Η οθόνη καρφιτσώθηκε"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Με αυτόν τον τρόπο παραμένει σε προβολή μέχρι να το ξεκαρφιτσώσετε. Αγγίξτε παρατεταμένα τα στοιχεία \"Επιστροφή\" και \"Επισκόπηση\" για ξεκαρφίτσωμα."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Με αυτόν τον τρόπο παραμένει σε προβολή μέχρι να το ξεκαρφιτσώσετε. Αγγίξτε παρατεταμένα την \"Επισκόπηση\" για ξεκαρφίτσωμα."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Το κατάλαβα"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Όχι"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Απόκρυψη <xliff:g id="TILE_LABEL">%1$s</xliff:g>;"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Ενεργή"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Ανενεργή"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Με τα στοιχεία ελέγχου ειδοποίησης ισχύος, μπορείτε να ορίσετε ένα επίπεδο βαρύτητας από 0 έως 5 για τις ειδοποιήσεις μιας εφαρμογής. \n\n"<b>"Επίπεδο 5"</b>" \n- Εμφάνιση στην κορυφή της λίστας ειδοποιήσεων \n- Να επιτρέπεται η διακοπή πλήρους οθόνης \n- Να γίνεται πάντα σύντομη προβολή \n\n"<b>"Επίπεδο 4"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να γίνεται πάντα σύντομη προβολή \n\n"<b>"Επίπεδο 3"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n\n"<b>"Επίπεδο 2"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n- Να μην χρησιμοποιείται ποτέ ήχος και δόνηση \n\n"<b>"Επίπεδο 1"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n- Να μην χρησιμοποιείται ποτέ ήχος και δόνηση \n- Απόκρυψη από την οθόνη κλειδώματος και τη γραμμή κατάστασης \n- Εμφάνιση στο κάτω μέρος της λίστας ειδοποιήσεων \n\n"<b>"Επίπεδο 0"</b>" \n- Αποκλεισμός όλων των ειδοποιήσεων από την εφαρμογή"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Ειδοποιήσεις"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Δεν θα λαμβάνεται πλέον αυτές τις ειδοποιήσεις."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Ειδοποιήσεις <xliff:g id="APP">%s</xliff:g> για"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Χαμηλή"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Μεσαία"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Υψηλή"</string>
+    <string name="high_importance" msgid="730741630855788381">"Επείγον"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Χωρίς ηχητική ή οπτική διακοπή"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Εμφάνιση χωρίς ειδοποίηση"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Αναπαραγωγή ήχου"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Αναπαραγωγή ήχου και εμφάνιση στην οθόνη"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Περισσότερες ρυθμίσεις"</string>
     <string name="notification_done" msgid="5279426047273930175">"Τέλος"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Στοιχεία ελέγχου κοινοποίησης <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 883c812..5f2beba 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lock screen."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Settings"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Overview."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Work lock screen"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Close"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi turned off."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work mode"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Dismiss"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Open"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Disconnect VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Your device is managed by <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> uses <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> to manage your device."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Your administrator can monitor and manage settings, corporate access, apps, data associated with your device and your device\'s location information."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Find out more"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Open VPN Settings"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information contact your admin."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your network activity."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your network activity including emails, apps and websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>. It is connected to <xliff:g id="APPLICATION">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nFor more information, contact your administrator."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>. It is connected to <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nYou\'re also connected to <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, which can monitor your personal network activity."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Device will stay locked until you manually unlock"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Get notifications faster"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 883c812..5f2beba 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lock screen."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Settings"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Overview."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Work lock screen"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Close"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi turned off."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work mode"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Dismiss"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Open"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Disconnect VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Your device is managed by <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> uses <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> to manage your device."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Your administrator can monitor and manage settings, corporate access, apps, data associated with your device and your device\'s location information."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Find out more"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Open VPN Settings"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information contact your admin."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your network activity."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your network activity including emails, apps and websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>. It is connected to <xliff:g id="APPLICATION">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nFor more information, contact your administrator."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>. It is connected to <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nYou\'re also connected to <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, which can monitor your personal network activity."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Device will stay locked until you manually unlock"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Get notifications faster"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 883c812..5f2beba 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lock screen."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Settings"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Overview."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Work lock screen"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Close"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi turned off."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work mode"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"No recent items"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"You\'ve cleared everything"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Dismiss"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Open"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Charged"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Disconnect VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Your device is managed by <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> uses <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> to manage your device."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Your administrator can monitor and manage settings, corporate access, apps, data associated with your device and your device\'s location information."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Find out more"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Open VPN Settings"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information contact your admin."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your network activity."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your network activity including emails, apps and websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"You\'re connected to <xliff:g id="APPLICATION">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>. It is connected to <xliff:g id="APPLICATION">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nFor more information, contact your administrator."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>. It is connected to <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nYou\'re also connected to <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, which can monitor your personal network activity."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Device will stay locked until you manually unlock"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Get notifications faster"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e20b1cae..beeb773 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Pantalla bloqueada"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Configuración"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Recientes"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Pantalla bloqueada del perfil de trabajo"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Cerrar"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi desactivado"</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabajo"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz nocturna"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"La tecnología NFC está inhabilitada"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"La tecnología NFC está habilitada"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"No hay elementos recientes"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Todo borrado"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Descartar"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Abrir"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir pantalla en la parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir pantalla a la izquierda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir pantalla a la derecha"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Desconectar VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> administra tu dispositivo."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> usa <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> para administrar tu dispositivo."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Tu administrador puede controlar la configuración, el acceso corporativo, las apps, los datos asociados y la información de ubicación de tu dispositivo."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Tu administrador controla la configuración, el acceso corporativo, las apps, los datos asociados a tu dispositivo y la información de ubicación."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Más información"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Estás conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede controlar la actividad de tu red, incluidos los correos electrónicos, las apps y los sitios web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Abrir configuración de VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Tu administrador activó el registro de red, que controla el tráfico en tu dispositivo.\n\nComunícate con él para obtener más información."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Tu administrador activó el registro de red, que controla el tráfico en tu dispositivo.\n\nComunícate con él para obtener más información."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Permitiste que una aplicación configurara una conexión VPN.\n\nEsta aplicación puede supervisar la actividad de la red y del dispositivo, incluidos los correos electrónicos, las aplicaciones y los sitios web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> administra tu perfil de trabajo.\n\nEl administrador puede supervisar la actividad de la red, incluidos los correos electrónicos, las aplicaciones y los sitios web.\n\nPara obtener más información, comunícate con el administrador.\n\nTambién tienes conexión a una VPN, que puede supervisar la actividad de tu red."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> administra tu perfil de trabajo.\n\nTu administrador puede controlar tu actividad en la red, como los correos electrónicos, las apps y los sitios web.\n\nComunícate con él para obtener más información.\n\nTambién estás conectado a una VPN, que puede controlar tu actividad en la red."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Tienes conexión a la aplicación <xliff:g id="APPLICATION">%1$s</xliff:g>, que puede supervisar la actividad de la red, incluidos los correos electrónicos, las aplicaciones y los sitios web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Tienes conexión a la aplicación <xliff:g id="APPLICATION">%1$s</xliff:g>, que puede supervisar la actividad de la red personal, incluidos los correos electrónicos, las aplicaciones y los sitios web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Te conectaste a <xliff:g id="APPLICATION">%1$s</xliff:g>, que puede supervisar la actividad de tu red personal, incluidos los correos electrónicos, las apps y los sitios web."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> administra tu perfil de trabajo. Tiene conexión a <xliff:g id="APPLICATION">%2$s</xliff:g>, que puede supervisar la actividad de tu red de trabajo, incluidos los correos electrónicos, las aplicaciones y los sitios web.\n\nPara obtener más información, comunícate con el administrador."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> administra tu perfil de trabajo. Está conectado a <xliff:g id="APPLICATION">%2$s</xliff:g>, que puede controlar la actividad de tu red laboral, como los correos electrónicos, las apps y los sitios web.\n\nPara obtener más información, comunícate con tu administrador."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> administra tu perfil de trabajo. Tiene conexión a <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, que puede supervisar la actividad de tu red de trabajo, incluidos los correos electrónicos, las aplicaciones y los sitios web.\n\nTambién tienes conexión a <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, que puede supervisar la actividad de la red personal."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"El dispositivo permanecerá bloqueado hasta que lo desbloquees manualmente."</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Recibe notificaciones más rápido"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Contraer"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Pantalla fija"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Esta función mantiene la pantalla visible hasta que dejes de fijarla. Para ello, mantén presionados los botones Atrás y Recientes."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Esta función mantiene la pantalla visible hasta que dejes de fijarla. Para ello, mantén presionado el botón Recientes."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Entendido"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"No, gracias"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Activado"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desactivado"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Con los controles de activación de notificaciones, puedes establecer un nivel de importancia para las notificaciones de una app. \n\n"<b>"Nivel 5"</b>" \n- Mostrar en la parte superior de la lista de notificaciones. \n- Permitir interrupción en la pantalla completa. \n- Mostrar siempre. \n\n"<b>"Nivel 4"</b>" \n- No permitir interrupción en la pantalla completa. \n- Mostrar siempre. \n\n"<b>"Nivel 3"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n\n"<b>"Nivel 2"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n- No sonar ni vibrar. \n\n"<b>"Nivel 1"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n- No sonar ni vibrar. \n- Ocultar de la pantalla bloqueada y la barra de estado. \n- Mostrar al final de la lista de notificaciones. \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas las notificaciones de la app."</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificaciones"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Ya no recibirás estas notificaciones."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notificaciones de <xliff:g id="APP">%s</xliff:g> para"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Baja"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Media"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Alta"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgente"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"No emitir sonido ni mostrar"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Mostrar sin emitir sonido"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Emitir sonido"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Emitir sonido y mostrar en pantalla"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Más opciones de configuración"</string>
     <string name="notification_done" msgid="5279426047273930175">"Listo"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index a2bf7a3..ea2d0b6 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Pantalla de bloqueo."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Ajustes"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Aplicaciones recientes."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Pantalla de bloqueo para el perfil de trabajo"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Cerrar"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi desactivado."</string>
@@ -213,10 +212,10 @@
     <string name="accessibility_quick_settings_bluetooth_connected" msgid="4306637793614573659">"Bluetooth conectado."</string>
     <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="2730003763480934529">"Bluetooth desactivado."</string>
     <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="8722351798763206577">"Bluetooth activado."</string>
-    <string name="accessibility_quick_settings_location_off" msgid="5119080556976115520">"Informes de Ubicación desactivados."</string>
-    <string name="accessibility_quick_settings_location_on" msgid="5809937096590102036">"Informes de Ubicación activados."</string>
-    <string name="accessibility_quick_settings_location_changed_off" msgid="8526845571503387376">"Informes de Ubicación desactivados."</string>
-    <string name="accessibility_quick_settings_location_changed_on" msgid="339403053079338468">"Informes de Ubicación activados."</string>
+    <string name="accessibility_quick_settings_location_off" msgid="5119080556976115520">"Informes de ubicación desactivados."</string>
+    <string name="accessibility_quick_settings_location_on" msgid="5809937096590102036">"Informes de ubicación activados."</string>
+    <string name="accessibility_quick_settings_location_changed_off" msgid="8526845571503387376">"Informes de ubicación desactivados."</string>
+    <string name="accessibility_quick_settings_location_changed_on" msgid="339403053079338468">"Informes de ubicación activados."</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"La alarma sonará a la(s) <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_close" msgid="3115847794692516306">"Cerrar panel."</string>
     <string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"Más tiempo."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabajo"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz nocturna"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"La conexión NFC está inhabilitada"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"La conexión NFC está habilitada"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"No hay elementos recientes"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Has rechazado todo"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Descartar"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Abrir"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir la pantalla en la parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir la pantalla a la izquierda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir la pantalla a la derecha"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Desconectar VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Tu dispositivo está administrado por <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> utiliza <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> para administrar tu dispositivo."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Tu admin. puede controlar y gestionar ajustes, acceso corporativo, apps, datos asociados al dispositivo y sus datos de ubicación."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Tu administrador puede controlar y gestionar su configuración, acceso corporativo y aplicaciones, así como los datos y la ubicación del dispositivo."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Más información"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Te has conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede controlar tu actividad de red, como los correos electrónicos, las aplicaciones y los sitios web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Abrir Ajustes de red VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Tu administrador ha activado el registro de la red para controlar el tráfico en tu dispositivo.\n\nPonte en contacto con él para obtener más información."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Tu administrador ha activado el registro de la red para supervisar el tráfico en tu dispositivo.\n\nPonte en contacto con él para obtener más información."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Has concedido permiso a una aplicación para configurar una conexión VPN.\n\nEsta aplicación puede controlar tu dispositivo y tu actividad de red, como correos electrónicos, aplicaciones y sitios web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"El administrador de tu perfil de trabajo es <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nTu administrador puede supervisar tu actividad de red, como correos electrónicos, aplicaciones y sitios web.\n\nPara obtener más información, ponte en contacto con tu administrador.\n\nTambién estás conectado a una red VPN que puede supervisar tu actividad de red."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"El administrador de tu perfil de trabajo es <xliff:g id="ORGANIZATION">%1$s</xliff:g>,\n\n que puede supervisar tu actividad de red, como correos electrónicos, aplicaciones y sitios web.\n\nPara obtener más información, ponte en contacto con tu administrador.\n\nTambién estás conectado a una red VPN, que puede supervisar tu actividad de red."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Estás conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que puede controlar tu actividad de red, como correos electrónicos, aplicaciones y sitios web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Estas conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que puede controlar tu actividad de red personal, como correos electrónicos, aplicaciones y sitios web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Estas conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que puede controlar tu actividad de red personal, como correos electrónicos, aplicaciones y sitios web."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"El administrador de tu perfil de trabajo es <xliff:g id="ORGANIZATION">%1$s</xliff:g> y está conectado a <xliff:g id="APPLICATION">%2$s</xliff:g>, que puede controlar tu actividad de red del trabajo, como correos electrónicos, aplicaciones y sitios web.\n\nPara obtener más información, ponte en contacto con el administrador."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"El administrador de tu perfil de trabajo es <xliff:g id="ORGANIZATION">%1$s</xliff:g> y está conectado a <xliff:g id="APPLICATION">%2$s</xliff:g>, que puede supervisar tu actividad de red en el trabajo, como correos electrónicos, aplicaciones y sitios web.\n\nPara obtener más información, ponte en contacto con tu administrador."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"El administrador de tu perfil de trabajo es <xliff:g id="ORGANIZATION">%1$s</xliff:g> y está conectado a <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, que puede controlar tu actividad de red, como correos electrónicos, aplicaciones y sitios web.\n\nTú también estás conectado a <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, que puede controlar tu actividad de red personal."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"El dispositivo permanecerá bloqueado hasta que se desbloquee manualmente"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Recibe notificaciones más rápido"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Mostrar"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Ocultar"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Pantalla fijada"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsados los botones Atrás y Aplicaciones recientes."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsado el botón Aplicaciones recientes."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Entendido"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"No, gracias"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Sí"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"No"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Los controles de energía de las notificaciones permiten establecer un nivel de importancia de 0 a 5 para las notificaciones de las aplicaciones. \n\n"<b>"Nivel 5"</b>" \n- Mostrar en la parte superior de la lista de notificaciones \n- Permitir interrumpir en el modo de pantalla completa \n- Mostrar siempre \n\n"<b>"Nivel 4"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- Mostrar siempre \n\n"<b>"Nivel 3"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- No mostrar nunca \n\n"<b>"Nivel 2"</b>" \n- Evitar interrumpir en el modo de pantalla completa\n- No mostrar nunca \n- No emitir sonido ni vibrar nunca \n\n"<b>"Nivel 1"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- No mostrar nunca \n- No emitir sonido ni vibrar nunca \n- Ocultar de la pantalla de bloqueo y de la barra de estado \n- Mostrar en la parte inferior de la lista de notificaciones \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas las notificaciones de la aplicación"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificaciones"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Ya no recibirás estas notificaciones."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notificaciones de <xliff:g id="APP">%s</xliff:g>:"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Baja"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Media"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Alta"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgente"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Sin sonido ni interrupción visual"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Mostrar de forma silenciosa"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Emitir sonido"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Emitir sonido y mostrar en pantalla"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Más ajustes"</string>
     <string name="notification_done" msgid="5279426047273930175">"Listo"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index ed6e7f4..01a3f7c 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Kuva lukustamine."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Seaded"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Ülevaade."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Töö lukustuskuva"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Sulgemine"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"WiFi on välja lülitatud."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> hoiatus"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Töörežiim"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Öövalgus"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC on keelatud"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC on lubatud"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Hiljutisi üksusi pole"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Olete kõik ära kustutanud"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Rakenduste teave"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horisontaalne poolitamine"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikaalne poolitamine"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Kohandatud poolitamine"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Loobu"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Ava"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Poolita ekraan üles"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Poolita ekraan vasakule"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Poolita ekraan paremale"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laetud"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Katkesta VPN-i ühendus"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Teie seadet haldab rakendus <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"Organisatsioon <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> kasutab teie seadme haldamiseks rakendust <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administraator saab jälgida ja hallata teie seadmega seotud seadeid, ettevõtte juurdepääsu, rakendusi ning andmeid ja teie seadme asukohateavet."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Administraator saab jälgida ja hallata teie seadmega seotud seadeid, ettevõtte juurdepääsu, rakendusi ja andmeid ning seadme asukohateavet."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Lisateave"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Olete ühendatud rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>, mis saab jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Ava VPN-i seaded"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Teie administraator on sisse lülitanud võrgu logimise funktsiooni, mis jälgib liiklust teie seadmes.\n\nLisateabe saamiseks pöörduge administraatori poole."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Teie administraator on sisse lülitanud võrgu logimise funktsiooni, mis jälgib teie seadmes liiklust.\n\nLisateabe saamiseks võtke ühendust administraatoriga."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Andsite rakendusele loa VPN-i ühenduse seadistamiseks.\n\nSee rakendus võib jälgida teie seadet ja võrgutegevusi, sh meile, rakendusi ja veebisaite."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Teie tööprofiili haldab organisatsioon <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministraator saab jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite.\n\nLisateabe saamiseks võtke ühendust administraatoriga.\n\nTeil on ühendus ka VPN-iga, mis saab jälgida teie võrgutegevusi."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Teie tööprofiili haldab <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministraator saab jälgida teie töökoha võrgutegevusi, sh meile, rakendusi ja veebisaite.\n\nLisateabe saamiseks võtke ühendust administraatoriga.\n\nTeil on ühendus ka VPN-iga, mis saab teie võrgutegevusi jälgida."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Teie seade on ühendatud rakendusega <xliff:g id="APPLICATION">%1$s</xliff:g>, mis võib jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Teie seade on ühendatud rakendusega <xliff:g id="APPLICATION">%1$s</xliff:g>, mis võib jälgida teie isiklikke võrgutegevusi, sh meile, rakendusi ja veebisaite."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Olete ühendatud rakendusega <xliff:g id="APPLICATION">%1$s</xliff:g>, mis võib jälgida teie isiklikke võrgutegevusi, sh meile, rakendusi ja veebisaite."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Teie tööprofiili haldab organisatsioon <xliff:g id="ORGANIZATION">%1$s</xliff:g>. See on ühendatud rakendusega <xliff:g id="APPLICATION">%2$s</xliff:g>, mis võib jälgida teie töökoha võrgutegevusi, sh meile, rakendusi ja veebisaite.\n\nLisateabe saamiseks võtke ühendust administraatoriga."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Teie tööprofiili haldab <xliff:g id="ORGANIZATION">%1$s</xliff:g>. See on ühendatud rakendusega <xliff:g id="APPLICATION">%2$s</xliff:g>, mis võib jälgida teie töökoha võrgutegevusi, sh meile, rakendusi ja veebisaite.\n\nLisateabe saamiseks võtke ühendust administraatoriga."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Teie tööprofiili haldab organisatsioon <xliff:g id="ORGANIZATION">%1$s</xliff:g>. See on ühendatud rakendusega <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, mis võib jälgida teie töökoha võrgutegevusi, sh meile, rakendusi ja veebisaite.\n\nTeie seade on ühendatud ka rakendusega <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, mis võib jälgida teie isiklikke võrgutegevusi."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Seade jääb lukku, kuni selle käsitsi avate"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Saate märguandeid kiiremini"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Laiendamine"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Ahendamine"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekraan on kinnitatud"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"See hoitakse kuval, kuni selle vabastate. Vabastamiseks puudutage pikalt nuppe Tagasi ja Ülevaade."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"See hoitakse kuval, kuni selle vabastate. Vabastamiseks puudutage pikalt nuppu Ülevaade."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Selge"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Tänan, ei"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Kas peita <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Sees"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Väljas"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Toite märguannete juhtnuppudega saate määrata rakenduse märguannete tähtsuse taseme vahemikus 0–5. \n\n"<b>"5. tase"</b>" \n- Kuva märguannete loendi ülaosas\n- Luba täisekraanil häirimine \n- Kuva alati ekraani servas \n\n"<b>"4. tase"</b>" \n- Keela täisekraanil häirimine \n- Kuva alati ekraani servas \n\n"<b>"3. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n\n"<b>"2. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n- Ära kunagi helise ega vibreeri \n\n"<b>"1. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n- Ära kunagi helise ega vibreeri \n- Peida lukustuskuval ja olekuribal \n- Kuva märguannete loendi allosas \n\n"<b>"Tase 0"</b>" \n- Blokeeri kõik rakenduse märguanded"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Märguanded"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Te ei saa enam neid märguandeid."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Rakenduse <xliff:g id="APP">%s</xliff:g> märguanded:"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Väike"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Keskmine"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Suur"</string>
+    <string name="high_importance" msgid="730741630855788381">"Kiireloomuline"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Heli ja visuaalne katkestus puudub"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Kuva vaikselt"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Esita heli"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Esita heli ja tõsta märguanne esile"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Rohkem seadeid"</string>
     <string name="notification_done" msgid="5279426047273930175">"Valmis"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguannete juhtnupud"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index c8e9ade..9d48236 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Pantaila blokeatzeko aukera."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Ezarpenak"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Ikuspegi orokorra."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Laneko pantaila blokeatua"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Itxi"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi konexioa desaktibatu egin da."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Abisua: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Lan modua"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Gaueko argia"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Desgaituta dago NFC"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Gaituta dago NFC"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Ez dago azkenaldi honetako ezer"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Dena garbitu duzu"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Aplikazioaren informazioa"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Zatitze horizontala"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Zatitze bertikala"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Zatitze pertsonalizatua"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Baztertu"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Ireki"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Banandu pantaila eta ezarri goian"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Banandu pantaila eta ezarri ezkerrean"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Banandu pantaila eta ezarri eskuinean"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kargatuta"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Deskonektatu VPN sarea"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> aplikazioak kudeatzen du gailu hau."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundeak <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> erabiltzen du gailua kudeatzeko."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Gailuko ezarpenak, enpresa-sarbidea, aplikazioak eta datuak gainbegira eta kudea ditzake administratzaileak, baita gailuaren kokapen-informazioa ere."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Gailuko ezarpenak, enpresa-sarbidea, aplikazioak eta datuak gainbegira eta kudea ditzake administratzaileak, baita gailuaren kokapen-informazioa ere."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Informazio gehiago"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta zaude eta hark sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Ireki VPN ezarpenak"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administratzaileak sare-erregistroak aktibatu ditu; horrela, zure gailuko trafikoa gainbegira dezake.\n\nInformazio gehiago lortzeko, jarri administratzailearekin harremanetan."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administratzaileak sare-erregistroak aktibatu ditu; horrela, zure gailuko trafikoa gainbegira dezake.\n\nInformazio gehiago lortzeko, jarri administratzailearekin harremanetan."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Aplikazio bati VPN konexio bat konfiguratzeko baimena eman diozu.\n\nAplikazio horrek gailuko eta sareko jarduerak kontrola ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> da laneko profilaren kudeatzailea.\n\nAdministratzaileak sareko jarduerak (mezu elektronikoak, aplikazioak eta webguneak barne) kontrola ditzake.\n\nInformazio gehiago lortzeko, jarri administratzailearekin harremanetan.\n\nHorrez gain, sareko jarduerak kontrola ditzakeen VPN batera konektatuta zaude."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> erakundeak kudeatzen du zure laneko profila.\n\nAdministratzaileak sareko jarduerak kontrola diezazkizuke, besteak beste, posta elektronikoa, aplikazioak eta webguneak.\n\nInformazio gehiago lortzeko, jarri administratzailearekin harremanetan.\n\nHorrez gain, VPN batera zaude konektatuta, eta hark ere kontrola ditzake zure sareko jarduerak."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN konexioa"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"<xliff:g id="APPLICATION">%1$s</xliff:g> aplikaziora konektatuta zaude. Aplikazio horrek sarean egiten dituzun jarduerak kontrola ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g> aplikaziora konektatuta zaude. Aplikazio horrek sarean egiten dituzun jarduera pertsonalak kontrola ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g> aplikaziora konektatuta zaude. Aplikazio horrek sarean egiten dituzun jarduera pertsonalak kontrola ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> da laneko profilaren kudeatzailea, eta profila <xliff:g id="APPLICATION">%2$s</xliff:g> aplikaziora konektatuta dago. Aplikazio horrek sarean egiten dituzun laneko jarduerak kontrola ditzake, mezu elektronikoak, aplikazioak eta webguneak barne.\n\nInformazio gehiago lortzeko, jarri administratzailearekin harremanetan."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> erakundeak kudeatzen du zure laneko profila. Erakundea <xliff:g id="APPLICATION">%2$s</xliff:g> aplikaziora dago konektatuta, bera arduratzen baita laneko sareko jarduerak kontrolatzeaz, besteak beste, posta elektronikoa, aplikazioak eta webguneak.\n\nInformazio gehiago lortzeko, jarri administratzailearekin harremanetan."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> da laneko profilaren kudeatzailea, eta profila <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> aplikaziora konektatuta dago. Aplikazio horrek sarean egiten dituzun laneko jarduerak kontrola ditzake, mezu elektronikoak, aplikazioak eta webguneak barne.\n\nHorrez gain, sarean egiten dituzun jarduera pertsonalak kontrola ditzakeen <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> aplikaziora konektatuta zaude."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Gailua blokeatuta egongo da eskuz desblokeatu arte"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Eskuratu jakinarazpenak azkarrago"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Zabaldu"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Tolestu"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Pantaila ainguratuta dago"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Horrela, ikusgai egongo da aingura kendu arte. Aingura kentzeko, eduki sakatuta \"Atzera\" eta \"Ikuspegi orokorra\" botoiak."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Horrela, ikusgai egongo da aingura kendu arte. Aingura kentzeko, eduki sakatuta \"Ikuspegi orokorra\" botoia."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Ados"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ez, eskerrik asko"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ezkutatu nahi duzu?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Aktibatuta"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desaktibatuta"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Bateria-mailaren arabera jakinarazpenak kontrolatzeko aukerekin, 0 eta 5 bitarteko garrantzi-mailetan sailka ditzakezu aplikazioen jakinarazpenak. \n\n"<b>"5. maila"</b>" \n- Erakutsi jakinarazpenen zerrendaren goialdean. \n- Baimendu etetea pantaila osoko moduan zaudenean. \n- Agerrarazi beti jakinarazpenak. \n\n"<b>"4. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Agerrarazi beti jakinarazpenak. \n\n"<b>"3. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n\n"<b>"2. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n- Ez egin soinurik edo dardararik inoiz. \n\n"<b>"1. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n- Ez egin soinurik edo dardararik inoiz. \n- Ezkutatu pantaila blokeatutik eta egoera-barratik. \n- Erakutsi jakinarazpenen zerrendaren behealdean. \n\n"<b>"0. maila"</b>" \n- Blokeatu aplikazioaren jakinarazpen guztiak."</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Jakinarazpenak"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Aurrerantzean ez duzu jasoko horrelako jakinarazpenik."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> aplikazioaren jakinarazpenak"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Txikia"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Ertaina"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Handia"</string>
+    <string name="high_importance" msgid="730741630855788381">"Premiazkoa"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Ez egin soinurik eta ez erakutsi"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Erakutsi soinurik egin gabe"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Egin soinua"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Egin soinua eta erakutsi pantailan"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Ezarpen gehiago"</string>
     <string name="notification_done" msgid="5279426047273930175">"Eginda"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 1992dd6..d65b218 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"قفل صفحه."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"تنظیمات"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"نمای کلی."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"صفحه حالت قفل نمایه کاری"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"بستن"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"‏Wi-Fi خاموش شد."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"هشدار <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"حالت کار"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"نور شب"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"بدون موارد اخیر"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"همه‌چیز را پاک کرده‌اید"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"اطلاعات برنامه"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسیم افقی"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"تقسیم عمودی"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"سفارشی کردن تقسیم"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"نپذیرفتن"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"باز کردن"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"تقسیم کردن صفحه به بالا"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"تقسیم کردن صفحه به چپ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"تقسیم کردن صفحه به راست"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"شارژ کامل شد"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"‏قطع اتصال VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> دستگاه شما را مدیریت می‌کند."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> با استفاده از <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> دستگاهتان را مدیریت می‌کند."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"سرپرست سیستم شما می‌تواند بر تنظیمات، دسترسی شرکتی، برنامه‌ها، داده‌های مرتبط با دستگاه و اطلاعات مکان دستگاهتان نظارت داشته باشد و آن‌ها را مدیریت کند."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"بیشتر بدانید"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"به <xliff:g id="VPN_APP">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"‏باز کردن تنظیمات VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"سرپرست شما گزارش‌گیری شبکه را (که بر ترافیک دستگاهتان نظارت می‌کند) روشن کرده است.\n\nبرای اطلاعات بیشتر با سرپرست خود تماس بگیرید."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"‏شما به برنامه‌ای برای تنظیم اتصال VPN اجازه دادید.\n\n این برنامه می‌تواند دستگاه و فعالیت شبکه‌تان را کنترل کند، از جمله رایانامه‌، برنامه‌ و وب‌سایت‌ها."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"‏نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود.\n\nسرپرستتان می‌تواند فعالیت شبکه‌تان از جمله رایانامه‌، برنامه‌ و وب‌‌سایت‌ها را کنترل کند.\n\nبرای دریافت اطلاعات بیشتر با سرپرستتان تماس بگیرید.\n\nهمچنین به یک VPN وصل هستید که می‌تواند فعالیت شبکه شما را کنترل کند."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شما از جمله رایانامه‌، برنامه‌ و وب‌سایت‌ها را کنترل کند."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شخصی شما از جمله رایانامه‌، برنامه‌ و وب‌سایت‌ها را کنترل کند."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شخصی شما را (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"نمایه کاری‌تان توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود. این به <xliff:g id="APPLICATION">%2$s</xliff:g> وصل است که فعالیت شبکه کاری‌تان از جمله رایانامه، برنامه و وب‌سایت‌ها را کنترل می‌کند.\n\nبرای دریافت اطلاعات بیشتر، با سرپرستتان تماس بگیرید."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود. این به <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> متصل است که می‌تواند فعالیت شبکه کاری‌تان از جمله رایانامه، برنامه و وب‌سایت‌ها را کنترل کند.\n\nشما همچنین به <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> متصل هستید که می‌تواند فعالیت شبکه شخصی‌تان را کنترل کند."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"دستگاه قفل باقی می‌ماند تا زمانی که قفل آن را به صورت دستی باز کنید"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"دریافت سریع‌تر اعلان‌ها"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"بزرگ کردن"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"کوچک کردن"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"صفحه نمایش پین شد"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"تا زمانی که پین را بردارید، در نما نگه‌داشته می‌شود. برای برداشتن پین، «برگشت» و «نمای کلی» را لمس کنید و نگه‌دارید."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"تا زمانی که پین را بردارید، در نما نگه‌داشته می‌شود. برای برداشتن پین، «نمای کلی» را لمس کنید و نگه‌دارید."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"متوجه شدم"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"نه متشکرم"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> مخفی شود؟"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"روشن"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"خاموش"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"با کنترل‌های قدرتمند اعلان می‌توانید سطح اهمیت اعلان‌های هر برنامه را از ۰ تا ۵ تعیین کنید. \n\n"<b>"سطح ۵"</b>" \n- در صدر فهرست اعلان‌ها نشان داده می‌شود \n- وقفه برای نمایش تمام‌صفحه مجاز است \n- همیشه اجمالی نشان داده می‌شود \n\n"<b>"سطح ۴"</b>" \n- وقفه برای نمایش تمام‌صفحه مجاز نیست \n- همیشه اجمالی نشان داده می‌شود \n\n"<b>"سطح ۳"</b>" \n- وقفه برای نمایش تمام‌صفحه مجاز نیست \n- هیچ‌وقت اجمالی نشان داده نمی‌شود \n\n"<b>"سطح ۲"</b>" \n- وقفه برای نمایش تمام‌صفحه مجاز نیست \n- هیچ‌وقت اجمالی نشان داده نمی‌شود \n- هیچ‌وقت صدا و لرزش ایجاد نمی‌کند \n\n"<b>"سطح ۱"</b>" \n- نمایش تمام صفحه مجاز نیست \n- هیچ‌وقت اجمالی نشان داده نمی‌شود \n- هیچ‌وقت صدا یا لرزش ایجاد نمی‌کند \n- در قفل صفحه و نوار وضعیت پنهان است \n- در پایین فهرست اعلان‌ها نشان داده می‌شود \n\n"<b>"سطح ۰"</b>" \n- همه اعلان‌های این برنامه مسدود است"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"اعلان‌ها"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"دیگر این اعلان‌ها را دریافت نخواهید کرد."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"اعلان‌های <xliff:g id="APP">%s</xliff:g> برای"</string>
+    <string name="min_importance" msgid="7559703098688382595">"کم"</string>
+    <string name="low_importance" msgid="6891335321576225228">"متوسط"</string>
+    <string name="default_importance" msgid="6400766013567512061">"زیاد"</string>
+    <string name="high_importance" msgid="730741630855788381">"فوری"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"بدون وقفه صوتی و تصویری"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"نمایش به‌صورت بی‌صدا"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"پخش صدا"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"پخش صدا و صفحه بازشو"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"تنظیمات بیشتر"</string>
     <string name="notification_done" msgid="5279426047273930175">"تمام"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"کنترل‌های اعلان <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 7176df4..dda508f 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lukitse näyttö."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Asetukset"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Viimeisimmät."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Työlukitusnäyttö"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Sulje"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi poistettiin käytöstä."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> – varoitus"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Työtila"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Yövalo"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Ei viimeaikaisia kohteita"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Kaikki on hoidettu."</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Sovellustiedot"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vaakasuuntainen jako"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pystysuuntainen jako"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Muokattu jako"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Hylkää"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Avaa"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Jaa näyttö ylös"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Jaa näyttö vasemmalle"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Jaa näyttö oikealle"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ladattu"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Katkaise VPN-yhteys"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Laitettasi hallinnoi <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> hallinnoi laitettasi sovelluksen <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> avulla."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Järjestelmänvalvoja voi valvoa ja hallinnoida asetuksia, yrityskäyttöä, sovelluksia, laitteen sijaintitietoja ja muita tietoja."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Lisätietoja"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Olet yhteydessä sovellukseen <xliff:g id="VPN_APP">%1$s</xliff:g>, joka voi valvoa verkkotoimintaasi, esimerkiksi sähköposteja, sovelluksia ja verkkosivustoja."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Avaa VPN-asetukset"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Järjestelmänvalvoja on ottanut käyttöön verkkolokitiedostojen tallentamisen. Sen avulla seurataan laitteellasi tapahtuvaa liikennettä.\n\nPyydä lisätietoja järjestelmänvalvojalta."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Olet myöntänyt sovellukselle oikeuden VPN-yhteyden muodostamiseen.\n\nSovellus voi valvoa laitettasi ja toimintaasi verkossa, esimerkiksi avaamiasi sähköposteja, sovelluksia ja verkkosivustoja."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Työprofiiliasi hallinnoi <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nJärjestelmänvalvojasi voi valvoa toimintaasi verkossa. Hän voi seurata esimerkiksi avaamiasi sähköposteja, sovelluksia ja verkkosivustoja.\n\nLisätietoja saat järjestelmänvalvojaltasi.\n\nLisäksi olet muodostanut yhteyden VPN-palveluun, joka voi valvoa toimintaasi verkossa."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Olet muodostanut yhteyden sovellukseen <xliff:g id="APPLICATION">%1$s</xliff:g>, joka voi valvoa toimintaasi verkossa. Sovellus voi seurata esimerkiksi avaamiasi sähköposteja, sovelluksia ja verkkosivustoja."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Olet muodostanut yhteyden sovellukseen <xliff:g id="APPLICATION">%1$s</xliff:g>, joka voi valvoa henkilökohtaista toimintaasi verkossa. Sovellus voi seurata esimerkiksi avaamiasi sähköposteja, sovelluksia ja verkkosivustoja."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Olet muodostanut yhteyden sovellukseen <xliff:g id="APPLICATION">%1$s</xliff:g>, joka voi valvoa henkilökohtaista toimintaasi verkossa. Sovellus voi esimerkiksi seurata avaamiasi sähköposteja, sovelluksia ja verkkosivustoja."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Työprofiiliasi hallinnoi <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Se on yhteydessä sovellukseen <xliff:g id="APPLICATION">%2$s</xliff:g>, joka voi valvoa työhön liittyvää toimintaasi verkossa. Sovellus voi seurata esimerkiksi avaamiasi sähköposteja, sovelluksia ja verkkosivustoja.\n\nSaat lisätietoja järjestelmänvalvojaltasi."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Työprofiiliasi hallinnoi <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Se on yhteydessä sovellukseen <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, joka voi valvoa työhön liittyvää toimintaasi verkossa. Sovellus voi seurata esimerkiksi avaamiasi sähköposteja, sovelluksia ja verkkosivustoja.\n\nLisäksi olet yhteydessä sovellukseen <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, joka voi valvoa henkilökohtaista toimintaasi verkossa."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Laite pysyy lukittuna, kunnes se avataan käsin"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Näe ilmoitukset nopeammin"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Laajenna."</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Tiivistä."</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Näyttö on kiinnitetty"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Pysyy näkyvissä, kunnes irrotat sen. Irrota painamalla pitkään Edellinen ja Viimeisimmät."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Pysyy näkyvissä, kunnes irrotat sen. Irrota painamalla pitkään Viimeisimmät."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Selvä"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ei kiitos"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Piilotetaanko <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Käytössä"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Pois käytöstä"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Ilmoitusten tehohallinnan avulla voit määrittää sovelluksen ilmoituksille tärkeystason väliltä 0–5. \n\n"<b>"Taso 5"</b>" \n– Ilmoitukset näytetään ilmoitusluettelon yläosassa \n– Näkyminen koko näytön tilassa sallitaan \n– Ilmoitukset kurkistavat aina näytölle\n\n"<b>"Taso 4"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ilmoitukset kurkistavat aina näytölle \n\n"<b>"Taso 3"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n\n"<b>"Taso 2"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n– Ei ääniä eikä värinää \n\n"<b>"Taso 1"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n– Ei ääniä eikä värinää \n– Ilmoitukset piilotetaan lukitusnäytöltä ja tilapalkista \n– Ilmoitukset näytetään ilmoitusluettelon alaosassa \n\n"<b>"Taso 0"</b>" \n– Kaikki sovelluksen ilmoitukset estetään"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Ilmoitukset"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Et saa näitä ilmoituksia enää."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Sovelluksen <xliff:g id="APP">%s</xliff:g> ilmoitukset:"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Matala"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Keskitaso"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Korkea"</string>
+    <string name="high_importance" msgid="730741630855788381">"Kiireellinen"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Ei ääntä tai näkyvää ilmoitusta"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Näkyy ilman ääntä"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Ääni"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Ääni, ilmoitus näkyy näytöllä"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Lisäasetukset"</string>
     <string name="notification_done" msgid="5279426047273930175">"Valmis"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ilmoitusten hallinta"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 3ec4ecc..0790fb4 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Écran de verrouillage"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Paramètres"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Aperçu"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Verrouillage de l\'écran du profil professionnel"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Fermer"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi désactivé"</string>
@@ -324,6 +323,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode Travail"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Éclairage nocturne"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Aucun élément récent"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vous avez tout effacé"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Détails de l\'application"</string>
@@ -337,6 +342,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Fermer"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Ouvrir"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Écran partagé dans le haut"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Écran partagé à la gauche"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Écran partagé à la droite"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargée"</string>
@@ -417,20 +427,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Déconnecter le RPV"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Votre appareil est géré par <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> utilise <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> pour gérer votre appareil."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"L\'admin. de l\'entreprise peut surv. et gérer les param., l\'accès, les applis et les données de cet app., y compris sa localisation"</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"En savoir plus"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Vous êtes connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité réseau, y compris les courriels, les applications et les sites Web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Paramètres RPV ouverts"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Votre administrateur a activé la journalisation réseau, qui surveille le trafic sur votre appareil.\n\nPour en savoir plus, communiquez avec lui."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Vous avez autorisé une application à configurer une connexion RPV.\n\nCette application peut contrôler l\'activité de votre appareil et votre activité sur le réseau, y compris les courriels, les applications et les sites Web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Votre profil professionnel est géré par <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nVotre administrateur peut contrôler votre activité sur le réseau, y compris les courriels, les applications et les sites Web.\n\nPour en savoir plus, communiquez avec votre administrateur.\n\nVous êtes également connecté à un RPV qui peut contrôler votre activité sur le réseau."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"RPV"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Vous êtes connecté à <xliff:g id="APPLICATION">%1$s</xliff:g>. Cette application peut contrôler votre activité sur le réseau, y compris les courriels, les applications et les sites Web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Vous êtes connecté à <xliff:g id="APPLICATION">%1$s</xliff:g>. Cette application peut contrôler votre activité personnelle sur le réseau, y compris les courriels, les applications et les sites Web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Vous êtes connecté à <xliff:g id="APPLICATION">%1$s</xliff:g>. Cette application peut contrôler votre activité personnelle sur le réseau, y compris les courriels, les applications et les sites Web."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Votre profil professionnel est géré par <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Il est connecté à <xliff:g id="APPLICATION">%2$s</xliff:g>. Cette application peut contrôler votre activité sur le réseau, y compris les courriels, les applications et les sites Web.\n\nPour en savoir plus, communiquez avec votre administrateur."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Votre profil professionnel est géré par <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Il est connecté à <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>. Cette application peut contrôler votre activité sur le réseau, y compris les courriels, les applications et les sites Web.\n\nVous êtes également connecté à <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>. Cette application peut contrôler votre activité personnelle sur le réseau."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"L\'appareil restera verrouillé jusqu\'à ce que vous le déverrouilliez manuellement"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Voir les notifications plus rapidement"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 36db7dd..f693c98 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Écran de verrouillage"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Paramètres"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Aperçu"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Écran de verrouillage du profil professionnel"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Fermer"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi désactivé."</string>
@@ -324,6 +323,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avertissement : <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode Travail"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Éclairage nocturne"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Aucun élément récent"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vous avez tout effacé."</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Infos application"</string>
@@ -337,6 +342,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Ignorer"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Ouvrir"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Partager l\'écran en haut"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Partager l\'écran sur la gauche"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Partager l\'écran sur la droite"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargé"</string>
@@ -417,20 +427,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Déconnecter le VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Votre appareil est géré par <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> utilise <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> pour gérer votre appareil."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Votre administrateur peut contrôler et gérer paramètres, accès aux contenus entreprise, applications, données et informations de localisation associées à cet appareil."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"En savoir plus"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Vous êtes connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité sur le réseau, y compris les e-mails, les applications et les sites Web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Ouvrir les paramètres VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Votre administrateur a activé la journalisation réseau, qui surveille le trafic sur votre appareil.\n\nPour en savoir plus, contactez-le."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Vous avez autorisé une application à configurer une connexion VPN.\n\nCette application peut contrôler l\'activité de votre appareil et votre activité sur le réseau, y compris votre activité relative aux e-mails, aux applications et aux sites Web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Votre profil professionnel est géré par <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nVotre administrateur peut contrôler votre activité sur le réseau (e-mails, applications et sites Web).\n\nPour en savoir plus, contactez votre administrateur.\n\nVous êtes également connecté à un VPN qui peut contrôler votre activité sur le réseau."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Vous êtes connecté à <xliff:g id="APPLICATION">%1$s</xliff:g>. Cette application peut contrôler votre activité sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Vous êtes connecté à <xliff:g id="APPLICATION">%1$s</xliff:g>. Cette application peut contrôler votre activité personnelle sur le réseau, y compris votre activité relative aux e-mails, aux applications et aux sites Web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Vous êtes connecté à <xliff:g id="APPLICATION">%1$s</xliff:g>. Cette application peut contrôler votre activité personnelle sur le réseau, y compris les e-mails, les applications et les sites Web."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Votre profil professionnel est géré par <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Il est connecté à <xliff:g id="APPLICATION">%2$s</xliff:g>. Cette application peut contrôler l\'activité de ce profil sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web.\n\nPour en savoir plus, contactez votre administrateur."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Votre profil professionnel est géré par <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Il est connecté à <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>. Cette application peut contrôler l\'activité de ce profil sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web.\n\nVous êtes également connecté à <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>. Cette application peut surveiller votre activité personnelle sur le réseau."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"L\'appareil restera verrouillé jusqu\'à ce que vous le déverrouilliez manuellement."</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Recevoir les notifications plus vite"</string>
@@ -442,10 +456,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Développer"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Réduire"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Écran épinglé"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Cet écran est épinglé jusqu\'à l\'annulation de l\'opération. Pour annuler l\'épinglage, appuyez de manière prolongée sur les boutons Retour et Aperçu."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Cet écran est épinglé jusqu\'à l\'annulation de l\'opération. Pour annuler l\'épinglage, appuyez de manière prolongée sur le bouton Aperçu."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Non, merci"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Masquer <xliff:g id="TILE_LABEL">%1$s</xliff:g> ?"</string>
@@ -512,28 +524,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Activé"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Désactivé"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Grâce aux commandes de gestion des notifications, vous pouvez définir le niveau d\'importance (compris entre 0 et 5) des notifications d\'une application. \n\n"<b>"Niveau 5"</b>" \n- Afficher en haut de la liste des notifications \n- Autoriser l\'interruption en plein écran \n- Toujours en aperçu \n\n"<b>"Niveau 4"</b>" \n- Empêcher l\'interruption en plein écran \n- Toujours en aperçu \n\n"<b>"Niveau 3"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n\n"<b>"Niveau 2"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n- Ne jamais émettre de signal sonore ni déclencher le vibreur \n\n"<b>"Niveau 1"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n- Ne jamais émettre de signal sonore ni déclencher le vibreur \n- Masquer les notifications dans l\'écran de verrouillage et la barre d\'état \n- Afficher au bas de la liste des notifications \n\n"<b>"Niveau 0"</b>" \n- Bloquer toutes les notifications de l\'application"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Vous ne recevrez plus ces notifications."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notifications de l\'application <xliff:g id="APP">%s</xliff:g> pour"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Faible"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Moyenne"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Élevée"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgente"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Aucune interruption sonore ni visuelle"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Affichage silencieux"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Alerte sonore"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Alerte sonore et affichage à l\'écran"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
     <string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Commandes de notification de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d7824e4..fda9ffe 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Pantalla de bloqueo."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Configuración"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Visión xeral."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Pantalla de bloqueo do perfil de traballo"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Pechar"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi desactivada."</string>
@@ -324,6 +323,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertencia <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de traballo"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz nocturna"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Non hai elementos recentes"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Borraches todo"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información da aplicación"</string>
@@ -337,6 +342,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dividir en horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dividir en vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Dividir de xeito personalizado"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Ignorar"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Abrir"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir pantalla na parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir pantalla á esquerda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir pantalla á dereita"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
@@ -417,20 +427,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Desconectar VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"O teu dispositivo está xestionado por <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> utiliza <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> para xestionar o teu dispositivo."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Para este dispositivo, o administrador pode controlar: configuración, acceso, aplicacións, datos e información de localización."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Máis información"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Estás conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode controlar a túa actividade na rede, mesmo os correos electrónicos, as aplicacións e os sitios web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Abrir configuración da VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"O administrador activou o rexistro na rede, que controla o tráfico do teu dispositivo.\n\nPara obter máis información, contacta co teu administrador."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Outorgaches permiso a unha aplicación para configurar unha conexión VPN.\n\nEsta aplicación pode supervisar a túa actividade na rede, incluídos os correos electrónicos, as aplicacións e os sitios web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"O teu perfil de traballo está xestionado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nO teu administrador é capaz de supervisar a túa actividade na rede, incluídos os correos electrónicos, as aplicacións e os sitios web.\n\nPara obter máis información, ponte en contacto co teu administrador.\n\nTamén estás conectado a unha VPN, que pode supervisar a túa actividade na rede."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Estás conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode supervisar a túa actividade na rede, incluídos os correos electrónicos, as aplicacións e os sitios web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Estás conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode supervisar a túa actividade persoal na rede, incluídos os correos electrónicos, as aplicacións e os sitios web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Estás conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode supervisar a túa actividade persoal na rede, incluídos os correos electrónicos, as aplicacións e os sitios web."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"O teu perfil de traballo está xestionado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Está conectado a <xliff:g id="APPLICATION">%2$s</xliff:g>, que pode supervisar a túa actividade na rede, incluídos os correos electrónicos, as aplicacións e os sitios web.\n\nPara obter máis información, ponte en contacto co teu administrador."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"O teu perfil de traballo está xestionado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Está conectado a <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, que pode supervisar a túa actividade na rede, incluídos os correos electrónicos, as aplicacións e os sitios web.\n\nTamén estás conectado a <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, que pode supervisar a túa actividade persoal na rede."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"O dispositivo permanecerá bloqueado ata que o desbloquees manualmente"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Recibir notificacións máis rápido"</string>
@@ -442,10 +456,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Ampliar"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Contraer"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"A pantalla está fixada"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"A pantalla manterase visible ata que a soltes. Para facelo, mantén premido Atrás e Visión xeral."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"A pantalla manterase visible ata que a soltes. Para facelo, mantén premido Visión xeral."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"De acordo"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Non, grazas"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Queres ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +524,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Activar"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desactivar"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Cos controis de notificacións mellorados, podes asignarlles un nivel de importancia comprendido entre 0 e 5 ás notificacións dunha aplicación determinada. \n\n"<b>"Nivel 5"</b>" \n- Mostrar na parte superior da lista de notificacións. \n- Permitir interrupcións no modo de pantalla completa. \n- Mostrar sempre. \n\n"<b>"Nivel 4"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Mostrar sempre. \n\n"<b>"Nivel 3"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n\n"<b>"Nivel 2"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n- Non soar nin vibrar nunca. \n\n"<b>"Nivel 1"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n- Non soar nin vibrar nunca. \n- Ocultar na pantalla de bloqueo e na barra de estado. \n- Mostrar na parte inferior da lista de notificacións. \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas as notificacións da aplicación."</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificacións"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Deixarás de recibir estas notificacións."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notificacións da aplicación <xliff:g id="APP">%s</xliff:g> para"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Baixa"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Media"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Alta"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urxente"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Nin son nin interrupción visual"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Mostrar en silencio"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Emitir son"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Emitir son e aparecer na pantalla"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Máis opcións"</string>
     <string name="notification_done" msgid="5279426047273930175">"Feito"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Controis de notificacións de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index e86f759..540ab16 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"લૉક સ્ક્રીન."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"સેટિંગ્સ"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"વિહંગાવલોકન."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"કાર્ય લૉક સ્ક્રીન"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"બંધ કરો"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi બંધ કર્યું."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ચેતવણી"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"કાર્ય મોડ"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"રાત્રિ પ્રકાશ"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC અક્ષમ કરેલ છે"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC સક્ષમ કરેલ છે"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"કોઇ તાજેતરની આઇટમ્સ નથી"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"તમે બધું સાફ કર્યું"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ઍપ્લિકેશન માહિતી"</string>
@@ -335,6 +337,16 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"આડું વિભક્ત કરો"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ઊભું વિભક્ત કરો"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"કસ્ટમ વિભક્ત કરો"</string>
+    <!-- no translation found for recents_accessibility_dismissed (2354459747918667050) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_open (1651449827614876864) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_top (9056056469282256287) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_left (8987144699630620019) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_right (275069779299592867) -->
+    <skip />
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ચાર્જ થઈ ગયું"</string>
@@ -415,20 +427,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN ડિસ્કનેક્ટ કરો"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"તમારું ઉપકરણ <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> દ્વારા સંચાલિત થાય છે."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, તમારા ઉપકરણનું સંચાલન કરવા માટે <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> નો ઉપયોગ કરે છે."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"તમારા વ્યવસ્થાપક સેટિંગ્સ, કૉર્પોરેટ ઍક્સેસ, ઍપ્સ, તમારા ઉપકરણ સાથે સંકળાયેલ ડેટા અને તમારા ઉપકરણની સ્થાન માહિતીને મૉનિટર અને સંચાલિત કરી શકે છે."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"વ્યવસ્થાપક સેટિંગ્સ, કૉર્પોરેટ ઍક્સેસ, ઍપ્સ, તમારા ઉપકરણ સંબંદ્ધ ડેટા અને ઉપકરણની સ્થાન માહિતીનું નિરીક્ષણ અને સંચાલન કરી શકે છે."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"વધુ જાણો"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"તમે <xliff:g id="VPN_APP">%1$s</xliff:g> સાથે કનેક્ટ થયાં છો, જે ઇમેઇલ્સ, ઍપ્લિકેશનો અને વેબસાઇટ્સ સહિત તમારી નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN સેટિંગ્સ ખોલો"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"તમારા વ્યવસ્થાપકે નેટવર્ક લૉગિંગ ચાલુ કરેલ છે, જે તમારા ઉપકરણ પર ટ્રાફિકને મૉનિટર કરે છે.\n\nવધુ માહિતી માટે, તમારા વ્યવસ્થાપકનો સંપર્ક કરો."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"તમારા વ્યવસ્થાપકે નેટવર્ક લૉગિંગ ચાલુ કર્યુ છે, જે તમારા ઉપકરણ પર ટ્રાફિકનું નિરીક્ષણ કરે છે.\n\nવધુ માહિતી માટે, તમારા વ્યવસ્થાપકનો સંપર્ક કરો."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"તમે VPN કનેક્શન સેટ કરવા માટે ઍપ્લિકેશન પરવાનગી આપી.\n\nઆ ઍપ્લિકેશન ઇમેઇલ્સ, ઍપ્લિકેશનો અને વેબસાઇટ્સ સહિત તમારા ઉપકરણ અને નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"તમારી કાર્ય પ્રોફાઇલ <xliff:g id="ORGANIZATION">%1$s</xliff:g> દ્વારા સંચાલિત થાય છે.\n\nતમારા વ્યવસ્થાપક ઇમેઇલ્સ, ઍપ્લિકેશનો અને વેબસાઇટ્સ સહિતની તમારી નેટવર્ક પ્રવૃત્તિને મૉનિટર કરવામાં સમર્થ છે.\n\nવધુ માહિતી માટે, તમારા વ્યવસ્થાપકનો સંપર્ક કરો.\n\nતમે VPN સાથે પણ કનેક્ટ છો, જે તમારી નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"તમારી કાર્ય પ્રોફાઇલનું સંચાલન <xliff:g id="ORGANIZATION">%1$s</xliff:g> દ્વારા કરવામાં આવે છે.\n\n તમારા વ્યવસ્થાપક ઇમેઇલ, ઍપ્લિકેશનો, અને વેબસાઇટો સહિતની તમારી કાર્ય નેટવર્ક પ્રવૃત્તિનું નિરીક્ષણ કરવામાં સક્ષમ છે.\n\nવધુ માહિતી માટે, તમારા વ્યવસ્થાપકનો સંપર્ક કરો.\n\nતમે VPN સાથે પણ કનેક્ટ કરેલ છે, જે તમારી નેટવર્ક પ્રવૃત્તિનું નિરીક્ષણ કરી શકે છે."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"તમે <xliff:g id="APPLICATION">%1$s</xliff:g> સાથે કનેક્ટ થયાં છો, જે ઇમેઇલ્સ, ઍપ્લિકેશનો અને વેબસાઇટ્સ સહિતની તમારી નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"તમે <xliff:g id="APPLICATION">%1$s</xliff:g> સાથે કનેક્ટ થયાં છો, જે ઇમેઇલ્સ, ઍપ્લિકેશનો અને વેબસાઇટ્સ સહિતની તમારી વ્યક્તિગત નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"તમે <xliff:g id="APPLICATION">%1$s</xliff:g> સાથે કનેક્ટ થયાં છો, જે ઇમેઇલ્સ, ઍપ્લિકેશનો અને વેબસાઇટ્સ સહિત તમારી વ્યક્તિગત નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"તમારી કાર્ય પ્રોફાઇલ <xliff:g id="ORGANIZATION">%1$s</xliff:g> દ્વારા સંચાલિત થાય છે. તે <xliff:g id="APPLICATION">%2$s</xliff:g> સાથે કનેક્ટ થયેલ છે, જે ઇમેઇલ્સ, ઍપ્લિકેશનો અને વેબસાઇટ્સ સહિતની તમારી કાર્ય નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે.\n\nવધુ માહિતી માટે, તમારા વ્યવસ્થાપકનો સંપર્ક કરો."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"તમારી કાર્ય પ્રોફાઇલનું સંચાલન <xliff:g id="ORGANIZATION">%1$s</xliff:g> દ્વારા કરવામાં આવે છે. તેને <xliff:g id="APPLICATION">%2$s</xliff:g> સાથે કનેક્ટ કરેલ છે, જે ઇમેઇલ, ઍપ્લિકેશનો અને વેબસાઇટો સહિતની તમારી કાર્ય નેટવર્ક પ્રવૃત્તિનું નિરીક્ષણ કરી શકે છે. \n\nવધુ માહિતી માટે, તમારા વ્યવસ્થાપકનો સંપર્ક કરો."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"તમારી કાર્ય પ્રોફાઇલ <xliff:g id="ORGANIZATION">%1$s</xliff:g> દ્વારા સંચાલિત થાય છે. તે <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> સાથે કનેક્ટ થયેલ છે, જે ઇમેઇલ્સ, ઍપ્લિકેશનો અને વેબસાઇટ્સ સહિતની તમારી કાર્ય નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે.\n\nતમે <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> સાથે પણ કનેક્ટ થયેલ છો, જે તમારી વ્યક્તિગત નેટવર્ક પ્રવૃત્તિને મૉનિટર કરી શકે છે."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"તમે ઉપકરણને મેન્યુઅલી અનલૉક કરશો નહીં ત્યાં સુધી તે લૉક રહેશે"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"વધુ ઝડપથી સૂચનાઓ મેળવો"</string>
@@ -440,10 +452,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"વિસ્તૃત કરો"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"સંકુચિત કરો"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"સ્ક્રીન પિન કરેલ છે"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. અનપિન કરવા માટે પાછળ અને વિહંગાવલોકન ટચ કરો અને પકડી રાખો."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. અનપિન કરવા માટે વિહંગાવલોકન ટચ કરો અને પકડી રાખો."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"સમજાઈ ગયું"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"નહીં આભાર"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ને છુપાવીએ?"</string>
@@ -510,28 +520,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ચાલુ"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"બંધ"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"પાવર સૂચના નિયંત્રણો સાથે, તમે ઍપ્લિકેશનની સૂચનાઓ માટે 0 થી 5 સુધીના મહત્વના સ્તરને સેટ કરી શકો છો. \n\n"<b>"સ્તર 5"</b>" \n- સૂચના સૂચિની ટોચ પર બતાવો \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 4"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 3"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n\n"<b>"સ્તર 2"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અને વાઇબ્રેશન કરશો નહીં \n\n"<b>"સ્તર 1"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n- લૉક સ્ક્રીન અને સ્થિતિ બારથી છુપાવો \n- સૂચના સૂચિના તળિયા પર બતાવો \n\n"<b>"સ્તર 0"</b>" \n- ઍપ્લિકેશનની તમામ સૂચનાઓને અવરોધિત કરો"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"સૂચનાઓ"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"તમને હવે આ સૂચનાઓ મળશે નહીં."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"આ માટે <xliff:g id="APP">%s</xliff:g> સૂચનાઓ"</string>
+    <string name="min_importance" msgid="7559703098688382595">"નિમ્ન"</string>
+    <string name="low_importance" msgid="6891335321576225228">"મધ્યમ"</string>
+    <string name="default_importance" msgid="6400766013567512061">"ઉચ્ચ"</string>
+    <string name="high_importance" msgid="730741630855788381">"તાત્કાલિક"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"કોઈ અવાજ અથવા વિઝ્યુઅલ અવરોધ નહીં"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"ચુપચાપ બતાવો"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"અવાજ કરો"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"અવાજ કરો અને સ્ક્રીન પર બતાવો"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"વધુ સેટિંગ્સ"</string>
     <string name="notification_done" msgid="5279426047273930175">"થઈ ગયું"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> સૂચના નિયંત્રણો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 13dac84..3308662 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"लॉक स्क्रीन."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"सेटिंग"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"अवलोकन."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"कार्य लॉक स्‍क्रीन"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"बंद करें"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"वाई-फ़ाई को बंद किया गया."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"कार्य मोड"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"नाइट लाइट"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC बंद है"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC चालू है"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"हाल ही का कोई आइटम नहीं"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"आपने सब कुछ साफ़ कर दिया है"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"एप्‍लिकेशन जानकारी"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज रूप से विभाजित करें"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"लम्बवत रूप से विभाजित करें"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"कस्‍टम रूप से विभाजित करें"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"खारिज करें"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"खुला"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ऊपर की ओर दो स्क्रीन बनाएं"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"बाईं ओर दो स्क्रीन बनाएं"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"दाईं ओर दो स्क्रीन बनाएं"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज हो गई है"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN डिस्‍कनेक्‍ट करें"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> आपका डिवाइस प्रबंधित करता है."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> आपका डिवाइस प्रबंधित करने के लिए <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> का उपयोग करता है."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"व्यवस्थापक डिवाइस से संबद्ध सेटिंग, कॉर्पोरेट एक्सेस, ऐप्लिकेशन, डेटा और डिवाइस की स्थान जानकारी को मॉनिटर और प्रबंधित कर सकता है."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"आपका व्यवस्थापक आपके डिवाइस से जुड़ी सेटिंग, कॉर्पोरेट एक्सेस, ऐप्लिकेशन, डेटा और आपके डिवाइस की स्थान जानकारी की निगरानी और उसका प्रबंधन कर सकता है."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"अधिक जानें"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"आप <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्लिकेशन और वेबसाइट सहित आपकी नेटवर्क गतिविधि को मॉनिटर कर सकता है."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN सेटिंग खोलें"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"आपके व्‍यवस्‍थापक ने नेटवर्क लॉगिंग को चालू कर दिया है, जो आपके डिवाइस पर ट्रैफ़िक को मॉनीटर करती है.\n\nअधिक जानकारी के लिए अपने व्‍यवस्‍थापक से संपर्क करें."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"आपके व्‍यवस्‍थापक ने नेटवर्क लॉग करना चालू कर दिया है, जो आपके डिवाइस पर ट्रैफ़िक की निगरानी करता है.\n\nअधिक जानकारी के लिए अपने व्‍यवस्‍थापक से संपर्क करें."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"आपने किसी ऐप को VPN कनेक्‍शन सेट करने की अनुमति दी है.\n\nयह ऐप ईमेल, ऐप्‍स और सुरक्षित वेबसाइटों सहित आपके डिवाइस और नेटवर्क की गतिविधि की निगरानी कर सकता है."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"आपकी कार्य प्रोफ़ाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> के द्वारा प्रबंधित है.\n\nआपका नियंत्रक ईमेल, ऐप्‍स और सुरक्षित वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी करने में सक्षम है.\n\nअधिक जानकारी के लिए, अपने नियंत्रक से संपर्क करें.\n\nआप एक ऐसे VPN से भी कनेक्‍ट हैं, जो आपकी नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> आपकी कार्य प्रोफ़ाइल को प्रबंधित करता है.\n\nआपका व्‍यवस्‍थापक ईमेल, ऐप्लिकेशन और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकता है.\n\nअधिक जानकारी के लिए अपने व्‍यवस्‍थापक से संपर्क करें.\n\nआप ऐसे VPN से भी कनेक्‍ट हैं, जो आपकी नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी व्‍यक्‍तिगत नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> से कनेक्‍ट हैं, जो ईमेल, ऐप्लिकेशन और वेबसाइट सहित आपकी व्‍यक्‍तिगत नेटवर्क गतिविधि को मॉनिटर कर सकता है."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"आपकी कार्य प्रोफ़ाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> के द्वारा प्रबंधित है. वह <xliff:g id="APPLICATION">%2$s</xliff:g> से कनेक्‍ट है, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी कार्य नेटवर्क गतिविधि की निगरानी कर सकता है.\n\nअधिक जानकारी के लिए, अपने नियंत्रक से संपर्क करें."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> आपकी कार्य प्रोफ़ाइल को प्रबंधित करता है. वह ऐसे <xliff:g id="APPLICATION">%2$s</xliff:g> से कनेक्‍ट है, जो ईमेल, ऐप्लिकेशन और वेबसाइटों सहित आपकी कार्य नेटवर्क गतिविधि की निगरानी कर सकता है.\n\nअधिक जानकारी के लिए अपने व्यवस्थापक से संपर्क करें."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"आपकी कार्य प्रोफ़ाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> के द्वारा प्रबंधित है. वह <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> से कनेक्‍ट है, जो ईमेल, ऐप्‍स और वेबसाइटों सहित आपकी कार्य नेटवर्क गतिविधि की निगरानी कर सकता है.\n\nआप <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> से भी कनेक्‍ट हैं, जो आपकी व्‍यक्‍तिगत नेटवर्क गतिविधि की निगरानी कर सकता है."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"जब तक कि आप मैन्‍युअल रूप से अनलॉक नहीं करते तब तक डिवाइस लॉक रहेगा"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"सूचनाएं अधिक तेज़ी से प्राप्त करें"</string>
@@ -649,7 +656,7 @@
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"विवरण खोलें."</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> सेटिंग खोलें."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"सेटिंग का क्रम संपादित करें."</string>
-    <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"पृष्ठ <xliff:g id="ID_2">%2$d</xliff:g> में से <xliff:g id="ID_1">%1$d</xliff:g>"</string>
+    <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"पेज <xliff:g id="ID_2">%2$d</xliff:g> में से <xliff:g id="ID_1">%1$d</xliff:g>"</string>
     <string name="pip_phone_expand" msgid="5889780005575693909">"विस्तृत करें"</string>
     <string name="pip_phone_minimize" msgid="1079119422589131792">"छोटा करें"</string>
     <string name="pip_phone_dismiss" msgid="1305916715402775904">"खारिज करें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index f160f2df..6a2ce8c 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -186,8 +186,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Zaključavanje zaslona."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Postavke"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Pregled."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Zaključan zaslon radnog profila"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Zatvaranje"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi isključen."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozorenje <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Način rada"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Noćno svjetlo"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je onemogućen"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je omogućen"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nema nedavnih stavki"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Izbrisali ste sve"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podijeli vodoravno"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podijeli okomito"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Podijeli prilagođeno"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Odbaci"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Otvori"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podijeli zaslon na vrhu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podijeli zaslon slijeva"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podijeli zaslon zdesna"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Prekini vezu s VPN-om"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Vašim uređajem upravlja aplikacija <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> upotrebljava aplikaciju <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> za upravljanje vašim uređajem."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administrator može nadzirati postavke, korporacijski pristup, aplikacije, podatke o uređaju i lokaciji uređaja te upravljati njima"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Administrator može nadzirati postavke, korporacijski pristup, aplikacije, podatke o uređaju i lokaciji uređaja te upravljati njima"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Saznajte više"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Povezani ste s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g> koja može nadzirati vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Otvorite postavke VPN-a"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administrator je uključio mrežni zapisnik koji prati promet na vašem uređaju.\n\nViše informacija možete dobiti od administratora."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administrator je uključio mrežni zapisnik koji prati promet na vašem uređaju.\n\nViše informacija možete saznati od administratora."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Dali ste dopuštenje aplikaciji za postavljanje VPN veze.\n\nTa aplikacija može nadzirati vašu aktivnost na uređaju i mreži, uključujući e-poštu, aplikacije i web-lokacije."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Vašim poslovnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nVaš administrator može nadzirati vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije.\n\nObratite se administratoru za više informacija.\n\nPovezani ste i s VPN-om koji može nadzirati vašu aktivnost na mreži."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Vašim radnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nVaš administrator može nadzirati vašu mrežnu aktivnost, uključujući e-poštu, aplikacije i web-lokacije.\n\nViše informacija možete saznati od administratora.\n\nPovezani ste i s VPN-om koji može nadzirati vašu mrežnu aktivnost."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Povezani ste s aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g> koja može nadzirati vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Povezani ste s aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g> koja može nadzirati vašu osobnu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Povezani ste s aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g> koja može nadzirati vašu osobnu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Vašim poslovnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je s aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g> koja može nadzirati vašu poslovnu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije.\n\nObratite se svojem administratoru za više informacija."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Vašim radnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profil je povezan s aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g> koja može nadzirati vašu poslovnu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije.\n\nViše informacija možete saznati od administratora."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Vašim poslovnim profilom upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je s aplikacijom <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> koja može nadzirati vašu poslovnu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije.\n\nPovezani ste i s aplikacijom <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> koja može nadzirati vašu osobnu aktivnost na mreži."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Uređaj će ostati zaključan dok ga ručno ne otključate"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Primajte obavijesti brže"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Proširivanje"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sažimanje"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Zaslon je prikvačen"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Zaslon će tako ostati u prvom planu dok ga ne otkvačite. Dodirnite i zadržite Natrag i Pregled da biste ga otkvačili."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Zaslon će tako ostati u prvom planu dok ga ne otkvačite. Dodirnite i zadržite Pregled da biste ga otkvačili."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Shvaćam"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, hvala"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li sakriti pločicu <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Uključeno"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Isključeno"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Napredne kontrole obavijesti omogućuju vam da postavite razinu važnosti za obavijesti aplikacije od 0 do 5. \n\n"<b>"Razina 5"</b>" \n– prikaži na vrhu popisa obavijesti \n– dopusti prekide prikaza na cijelom zaslonu \n– uvijek dopusti brzi pregled \n\n"<b>"Razina 4"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– uvijek dopusti brzi pregled \n\n"<b>"Razina 3"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled\n\n"<b>"Razina 2"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled \n– nikad ne emitiraj zvuk ni vibraciju \n\n"<b>"Razina 1"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled \n– nikad ne emitiraj zvuk ni vibraciju \n– ne prikazuj na zaključanom zaslonu i traci statusa \n– prikaži na dnu popisa obavijesti \n\n"<b>"Razina 0"</b>" \n– blokiraj sve obavijesti aplikacije"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Obavijesti"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Više nećete primati te obavijesti."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Obavijesti aplikacije <xliff:g id="APP">%s</xliff:g> za"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Niski"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Srednji"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Visoki"</string>
+    <string name="high_importance" msgid="730741630855788381">"Hitni"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Bez zvučnog ili vizualnog ometanja"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Prikaži tiho"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Reproduciraj zvuk"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Reproduciraj zvuk i prikaži na zaslonu"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
     <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole obavijesti za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 94b0f0e..334427f 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lezárási képernyő."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Beállítások"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Áttekintés."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Munka lezárási képernyővel"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Bezárás"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi kikapcsolva."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Figyelem! <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Munka mód"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Éjszakai fény"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Az NFC ki van kapcsolva"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Az NFC be van kapcsolva"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nincsenek mostanában használt elemek"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Mindent törölt"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Az alkalmazás adatai"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Osztott vízszintes"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Osztott függőleges"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Osztott egyéni"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Elvetés"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Megnyitás"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Osztott képernyő felülre"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Osztott képernyő balra"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Osztott képernyő jobbra"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Feltöltve"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN-kapcsolat bontása"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Az eszközt a(z) <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> kezeli."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"A(z) <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> a(z) <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> alkalmazást használja az eszközkezeléshez."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"A rendszergazda kezelheti az eszközzel kapcsolatos beállításokat, vállalati hozzáférést, alkalmazásokat, adatokat és helyadatokat."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"A rendszergazda felügyelheti és kezelheti az eszköz beállításait, vállalatszintű hozzáférését, alkalmazásait, adatait és helyadatait."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"További információ"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Ön kapcsolódik ehhez: <xliff:g id="VPN_APP">%1$s</xliff:g>, amely figyelheti hálózati tevékenységét, köztük a levelezést, az alkalmazás- és webhelyhasználatot."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN-beállítások megnyitása"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Rendszergazdája bekapcsolta a hálózati naplózást, amely figyeli eszköze forgalmát.\n\nHa további információra van szüksége, vegye fel a kapcsolatot rendszergazdájával."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"A rendszergazda bekapcsolta az eszköz forgalmát figyelő hálózati naplózást.\n\nHa további információra van szüksége, forduljon a rendszergazdához."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Engedélyezte egy alkalmazásnak, hogy VPN-kapcsolatot létesítsen.\n\nEz az alkalmazás (az e-mailekre, alkalmazásokra és a webhelyekre is kiterjedően) figyelemmel kísérheti az Ön eszközét és hálózati tevékenységét."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Munkaprofilját a következő felügyeli: <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nA rendszergazda (az e-mailekre, alkalmazásokra és a webhelyekre is kiterjedően) figyelemmel kísérheti hálózati tevékenységét.\n\nTovábbi információért forduljon a rendszergazdához.\n\nÖn egy VPN-hez is csatlakozik, amely szintén figyelemmel kísérheti hálózati tevékenységét."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Munkaprofilját a(z) <xliff:g id="ORGANIZATION">%1$s</xliff:g> kezeli.\n\nA rendszergazda felügyelheti hálózati tevékenységét, köztük az e-maileket, az alkalmazásokat és a webhelyeket.\n\nHa további információra van szüksége, forduljon a rendszergazdához.\n\nVPN-hez is kapcsolódik, amely szintén felügyelheti hálózati tevékenységét."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Csatlakoztatta a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazást, amely figyelheti hálózati tevékenységét, beleértve az e-maileket, az alkalmazásokat és a webhelyeket."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Csatlakoztatta a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazást, amely figyelheti személyes hálózati tevékenységét, beleértve az e-maileket, az alkalmazásokat és a webhelyeket."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Ön a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazáshoz csatlakozik, amely figyelheti személyes hálózati tevékenységét, beleértve az e-maileket, alkalmazásokat és webhelyeket."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Munkaprofilját a(z) <xliff:g id="ORGANIZATION">%1$s</xliff:g> felügyeli. Csatlakoztatva van hozzá a(z) <xliff:g id="APPLICATION">%2$s</xliff:g> alkalmazás, amely figyelheti az Ön hálózati tevékenységét, beleértve az e-maileket, az alkalmazásokat és a webhelyeket.\n\nTovábbi információért forduljon a rendszergazdájához."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Munkaprofilját a(z) <xliff:g id="ORGANIZATION">%1$s</xliff:g> kezeli. Össze van kapcsolva a(z) <xliff:g id="APPLICATION">%2$s</xliff:g> alkalmazással, amely felügyelheti az Ön munkahelyi hálózati tevékenységét, beleértve az e-maileket, az alkalmazásokat és a webhelyeket.\n\nHa további információra van szüksége, forduljon a rendszergazdához."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Munkaprofilját a(z) <xliff:g id="ORGANIZATION">%1$s</xliff:g> felügyeli. Csatlakoztatva van hozzá a(z) <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> alkalmazás, amely figyelheti az Ön hálózati tevékenységét, beleértve az e-maileket, az alkalmazásokat és a webhelyeket.\n\nCsatlakoztatta továbbá a(z) <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> alkalmazást, amely szintén figyelemmel kísérheti személyes hálózati tevékenységét."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Az eszköz addig zárolva marad, amíg kézileg fel nem oldja"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Gyorsabban megkaphatja az értesítéseket"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Kibontás"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Összecsukás"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"A képernyő rögzítve van"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Megjelenítve tartja addig, amíg Ön fel nem oldja a rögzítést. A feloldáshoz tartsa lenyomva a Vissza és az Áttekintés lehetőséget."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Megjelenítve tartja addig, amíg Ön fel nem oldja a rögzítést. A feloldáshoz tartsa lenyomva az Áttekintés lehetőséget."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Értem"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nem, köszönöm"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Elrejti ezt: <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Bekapcsolva"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Kikapcsolva"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Az értesítési beállítások révén 0-tól 5-ig állíthatja be a fontossági szintet az alkalmazás értesítéseinél. \n\n"<b>"5. szint"</b>" \n– Megjelenítés az értesítési lista tetején \n– Teljes képernyő megszakításának engedélyezése \n– Mindig felugrik \n\n"<b>"4. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Mindig felugrik \n\n"<b>"3. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n\n"<b>"2. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n– Soha nincs hangjelzés és rezgés \n\n"<b>"1. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n– Soha nincs hangjelzés vagy rezgés \n– Elrejtés a lezárási képernyőről és az állapotsávról \n– Megjelenítés az értesítési lista alján \n\n"<b>"0. szint"</b>" \n– Az alkalmazás összes értesítésének letiltása"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Értesítések"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Többé nem jelennek meg ezek az értesítések."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g>-értesítések"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Nem fontos"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Közepesen fontos"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Fontos"</string>
+    <string name="high_importance" msgid="730741630855788381">"Sürgős"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Hangjelzés és vizuális megszakítás nélkül"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Megjelenítés hangjelzés nélkül"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Hangjelzés"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Hangjelzés és felugró értesítés a képernyőn"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"További beállítások"</string>
     <string name="notification_done" msgid="5279426047273930175">"Kész"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-értesítések vezérlői"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 8889a96..ef351bb9 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Էկրանի կողպում:"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Կարգավորումներ"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Համատեսք"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Աշխատանքային պրոֆիլի կողպէկրան"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Փակել"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>:"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi-ն անջատվեց:"</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> զգուշացում"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Աշխատանքային ռեժիմ"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Գիշերային լույս"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC-ն անջատված է"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC-ն միացված է"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Վերջին տարրեր չկան"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Դուք ջնջել եք ամենը"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Հավելվածի մասին"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Հորիզոնական տրոհում"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Ուղղահայաց տրոհում"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Հատուկ տրոհում"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Փակել"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Բացել"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Տրոհել էկրանը վերևից"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Տրոհել էկրանը ձախից"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Տրոհել էկրանն աջից"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Լիցքավորված է"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Անջատել VPN-ը"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Ձեր սարքը կառավարվում է <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> հավելվածի կողմից:"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>-ը ձեր սարքը կառավարելու համար օգտագործում է <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> հավելվածը:"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Ձեր ադմինիստրատորը կարող է վերահսկել և կառավարել ձեր սարքի հետ կապակցված կարգավորումները, կորպորատիվ մուտքը, հավելվածները և տվյալները, ինչպես նաև ձեր սարքի տեղադրության տվյալները:"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Ձեր ադմինիստրատորը կարող է վերահսկել և կառավարել ձեր սարքի հետ կապակցված կարգավորումները, կորպորատիվ մուտքը, հավելվածները և տվյալները, ինչպես նաև ձեր սարքի տեղադրության տվյալները:"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Իմանալ ավելին"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Դուք կապակցված եք <xliff:g id="VPN_APP">%1$s</xliff:g> հավելվածին, որը կարող է վերահսկել ձեր ցանցային գործողությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը:"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Բացել VPN-ի կարգավորումները"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Ձեր ադմինիստրատորը միացրել է ցանցային իրադարձությունների գրանցումը, որը վերահսկում է ձեր սարքի թրաֆիկը։\n\nԼրացուցիչ տեղեկություններ ստանալու համար դիմեք ձեր ադմինիստրատորին։"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Ձեր ադմինիստրատորը միացրել է ցանցային իրադարձությունների գրանցումը, որը վերահսկում է ձեր սարքի թրաֆիկը։\n\nԼրացուցիչ տեղեկություններ ստանալու համար դիմեք ձեր ադմինիստրատորին։"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Ինչ-որ հավելվածի թույլ եք տվել հաստատել VPN կապակցում:\n\nԱյդ հավելվածը կարող է վերահսկել ձեր սարքի և ցանցի գործունեությունը, այդ թվում նաև էլփոստի հաշիվները, հավելվածները և կայքերը:"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Աշխատանքային պրոֆիլի կառավարիչն է՝ <xliff:g id="ORGANIZATION">%1$s</xliff:g>:\n\nԱդմինիստրատորը կարող է վերահսկել ցանցում ունեցած գործունեությունը, այդ թվում նաև էլփոստի հաշիվները, հավելվածները և կայքերը:\n\nԼրացուցիչ տեղեկությունների համար դիմեք ադմինիստրատորին:\n\nՆաև ունեք VPN կապակցում, ինչը կարող է վերահսկել ձեր ցանցում կատարած գործողությունները:"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Ձեր աշխատանքային պրոֆիլի կառավարիչն է <xliff:g id="ORGANIZATION">%1$s</xliff:g> կազմակերպությունը։\n\nԱդմինիստրատորը կարող է վերահսկել ձեր ցանցային գործունեությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը։\n\nԼրացուցիչ տեղեկությունների համար դիմեք ադմինիստրատորին։\n\nԴուք կապակցված են նաև VPN ցանցին, որը կարող է վերահսկել ձեր ցանցային գործունեությունը։"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Դուք կապակցված եք <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածին, որը կարող է վերահսկել ձեր ցանցի գործունեությունը, այդ թվում նաև էլփոստի հաշիվները, հավելվածները և կայքերը:"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Դուք կապակցված եք <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածին, որը կարող է վերահսկել անձնական ցանցում կատարած ձեր գործողությունները, այդ թվում նաև էլփոստի հաշիվները, հավելվածները և կայքերը:"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Դուք կապակցված եք <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածին, որը կարող է վերահսկել անձնական ցանցում կատարած ձեր գործողությունները, այդ թվում նաև էլփոստի հաշիվները, հավելվածները և կայքերը:"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Աշխատանքային պրոֆիլի կառավարիչն է՝ <xliff:g id="ORGANIZATION">%1$s</xliff:g>: Այն կապակցված է <xliff:g id="APPLICATION">%2$s</xliff:g> հավելվածին, որը կարող է վերահսկել աշխատանքային ցանցում կատարած գործունեությունը, այդ թվում նաև էլփոստի հաշիվները, հավելվածները և կայքերը:\n\nԼրացուցիչ տեղեկությունների համար դիմեք ադմինիստրատորին:"</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Ձեր աշխատանքային պրոֆիլի կառավարիչն է <xliff:g id="ORGANIZATION">%1$s</xliff:g> կազմակերպությունը։ Այն կապակցված է <xliff:g id="APPLICATION">%2$s</xliff:g> հավելվածին, որը կարող է վերահսկել ձեր ցանցային գործունեությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը։\n\nԼրացուցիչ տեղեկությունների համար դիմեք ադմինիստրատորին:"</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Աշխատանքային պրոֆիլի կառավարիչն է՝ <xliff:g id="ORGANIZATION">%1$s</xliff:g>: Այն կապակցված է <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> հավելվածին, որը կարող է վերահսկել աշխատանքային ցանցում կատարած գործունեությունը, այդ թվում նաև էլփոստի հաշիվները, հավելվածները և կայքերը:\n\nԴուք նույնպես կապակցված եք <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> հավելվածին, որը կարող է վերահսկել անձնական ցանցում կատարած ձեր գործողությունները:"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Սարքը կմնա արգելափակված՝ մինչև ձեռքով չբացեք"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Ավելի արագ ստացեք ծանուցումները"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Ընդարձակել"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Կոծկել"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Էկրանն ամրացված է"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Էկրանը կմնա տեսադաշտում, մինչև այն ապամրացնեք: Ապամրացնելու համար հպեք և պահեք Հետ և Համատեսք կոճակները:"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Էկրանը կմնա տեսադաշտում, մինչև այն ապամրացնեք: Ապամրացնելու համար հպեք և պահեք Համատեսք կոճակը:"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Եղավ"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ոչ"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Թաքցնե՞լ <xliff:g id="TILE_LABEL">%1$s</xliff:g>-ը:"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Միացնել"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Անջատել"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Ծանուցումների ընդլայնված կառավարման օգնությամբ կարող եք յուրաքանչյուր հավելվածի ծանուցումների համար նշանակել կարևորության աստիճան՝ 0-5 սահմաններում: \n\n"<b>"5-րդ աստիճան"</b>" \n- Ցուցադրել ծանուցումների ցանկի վերևում \n- Թույլատրել լիաէկրան ընդհատումները \n- Միշտ ցուցադրել կարճ ծանուցումները \n\n"<b>"4-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Միշտ ցուցադրել կարճ ծանուցումները \n\n"<b>"3-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n\n"<b>"2-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n- Անջատել ձայնը և թրթռումը \n\n"<b>"1-ին աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n- Անջատել ձայնը և թրթռումը \n- Չցուցադրել կողպէկրանում և կարգավիճակի գոտում \n- Ցուցադրել ծանուցումների ցանկի ներքևում \n\n"<b>"0-րդ աստիճան"</b>\n"- Արգելափակել հավելվածի բոլոր ծանուցումները"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Ծանուցումներ"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Այս ծանուցումներն այլևս չեք ստանա։"</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> ծանուցումներ հետևյալ ալիքի համար"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Ցածր"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Միջին"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Բարձր"</string>
+    <string name="high_importance" msgid="730741630855788381">"Շտապ"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Առանց ձայնի և տեսողական ընդհատումների"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Ցույց տալ անձայն"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Ձայն հանել"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Ձայն հանել և ցուցադրել էկրանին"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Այլ կարգավորումներ"</string>
     <string name="notification_done" msgid="5279426047273930175">"Պատրաստ է"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարներ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 2ccbda4..6ace014 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Layar kunci."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Setelan"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Ringkasan."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Layar kunci kantor"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Tutup"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi dinonaktifkan."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Peringatan <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mode kerja"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Cahaya Malam"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC dinonaktifkan"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC diaktifkan"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Tidak ada item baru-baru ini"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Anda sudah menghapus semua"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Info Aplikasi"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Pisahkan Horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pisahkan Vertikal"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Pisahkan Khusus"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Tutup"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Buka"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Pisahkan layar ke atas"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Pisahkan layar ke kiri"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Pisahkan layar ke kanan"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Terisi"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Putuskan sambungan VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Perangkat dikelola oleh <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> menggunakan <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> untuk mengelola perangkat Anda."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Admin dapat memantau dan mengelola setelan, akses perusahaan, aplikasi, data terkait perangkat, dan informasi lokasi perangkat."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Admin dapat memantau dan mengelola setelan, akses perusahaan, aplikasi, data terkait perangkat, dan informasi lokasi perangkat."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Pelajari lebih lanjut"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Anda tersambung ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Buka Setelan VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Admin telah mengaktifkan pencatatan log jaringan, yang memantau traffic di perangkat.\n\nUntuk informasi selengkapnya, hubungi admin."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Admin telah mengaktifkan pencatatan log jaringan, yang memantau traffic di perangkat.\n\nUntuk informasi selengkapnya, hubungi admin."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Anda memberikan izin kepada aplikasi untuk menyiapkan sambungan VPN.\n\nAplikasi ini ini dapat memantau aktivitas perangkat dan jaringan, termasuk email, aplikasi, dan situs web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Profil kerja dikelola oleh <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs web.\n\nUntuk informasi selengkapnya, hubungi administrator.\n\nAnda juga tersambung ke VPN yang dapat memantau aktivitas jaringan."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Profil kerja dikelola oleh <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdmin dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs web.\n\nUntuk informasi selengkapnya, hubungi admin.\n\nAnda juga tersambung ke VPN, yang dapat memantau aktivitas jaringan."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Anda tersambung ke <xliff:g id="APPLICATION">%1$s</xliff:g>, yang dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Anda tersambung ke <xliff:g id="APPLICATION">%1$s</xliff:g>, yang dapat memantau aktivitas jaringan pribadi, termasuk email, aplikasi, dan situs web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Anda tersambung ke <xliff:g id="APPLICATION">%1$s</xliff:g>, yang dapat memantau aktivitas jaringan pribadi, termasuk email, aplikasi, dan situs web.."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Profil kerja dikelola oleh <xliff:g id="ORGANIZATION">%1$s</xliff:g> dan tersambung ke <xliff:g id="APPLICATION">%2$s</xliff:g>, yang dapat memantau aktivitas jaringan kerja, termasuk email, aplikasi, dan situs web.\n\nUntuk informasi selengkapnya, hubungi administrator."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Profil kerja dikelola oleh <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profil ini terhubung ke <xliff:g id="APPLICATION">%2$s</xliff:g>, yang dapat memantau aktivitas jaringan kerja, termasuk email, aplikasi, dan situs web.\n\nUntuk informasi selengkapnya, hubungi admin."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Profil kerja dikelola oleh <xliff:g id="ORGANIZATION">%1$s</xliff:g> dan tersambung ke <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, yang dapat memantau aktivitas jaringan kerja, termasuk email, aplikasi, dan situs web.\n\nAnda juga tersambung ke <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, yang dapat memantau aktivitas jaringan pribadi."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Perangkat akan tetap terkunci hingga Anda membukanya secara manual"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Dapatkan pemberitahuan lebih cepat"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 9d2d43f..8538305 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lásskjár."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Stillingar"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Yfirlit."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Vinnulásskjár"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Loka"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Slökkt á Wi-Fi."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> viðvörun"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Vinnustilling"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Næturljós"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Slökkt á NFC"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Kveikt á NFC"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Engin nýleg atriði"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Þú hefur hreinsað allt"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Forritsupplýsingar"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Lárétt skipting"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Lóðrétt skipting"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Sérsniðin skipting"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Hunsa"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Opna"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Skipta skjá að ofanverðu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Skipta skjá til vinstri"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Skipta skjá til hægri"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Fullhlaðin"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Aftengja VPN-net"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Þessu tæki er stýrt af <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> notar <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> til að stýra tækinu þínu."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Kerfisstjóri getur fylgst með og stjórnað stillingum, fyrirtækjaaðgangi, forritum, gögnum tengdum tækinu og staðsetningu tækisins."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Kerfisstjóri getur fylgst með og stjórnað stillingum, fyrirtækjaaðgangi, forritum, gögnum tengdum tækinu og staðsetningu tækisins."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Frekari upplýsingar"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Þú ert með tengingu við <xliff:g id="VPN_APP">%1$s</xliff:g>, sem getur fylgst með netnotkun þinni, þ. á m. tölvupósti, forritum og vefsvæðum."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Opna VPN-stillingar"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Kerfisstjóri hefur kveikt á eftirliti netkerfa, sem fylgist með netumferð á tækinu þínu.\n\nHafðu samband við kerfisstjóra til að fá frekari upplýsingar."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Kerfisstjóri hefur kveikt á eftirliti netkerfa, sem fylgist með netumferð á tækinu þínu.\n\nHafðu samband við kerfisstjóra til að fá frekari upplýsingar."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Þú veittir forriti heimild til að koma á VPN-tengingu.\n\nÞetta forrit getur fylgst með virkni þinni í tækinu og á netinu, þar á meðal tölvupósti, forritum og vefsvæðum."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Vinnusniðinu þínu er stjórnað af <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nKerfisstjórinn þinn getur fylgst með netnotkun þinni, þar á meðal tölvupósti, forritum og vefsvæðum.\n\nHafðu samband við kerfisstjórann til að fá frekari upplýsingar.\n\nÞú ert einnig með tengingu við VPN-net sem getur fylgst með netnotkun þinni."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> stýrir vinnusniðinu þínu.\n\nKerfisstjórinn getur fylgst með virkni þinni á netinu, þ.m.t. tölvupósti, forritum og vefsvæðum.\n\nHafðu samband við kerfisstjórann til að fá frekari upplýsingar.\n\nÞú ert einnig með VPN-tengingu, sem getur fylgst með virkni þinni á netinu."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Þú ert með tengingu við <xliff:g id="APPLICATION">%1$s</xliff:g>, sem getur fylgst með netnotkun þinni, þ. á m. tölvupósti, forritum og vefsvæðum."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Þú ert með tengingu við <xliff:g id="APPLICATION">%1$s</xliff:g>, sem getur fylgst með persónulegri netnotkun þinni, þ. á m. tölvupósti, forritum og vefsvæðum."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Þú ert með tengingu við <xliff:g id="APPLICATION">%1$s</xliff:g>, sem getur fylgst með persónulegri netnotkun þinni, þ. á m. tölvupósti, forritum og vefsvæðum."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Vinnusniðinu þínu er stjórnað af <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Það er tengt <xliff:g id="APPLICATION">%2$s</xliff:g>, sem getur fylgst með vinnutengdri netnotkun þinni, þar á meðal tölvupósti, forritum og vefsvæðum.\n\nHafðu samband við kerfisstjórann til að fá frekari upplýsingar."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> stýrir vinnusniðinu þínu. Það er tengt við <xliff:g id="APPLICATION">%2$s</xliff:g>, sem getur fylgst með netvirkni þinni í vinnunni, þar á meðal tölvupósti, forritum og vefsvæðum.\n\nHafðu samband við kerfisstjóra til að fá frekari upplýsingar."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Vinnusniðinu þínu er stjórnað af <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Það er tengt <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, sem getur fylgst með vinnutengdri netnotkun þinni, þar á meðal tölvupósti, forritum og vefsvæðum.\n\nÞú ert einnig með tengingu við <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, sem getur fylgst með persónulegri netnotkun þinni."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Tækið verður læst þar til þú opnar það handvirkt"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Fáðu tilkynningar hraðar"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Stækka"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Minnka"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skjárinn er festur"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Þetta heldur þessu opnu þangað til þú losar það. Haltu fingri á „Til baka“ og „Yfirlit“ til að losa."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Þetta heldur þessu opnu þangað til þú losar það. Haltu fingri á „Yfirlit“ til að losa."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Ég skil"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nei, takk"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fela <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Kveikt"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Slökkt"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Með orkutilkynningastýringum geturðu stillt mikilvægi frá 0 upp í 5 fyrir tilkynningar forrita. \n\n"<b>"Stig 5"</b>" \n- Sýna efst á tilkynningalista \n- Leyfa truflun þegar birt er á öllum skjánum \n- Kíkja alltaf \n\n"<b>"Stig 4"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja alltaf \n\n"<b>"Stig 3"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n\n"<b>"Stig 2"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n- Slökkva á hljóði og titringi \n\n"<b>"Stig 1"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n- Slökkva á hljóði og titringi \n- Fela á lásskjá og stöðustiku \n- Sýna neðst á tilkynningalista \n\n"<b>"Stig 0"</b>" \n- Setja allar tilkynningar frá forriti á bannlista"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Tilkynningar"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Þú færð þessar tilkynningar ekki framar."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Tilkynningar frá <xliff:g id="APP">%s</xliff:g> fyrir"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Ekki mikilvægt"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Í meðallagi"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Mikilvægt"</string>
+    <string name="high_importance" msgid="730741630855788381">"Áríðandi"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Ekkert hljóð eða sjónræn truflun"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Sýna án hljóðs"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Spila hljóð"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Spila hljóð og birta sprettitilkynningu"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Fleiri stillingar"</string>
     <string name="notification_done" msgid="5279426047273930175">"Lokið"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 93bbd43..3273784 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Schermata di blocco."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Impostazioni"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Panoramica."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Schermata di blocco del profilo di lavoro"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Chiudi"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi disattivato."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avviso <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modalità Lavoro"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luminosità notturna"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC non attiva"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC attiva"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nessun elemento recente"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hai cancellato tutto"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informazioni sull\'applicazione"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisione in orizzontale"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisione in verticale"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisione personalizzata"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Ignora"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Apri"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Schermo diviso in alto"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Schermo diviso a sinistra"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Schermo diviso a destra"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carica"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Scollega VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Il dispositivo è gestito dall\'app <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> utilizza l\'app <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> per gestire il dispositivo."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"L\'amministratore può monitorare e gestire impostazioni, accesso aziendale, app, dati associati al dispositivo e informazioni sulla posizione del dispositivo."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"L\'amministratore può monitorare e gestire impostazioni, accesso aziendale, app, dati associati al dispositivo e informazioni sulla posizione del dispositivo."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Ulteriori informazioni"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Sei connesso a <xliff:g id="VPN_APP">%1$s</xliff:g>, che consente di monitorare le attività di rete, inclusi siti web, email e app."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Apri impostazioni VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"L\'amministratore ha attivato i log di rete, che monitorano il traffico sul dispositivo.\n\nContatta l\'amministratore per ulteriori informazioni."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"L\'amministratore ha attivato i log di rete, che consentono di monitorare il traffico sul dispositivo.\n\nPer ulteriori informazioni, contatta l\'amministratore."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Hai autorizzato l\'app a configurare una connessione VPN.\n\nQuesta app può monitorare il tuo dispositivo e l\'attività di rete, inclusi email, app e siti web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Il tuo profilo di lavoro è gestito da <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nL\'amministratore può monitorare l\'attività di rete, inclusi email, app e siti web.\n\nPer ulteriori informazioni, contatta l\'amministratore.\n\nSei connesso anche a VPN, da cui è possibile monitorare la tua attività di rete."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Il tuo profilo di lavoro è gestito da <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nL\'amministratore può monitorare la tua attività di rete, inclusi siti web, email e app.\n\nPer ulteriori informazioni, contatta l\'amministratore.\n\nSei inoltre connesso a una VPN, da cui è possibile monitorare la tua attività di rete."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Sei connesso a <xliff:g id="APPLICATION">%1$s</xliff:g>, da cui è possibile monitorare la tua attività di rete, inclusi email, app e siti web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Sei connesso a <xliff:g id="APPLICATION">%1$s</xliff:g>, da cui è possibile monitorare la tua attività di rete personale, inclusi email, app e siti web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Sei collegato a <xliff:g id="APPLICATION">%1$s</xliff:g>, che consente di monitorare la tua attività di rete personale, inclusi siti web, email e app."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Il tuo profilo di lavoro è gestito da <xliff:g id="ORGANIZATION">%1$s</xliff:g>. È connesso a <xliff:g id="APPLICATION">%2$s</xliff:g>, da cui è possibile monitorare la tua attività di rete lavorativa, inclusi email, app e siti web.\n\nPer ulteriori informazioni, contatta il tuo amministratore."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Il tuo profilo di lavoro è gestito da <xliff:g id="ORGANIZATION">%1$s</xliff:g>. È connesso a <xliff:g id="APPLICATION">%2$s</xliff:g>, da cui è possibile monitorare la tua attività di rete lavorativa, inclusi siti web, email e app.\n\nPer ulteriori informazioni, contatta l\'amministratore."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Il tuo profilo di lavoro è gestito da <xliff:g id="ORGANIZATION">%1$s</xliff:g>. È connesso a <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, da cui è possibile monitorare la tua attività di rete lavorativa, inclusi email, app e siti web.\n\nSei connesso anche a <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, da cui è possibile monitorare la tua attività di rete personale."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Il dispositivo resterà bloccato fino allo sblocco manuale"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Ricevi notifiche più velocemente"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Espandi"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Comprimi"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"La schermata è bloccata"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"La schermata rimane visibile finché non viene disattivato il blocco su schermo. Per disattivarlo, tieni premuto Indietro e Panoramica."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"La schermata rimane visibile finché non viene disattivato il blocco su schermo. Per disattivarlo, tieni premuto Panoramica."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"No, grazie"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Nascondere <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"On"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"I controlli di gestione delle notifiche ti consentono di impostare un livello di importanza compreso tra 0 e 5 per le notifiche di un\'app. \n\n"<b>"Livello 5"</b>" \n- Mostra in cima all\'elenco di notifiche \n- Consenti l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 4"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 3"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n\n"<b>"Livello 2"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n\n"<b>"Livello 1"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n- Nascondi da schermata di blocco e barra di stato \n- Mostra in fondo all\'elenco di notifiche \n\n"<b>"Livello 0"</b>" \n- Blocca tutte le notifiche dell\'app"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifiche"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Non riceverai più queste notifiche."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notifiche di <xliff:g id="APP">%s</xliff:g> per"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Bassa"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Media"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Alta"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgente"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Senza suoneria o interruzione visiva"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Mostra silenziosamente"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Con suoneria"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Con suoneria e visualizzazione sullo schermo"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string>
     <string name="notification_done" msgid="5279426047273930175">"Fine"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Controlli di notifica per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 6b5b115..c33c536 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"מסך נעילה."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"הגדרות"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"סקירה."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"מסך נעילה בעבודה"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"סגור"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"‏Wifi כבוי."</string>
@@ -326,6 +325,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"אזהרה - <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"מצב עבודה"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"תאורת לילה"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"‏NFC מושבת"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"‏NFC מופעל"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"אין פריטים אחרונים"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"מחקת הכול"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"מידע על האפליקציה"</string>
@@ -339,6 +341,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"פיצול אופקי"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"פיצול אנכי"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"פיצול מותאם אישית"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"סגור"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"פתח"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"פיצול מסך למעלה"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"פיצול מסך לשמאל"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"פיצול מסך לימין"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"טעון"</string>
@@ -419,20 +426,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"‏נתק את ה-VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"המכשיר שלך מנוהל על ידי <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> משתמש באפליקציה <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> כדי לנהל את מכשירך."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"מנהל המערכת יכול לבצע מעקב ולנהל הגדרות, גישה ארגונית, אפליקציות ונתונים המשויכים למכשירך, ולמידע על מיקום המכשיר."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"מנהל המערכת יכול לנטר ולנהל הגדרות, גישה ארגונית, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"למידע נוסף"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"אתה מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"‏פתח את הגדרות ה-VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"מנהל המערכת הפעיל את תכונת רישום התנועה ברשת, אשר מנטרת את תנועת הנתונים במכשירך.\n\nלמידע נוסף, צור קשר עם מנהל המערכת."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"מנהל המערכת הפעיל את תכונת רישום התנועה ברשת, שמנטרת את תנועת הנתונים במכשיר.\n\nלמידע נוסף, צור קשר עם מנהל המערכת."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"‏נתת לאפליקציה כלשהי הרשאה להגדיר חיבור ‏VPN‏.\n\nהאפליקציה הזו יכולה לעקוב אחר הפעילות שלך ברשת ובמכשיר, כולל הודעות אימייל, אפליקציות ואתרים."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"‏פרופיל העבודה שלך מנוהל על ידי <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nמנהל המערכת שלך יכול לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים.\n\nלמידע נוסף, פנה למנהל המערכת שלך.\n\nאתה מחובר גם לרשת VPN, שיכולה לעקוב אחר הפעילות שלך ברשת."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"‏פרופיל העבודה שלך מנוהל על-ידי <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\n מנהל המערכת שלך יכול לעקוב אחרי הפעילות שלך ברשת, כולל פעילות באימייל, באפליקציות ובאתרים.\n\n למידע נוסף, צור קשר עם מנהל המערכת.\n\nבנוסף, אתה מחובר ל-VPN, שגם באמצעותו ניתן לעקוב אחרי הפעילות שלך ברשת."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"אתה מחובר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"אתה מחובר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת הפרטית, כולל הודעות אימייל, אפליקציות ואתרים."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"אתה מחובר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת הפרטית, כולל הודעות אימייל, אפליקציות ואתרים."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"פרופיל העבודה שלך מנוהל על ידי <xliff:g id="ORGANIZATION">%1$s</xliff:g>. הוא מחובר לאפליקציה <xliff:g id="APPLICATION">%2$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת העסקית, כולל הודעות אימייל, אפליקציות ואתרים.\n\nלמידע נוסף, פנה למנהל המערכת שלך."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"פרופיל העבודה שלך מנוהל על-ידי <xliff:g id="ORGANIZATION">%1$s</xliff:g>. הוא מחובר ל-<xliff:g id="APPLICATION">%2$s</xliff:g>, אפליקציה שיכולה לעקוב אחרי הפעילות שלך ברשת, כולל פעילות באימייל, באפליקציות ובאתרים.\n\nלמידע נוסף, צור קשר עם מנהל המערכת."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"פרופיל העבודה שלך מנוהל על ידי <xliff:g id="ORGANIZATION">%1$s</xliff:g>. הוא מחובר לאפליקציה <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת העסקית, כולל הודעות אימייל, אפליקציות ואתרים.\n\nאתה מחובר גם לאפליקציה <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת הפרטית."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"המכשיר יישאר נעול עד שתבטל את נעילתו באופן ידני"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"קבל התראות מהר יותר"</string>
@@ -444,10 +451,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"הרחב"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"כווץ"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"המסך מוצמד"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"נשאר בתצוגה עד לביטול ההצמדה. גע בלחצנים \'הקודם\' ו\'סקירה\' והחזק כדי לבטל את ההצמדה."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"נשאר בתצוגה עד לביטול ההצמדה. גע בלחצן \'סקירה\' והחזק כדי לבטל את ההצמדה."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"הבנתי"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"לא, תודה"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"להסתיר<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -514,28 +519,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"פועל"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"כבוי"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"בעזרת פקדים של הודעות הפעלה, תוכל להגדיר רמת חשיבות מ-0 עד 5 להודעות אפליקציה. \n\n"<b>"רמה 5"</b>" \n- הצג בראש רשימת ההודעות \n- אפשר הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 4"</b>" \n- מנע הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 3"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n\n"<b>"רמה 2"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n\n"<b>"רמה 1"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n- הסתר ממסך הנעילה ומשורת הסטטוס \n- הצג בתחתית רשימת ההודעות \n\n"<b>"רמה 0"</b>" \n- חסום את כל ההודעות מהאפליקציה"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"הודעות"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"לא תקבל את ההודעות האלה יותר."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"הודעות <xliff:g id="APP">%s</xliff:g> עבור"</string>
+    <string name="min_importance" msgid="7559703098688382595">"נמוכה"</string>
+    <string name="low_importance" msgid="6891335321576225228">"בינונית"</string>
+    <string name="default_importance" msgid="6400766013567512061">"גבוהה"</string>
+    <string name="high_importance" msgid="730741630855788381">"דחופה"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"ללא צליל וללא הפרעה ויזואלית"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"הצג ללא צליל"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"השמע צליל"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"השמע צליל והצג במסך"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"הגדרות נוספות"</string>
     <string name="notification_done" msgid="5279426047273930175">"סיום"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> פקדי הודעות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index aa52801..97d5834 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ロック画面"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"設定"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"最近"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"仕事用プロファイルのロック画面"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"閉じる"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-FiをOFFにしました。"</string>
@@ -324,6 +323,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"警告: 上限は<xliff:g id="DATA_LIMIT">%s</xliff:g>です"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work モード"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"読書灯"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"最近のタスクはありません"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"すべてのタスクを消去しました"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"アプリ情報"</string>
@@ -337,6 +342,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"横に分割"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"縦に分割"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"分割(カスタム)"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"閉じる"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"開く"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"画面を上に分割"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"画面を左に分割"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"画面を右に分割"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"充電が完了しました"</string>
@@ -417,20 +427,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPNを切断"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"この端末は <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>で管理されています。"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> は <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>を使用してこの端末を管理しています。"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"管理者は、この端末に関連付けられた設定、コーポレート アクセス、アプリ、データと、端末の位置情報を監視および管理できます。"</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"詳細"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"「<xliff:g id="VPN_APP">%1$s</xliff:g>」に接続しています。このアプリはあなたのネットワーク アクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN 設定を開く"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"管理者がネットワーク ログを有効にしているため、この端末のトラフィックは監視されています。\n\n詳しくは、管理者にお問い合わせください。"</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"アプリにVPN接続の設定を許可しました。\n\nこのアプリはあなたの端末やネットワークアクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"この仕事用プロファイルは<xliff:g id="ORGANIZATION">%1$s</xliff:g>によって管理されています。\n\n管理者はあなたのネットワークアクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。\n\n詳しくは管理者にお問い合わせください。\n\nVPNにも接続しているため、VPNもネットワークアクティビティを監視できます。"</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"<xliff:g id="APPLICATION">%1$s</xliff:g>に接続しています。このアプリはあなたのネットワークアクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g>に接続しています。このアプリはあなたの個人のネットワークアクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"「<xliff:g id="APPLICATION">%1$s</xliff:g>」に接続しています。このアプリはあなたの個人のネットワーク アクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"この仕事用プロファイルは<xliff:g id="ORGANIZATION">%1$s</xliff:g>によって管理され、<xliff:g id="APPLICATION">%2$s</xliff:g>に接続しています。このアプリはあなたの仕事のネットワークアクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。\n\n詳しくは管理者にお問い合わせください。"</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"この仕事用プロファイルは<xliff:g id="ORGANIZATION">%1$s</xliff:g>によって管理され、<xliff:g id="APPLICATION_WORK">%2$s</xliff:g>に接続しています。このアプリはあなたの仕事のネットワークアクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。\n\n<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>にも接続しているため、個人のネットワークアクティビティも監視できます。"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"手動でロックを解除するまでロックされたままとなります"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"通知をすばやく確認できます"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index bb73c4b..9e65155 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ეკრანის დაბლოკვა."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"პარამეტრები"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"მიმოხილვა"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"სამსახურის ჩაკეტილი ეკრანი"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"დახურვა"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi გამორთულია."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> გაფრთხილება"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"სამსახურის რეჟიმი"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ღამის განათება"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC გათიშულია"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ჩართულია"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ყველაფერი გასუფთავდა"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"აპლიკაციის შესახებ"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ჰორიზონტალური გაყოფა"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ვერტიკალური გაყოფა"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ინდივიდუალური გაყობა"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"დახურვა"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"გახსნა"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ეკრანის გაყოფა ზემოთ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ეკრანის გაყოფა მარცხნივ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ეკრანის გაყოფა მარჯვნივ"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"დატენილია"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN-ის გათიშვა"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"თქვენს მოწყობილობას მართავს <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> იყენებს <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>-ს თქვენი მოწყობილობის სამართავად."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"თქვენს ადმინისტრატორს შეუძლია მოწყობილობასთან დაკავშირებული პარამეტრების, კორპორატიული წვდომის, აპებისა და მონაცემების, მათ შორის, თქვენი მოწყობილობის მდებარეობის ინფორმაციის, მონიტორინგი და მართვა."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"თქვენს ადმინისტრატორს შეუძლია მოწყობილობასთან დაკავშირებული პარამეტრების, კორპორაციული წვდომის, აპებისა და მონაცემების (მათ შორის, თქვენი მოწყობილობის მდებარეობის ინფორმაციის) მონიტორინგი და მართვა."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"შეიტყვეთ მეტი"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"თქვენ დაუკავშირდით <xliff:g id="VPN_APP">%1$s</xliff:g>-ს, რომელსაც თქვენი ქსელის აქტივობის, მათ შორის, ელფოსტის, აპებისა და ვებსაიტების, მონიტორინგი შეუძლია."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN-ის პარამეტრების გახსნა"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"თქვენმა ადმინისტრატორმა ქსელის ჟურნალირება ჩართო, რომელიც თქვენი მოწყობილობის ტრაფიკის მონიტორინგს ახორციელებს.\n\nდამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"თქვენმა ადმინისტრატორმა ქსელის ჟურნალირება ჩართო, რომელიც თქვენი მოწყობილობის ტრაფიკის მონიტორინგს ახორციელებს.\n\nდამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"თქვენ მიეცით ნებართვა აპს, დააყენოს VPN კავშირი.\n\nამ აპს შეუძლია თქვენი მოწყობილობის და ქსელის აქტივობის, მათ შორის, ელფოსტის, აპებისა და ვებსაიტების მონიტორინგი."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"თქვენს სამუშაო პროფილს მართავს <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nთქვენს ადმინისტრატორს შეუძლია თქვენი ქსელის აქტივობის მონიტორინგი, მათ შორის, ელფოსტის, აპებისა და ვებ-საიტების.\n\nდამატებითი ინფორმაციისთვის, დაუკავშირდით თქვენს ადმინისტრატორს.\n\nთქვენ ასევე დაკავშირებული ხართ VPN-თან, რომელსაც შეუძლია თქვენი ქსელის აქტივობის მონიტორინგი."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"თქვენს სამსახურის პროფილს მართავს <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nთქვენს ადმინისტრატორს შეუძლია თქვენი ქსელის აქტივობის (მათ შორის, ელფოსტის, აპებისა და ვებსაიტების) მონიტორინგი.\n\nდამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს.\n\nგარდა ამისა, თქვენ დაკავშირებული ხართ VPN-თან, რომელსაც ასევე შეუძლია თქვენი ქსელის აქტივობის მონიტორინგი."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"თქვენ დაუკავშირდით <xliff:g id="APPLICATION">%1$s</xliff:g>-ს, რომელსაც შეუძლია თქვენი ქსელის აქტივობის, მათ შორის, ელფოსტის, აპებისა და ვებსაიტების მონიტორინგი."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"თქვენ დაუკავშირდით <xliff:g id="APPLICATION">%1$s</xliff:g>-ს, რომელსაც შეუძლია თქვენი პირადი ქსელის აქტივობის, მათ შორის, ელფოსტის, აპებისა და ვებსაიტების მონიტორინგი."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"თქვენ დაუკავშირდით <xliff:g id="APPLICATION">%1$s</xliff:g>-ს, რომელსაც თქვენი პირადი ქსელის აქტივობის მონიტორინგი შეუძლია, მათ შორის, ელფოსტის, აპებისა და ვებსაიტების."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"თქვენი სამუშაო პროფილი <xliff:g id="ORGANIZATION">%1$s</xliff:g>-ის მიერ იმართება. ის დაკავშირებულია <xliff:g id="APPLICATION">%2$s</xliff:g>-თან, რომელსაც შეუძლია თქვენი სამსახურის ქსელის აქტივობის, მათ შორის, ელფოსტის, აპებისა და ვებსაიტების მონიტორინგი.\n\nდამატებითი ინფორმაციისთვის მიმართეთ თქვენს ადმინისტრატორს."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"თქვენს სამსახურის პროფილს მართავს <xliff:g id="ORGANIZATION">%1$s</xliff:g>. ის დაკავშირებულია <xliff:g id="APPLICATION">%2$s</xliff:g>-თან, რომელსაც თქვენი ქსელის აქტივობის (მათ შორის, ელფოსტის, აპებისა და ვებსაიტების) მონიტორინგი შეუძლია.\n\nდამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"თქვენი სამუშაო პროფილი <xliff:g id="ORGANIZATION">%1$s</xliff:g>-ის მიერ იმართება. ის დაკავშირებულია <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>-თან, რომელსაც შეუძლია თქვენი სამსახურის ქსელის აქტივობის, მათ შორის, ელფოსტის, აპებისა და ვებსაიტების მონიტორინგი.\n\nასევე, დაკავშირებული ხართ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>-თან, რომელსაც შეუძლია თქვენი პირადი ქსელის აქტივობის მონიტორინგი."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"მოწყობილობის დარჩება ჩაკეტილი, სანამ ხელით არ გახსნით"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"შეტყობინებების უფრო სწრაფად მიღება"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"გავრცობა"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ჩაკეცვა"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"ეკრანი ჩამაგრებულია"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"ამით ის დარჩება ხედში ჩამაგრების მოხსნამდე. ჩამაგრების მოსახსნელად, ხანგრძლივად შეეხეთ „უკან და მიმოხილვა“-ს."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"ამით ის დარჩება ხედში ჩამაგრების მოხსნამდე. ჩამაგრების მოსახსნელად, ხანგრძლივად შეეხეთ „მიმოხილვა“-ს."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"გასაგებია"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"არა, გმადლობთ"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"დაიმალოს <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ჩართული"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"გამორთული"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"შეტყობინებების მართვის საშუალებების მეშვეობით, შეგიძლიათ განსაზღვროთ აპის შეტყობინებების მნიშვნელობის დონე 0-დან 5-მდე დიაპაზონში. \n\n"<b>"დონე 5"</b>" \n— შეტყობინებათა სიის თავში ჩვენება \n— სრულეკრანიანი რეჟიმის შეფერხების დაშვება \n— ეკრანზე ყოველთვის გამოჩენა \n\n"<b>"დონე 4"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე ყოველთვის გამოჩენა \n\n"<b>"დონე 3"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n\n"<b>"დონე 2"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n— ხმისა და ვიბრაციის აღკვეთა \n\n"<b>"დონე 1"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n— ხმისა და ვიბრაციის აღკვეთა \n— ჩაკეტილი ეკრანიდან და სტატუსის ზოლიდან დამალვა \n— შეტყობინებათა სიის ბოლოში ჩვენება \n\n"<b>"დონე 0"</b>" \n— აპის ყველა შეტყობინების დაბლოკვა"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"შეტყობინებები"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"ამ შეტყობინებებს აღარ მიიღებთ."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g>-ის შეტყობინებები"</string>
+    <string name="min_importance" msgid="7559703098688382595">"დაბალი"</string>
+    <string name="low_importance" msgid="6891335321576225228">"საშუალო"</string>
+    <string name="default_importance" msgid="6400766013567512061">"მაღალი"</string>
+    <string name="high_importance" msgid="730741630855788381">"სასწრაფო"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"ხმოვანი ან ვიზუალური შეფერხების გარეშე"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"უხმოდ ჩვენება"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"ხმის გამოცემა"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"ხმის გამოცემა და ეკრანზე გამოჩენა"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"დამატებითი პარამეტრები"</string>
     <string name="notification_done" msgid="5279426047273930175">"მზადაა"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> შეტყობინებების მართვის საშუალებები"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index f5bf5f5..be32860 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Бекіту экраны."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Параметрлер"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Шолу."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Әрекетті құлыптау экраны"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Жабу"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi өшірілді."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> туралы ескерту"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Жұмыс режимі"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Түнгі жарық"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC өшірулі"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC қосулы"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Жақындағы элементтер жоқ"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Сіз барлығын өшірдіңіз"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Қолданба туралы ақпарат"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Бөлінген көлденең"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Бөлінген тік"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Бөлінген теңшелетін"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Қабылдамау"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Ашу"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Экранды жоғарыға қарай бөлу"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Экранды солға қарай бөлу"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Экранды оңға қарай бөлу"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Зарядталды"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN желісін ажырату"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Құрылғыңызды <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> басқарады."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> құрылғыны <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> қолданбасымен басқарады."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Әкімші параметрлерді, корпоративтік кіру құқығын, қолданбаларды, құрылғыға қатысты деректерді, құрылғының орналасқан жер ақпаратын бақылай және басқара алады."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Әкімші параметрлерді, корпоративтік кіру рұқсаттарын, қолданбаларды, құрылғыға қатысты деректерді, құрылғының орналасқан жер ақпаратын бақылай және басқара алады."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Толығырақ"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Желідегі әрекеттерді, соның ішінде электрондық хабарларды, қолданбаларды және вебсайттарды бақылайтын <xliff:g id="VPN_APP">%1$s</xliff:g> қолданбасына қосылдыңыз."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN параметрлерін ашу"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Әкімші құрылғыдағы трафикті қадағалау үшін желі журналын жүргізуді қосып қойған.\n\nТолығырақ ақпарат алу үшін әкімшімен хабарласыңыз."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Әкімші құрылғыдағы трафикті қадағалау үшін желі журналын жүргізуді қосып қойған.\n\nТолығырақ ақпарат алу үшін әкімшімен хабарласыңыз."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Қолданбаға VPN байланысын орнату рұқсатын бердіңіз.\n\nБұл қолданба құрылғыңызды және желідегі белсенділігіңізді, соның ішінде электрондық пошталарды, қолданбаларды және веб-сайттарды бақылай алады."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Жұмыс профиліңізді <xliff:g id="ORGANIZATION">%1$s</xliff:g> басқарады.\n\nӘкімші желідегі белсенділігіңізді, соның ішінде электрондық пошталарды, қолданбаларды және веб-сайттарды бақылай алады.\n\nҚосымша ақпарат алу үшін әкімшіге хабарласыңыз.\n\nСондай-ақ сіз желідегі белсенділігіңізді бақылай алатын VPN желісіне қосылғансыз."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Жұмыс профиліңізді <xliff:g id="ORGANIZATION">%1$s</xliff:g> басқарады.\n\nӘкімші желідегі белсенділігіңізді, соның ішінде электрондық пошталарды, қолданбаларды және вебсайттарды бақылай алады.\n\nҚосымша ақпарат алу үшін әкімшіге хабарласыңыз.\n\nСондай-ақ сіз желідегі белсенділігіңізді бақылай алатын VPN желісіне қосылғансыз."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Сіз желідегі белсенділігіңізді, соның ішінде электрондық пошталарды, қолданбаларды және веб-сайттарды бақылай алатын <xliff:g id="APPLICATION">%1$s</xliff:g> қолданбасына қосылғансыз."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Сіз жеке желідегі белсенділігіңізді, соның ішінде электрондық пошталарды, қолданбаларды және веб-сайттарды бақылай алатын <xliff:g id="APPLICATION">%1$s</xliff:g> қолданбасына қосылғансыз."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Жеке желідегі әрекеттеріңізді, соның ішінде электрондық пошта хабарларын, қолданбаларды және вебсайттарды бақылай алатын <xliff:g id="APPLICATION">%1$s</xliff:g> қолданбасына қосылғансыз."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Жұмыс профиліңізді <xliff:g id="ORGANIZATION">%1$s</xliff:g> басқарады. Ол жұмыс кезінде желідегі белсенділігіңізді, соның ішінде электрондық пошталарды, қолданбаларды және веб-сайттарды бақылай алатын <xliff:g id="APPLICATION">%2$s</xliff:g> қолданбасына қосылған.\n\nҚосымша ақпарат алу үшін әкімшіге хабарласыңыз."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Жұмыс профиліңізді <xliff:g id="ORGANIZATION">%1$s</xliff:g> басқарады. Ол жұмыс барысындағы желідегі белсенділігіңізді, соның ішінде электрондық хабарларды, қолданбаларды және вебсайттарды бақылай алатын <xliff:g id="APPLICATION">%2$s</xliff:g> қолданбасына қосылған.\n\nҚосымша ақпарат алу үшін әкімшіге хабарласыңыз."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Жұмыс профиліңізді <xliff:g id="ORGANIZATION">%1$s</xliff:g> басқарады. Ол желідегі белсенділігіңізді, соның ішінде электрондық пошталарды, қолданбаларды және веб-сайттарды бақылай алатын <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> қолданбасына қосылған.\n\nСондай-ақ сіз желідегі жеке белсенділігіңізді бақылай алатын <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> қолданбасына қосылғансыз."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Қолмен бекітпесін ашқанша құрылғы бекітілген күйде қалады"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Хабарландыруларды тезірек алу"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Жаю"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Жию"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Экран түйрелді"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Экран босатылғанға дейін көрсетіліп тұрады. Оны босату үшін \"Артқа\" және \"Шолу\" түймелерін басып тұрыңыз."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Экран босатылғанға дейін көрсетіліп тұрады. Оны босату үшін \"Кері\" түймесін басып тұрыңыз."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Түсіндім"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Жоқ, рақмет"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> жасыру керек пе?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Қосулы"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Өшірулі"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Қуат хабарландыруының басқару элементтерімен қолданбаның хабарландырулары үшін 0-ден бастап 5-ке дейін маңыздылық деңгейін орнатуға болады. \n\n"<b>"5-деңгей"</b>" \n- Хабарландыру тізімінің ең басында көрсету \n- Толық экранға ашылуын рұқсат ету \n- Әрдайым қалқымалы хабарландыру түрінде көрсету \n\n"<b>"4-деңгей"</b>" \n- Толық экранға шығармау \n- Әрдайым қалқымалы хабарландыру түрінде көрсету \n\n"<b>"3-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n\n"<b>"2-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n- Ешқашан дыбыс және діріл шығармау \n\n"<b>"1-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n- Ешқашан дыбыс немесе діріл шығармау \n- Құлыпталған экраннан және күйін көрсету жолағынан жасыру \n- Хабарландыру тізімінің ең астында көрсету \n\n"<b>"0-деңгей"</b>" \n- Қолданбадағы барлық хабарландыруларға тыйым салу"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Хабарландырулар"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Сізге енді бұл хабарландырулар жіберілмейді."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> хабарландырулары"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Төмен"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Орташа"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Жоғары"</string>
+    <string name="high_importance" msgid="730741630855788381">"Шұғыл"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Дыбыссыз және визуалдық кедергісіз"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Дыбыссыз көрсету"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Дыбыстық сигнал беру"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Дыбыстық сигнал беру және экранға шығару"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Қосымша параметрлер"</string>
     <string name="notification_done" msgid="5279426047273930175">"Дайын"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларды басқару элементтері"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e160283..3d87668 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ចាក់​សោ​អេក្រង់។"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ការកំណត់"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"ទិដ្ឋភាព​។"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"អេក្រង់​ចាក់​សោ​លក្ខណៈ​ការងារ"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"បិទ"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"បាន​បិទ​វ៉ាយហ្វាយ។"</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ការ​ព្រមាន"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"របៀបការងារ"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ពន្លឺពេលយប់"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"មិនមានធាតុថ្មីៗទេ"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"អ្នកបានជម្រះអ្វីៗទាំងអស់"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មាន​កម្មវិធី"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"បំបែកផ្តេក"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"បំបែកបញ្ឈរ"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"បំបែកផ្ទាល់ខ្លួន"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"បដិសេធ"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"បើក"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"បំបែក​អេក្រង់​ទៅ​ខាងលើ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"បំបែក​អេក្រង់​ទៅ​ខាងឆ្វេង"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"បំបែក​អេក្រង់​ទៅ​ខាងស្តាំ"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"បាន​បញ្ចូល​ថ្ម​​"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"ផ្ដាច់ VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"ឧបករណ៍របស់អ្នកគ្រប់គ្រងដោយ <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ។"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ប្រើប្រាស់ <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ដើម្បីគ្រប់គ្រងឧបករណ៍របស់អ្នក។"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"អ្នកគ្រប់គ្រងរបស់អ្នកអាចតាមដាន និងគ្រប់គ្រងការកំណត់ ការចូលលក្ខណៈ​ក្រុមហ៊ុន កម្មវិធី ទិន្នន័យពាក់ព័ន្ធនឹងឧបករណ៍របស់អ្នក និងព័ត៌មានទីតាំងឧបករណ៍របស់អ្នក។"</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"ស្វែងយល់បន្ថែម"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"អ្នកបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g> ដែលអាចតាមដានសកម្មភាពក្នុងបណ្តាញរបស់អ្នក រួមទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រផងដែរ។"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"បើក​ការ​កំណត់​ VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"អ្នក​គ្រប់គ្រង​របស់អ្នក​បាន​បើក​ការ​ធ្វើ​កំណត់ហេតុ​បណ្តាញ​ ដែល​វា​នឹង​តាមដាន​ចរាចរណ៍​បណ្តាញ​នៅលើ​ឧបករណ៍​របស់អ្នក។\n\nសម្រាប់​ព័ត៌មាន​បន្ថែម​ សូម​ទាក់ទង​អ្នក​គ្រប់គ្រង​របស់អ្នក។"</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"អ្នកបានអនុញ្ញាតឲ្យកម្មវិធីដំឡើងការតភ្ជាប់ VPN។\n\nកម្មវិធីនេះអាចឃ្លាំមើលឧបករណ៍ និងសកម្មភាពបណ្តាញរបស់អ្នក រាប់បញ្ចូលទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រ។"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"ប្រវត្តិការងាររបស់អ្នកត្រូវបានគ្រប់គ្រងដោយ <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nអ្នកគ្រប់គ្រងរបស់អ្នកមានលទ្ធភាពអាចឃ្លាំមើលសកម្មភាពបណ្តាញរបស់អ្នក រួមបញ្ចូលទាំងអ៊ីមែល កម្មវិធី គេហទំព័រ។\n\nសម្រាប់ព័ត៌មានបន្ថែម សូមទាក់ទងអ្នកគ្រប់គ្រងរបស់អ្នក។\n\nអ្នកក៏ត្រូវបានភ្ជាប់ជាមួួយ VPN ផងដែរ ដែលវាអាចឃ្លាំមើលសកម្មភាពបណ្តាញរបស់អ្នក។"</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"អ្នកត្រូវបានតភ្ជាប់ទៅ <xliff:g id="APPLICATION">%1$s</xliff:g> ដែលអាចឃ្លាំមើលសកម្មភាពបណ្តាញរបស់អ្នក រាប់បញ្ចូលទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រ។"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"អ្នកត្រូវបានតភ្ជាប់ទៅ <xliff:g id="APPLICATION">%1$s</xliff:g> ដែលអាចឃ្លាំមើលសកម្មភាពបណ្តាញរបស់អ្នក រាប់បញ្ចូលទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រ។"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"អ្នកត្រូវបានភ្ជាប់ទៅ <xliff:g id="APPLICATION">%1$s</xliff:g> ដែលអាចឃ្លាំមើលសកម្មភាពបណ្តាញរបស់អ្នក រាប់បញ្ចូលទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រ។"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"ប្រវត្តិរូបការងាររបស់អ្នកត្រូវបានគ្រប់គ្រងដោយ <xliff:g id="ORGANIZATION">%1$s</xliff:g>។ វាត្រូវបានតភ្ជាប់ទៅនឹង <xliff:g id="APPLICATION">%2$s</xliff:g> ដែលអាចឃ្លាំមើលសកម្មភាពបណ្តាញរបស់អ្នក រាប់បញ្ចូលទាំងអ៊ីមែល កម្មវិធី គេហទំព័រ។\n\nសម្រាប់ព័ត៌មានបន្ថែម សូមទាក់ទងអ្នកគ្រប់គ្រប់របស់អ្នក។"</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"ប្រវត្តិរូបការងាររបស់អ្នកត្រូវបានគ្រប់គ្រងដោយ <xliff:g id="ORGANIZATION">%1$s</xliff:g>។ វាត្រូវបានតភ្ជាប់ទៅនឹង <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> ដែលអាចឃ្លាំមើលសកម្មភាពបណ្តាញរបស់អ្នក រាប់បញ្ចូលទាំងអ៊ីមែល កម្មវិធី គេហទំព័រ។\n\nអ្នកក៏ត្រូវបានតភ្ជាប់ផងដែរទៅនឹង <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ដែលអាចឃ្លាំមើលសកម្មភាពបណ្តាញផ្ទាល់ខ្លួនរបស់អ្នក។"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"ឧបករណ៍​នឹង​ចាក់​សោ​រហូត​ដល់​អ្នក​ដោះ​សោ​ដោយ​ដៃ"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"ទទួល​បាន​ការ​ជូន​ដំណឹង​កាន់តែ​លឿន"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"ពង្រីក"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"បង្រួម"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"អេក្រង់​ត្រូវ​បាន​ភ្ជាប់"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"វា​នឹង​នៅតែ​បង្ហាញ រហូត​ទាល់​តែ​អ្នក​ដក​ការដៅ។ សូម​សង្កត់​ប៊ូតុង​ថយ​ក្រោយ និង​ប៊ូតុង​ទិដ្ឋភាពរួម​ឲ្យ​ជាប់ ដើម្បី​ដក​ការ​ដៅ។"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"វា​នឹង​នៅតែ​បង្ហាញ រហូត​ទាល់​តែ​អ្នក​ដក​ការ​ដៅ។ សូម​សង្កត់​ប៊ូតុង​ទិដ្ឋភាពរួម​​ឲ្យ​ជាប់ ដើម្បី​ដក​ការ​ដៅ។"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"យល់​ហើយ"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"ទេ អរគុណ"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"លាក់ <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"បើក"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"បិទ"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"ជាមួយអង្គគ្រប់គ្រងការជូនដំណឹងថាមពល អ្នកអាចកំណត់កម្រិតសំខាន់ពី 0 ទៅ 5 សម្រាប់ការជូនដំណឹងរបស់កម្មវិធី។ \n\n"<b>"កម្រិត 5"</b>" \n- បង្ហាញនៅផ្នែកខាងលើបញ្ជីជូនដំណឹង \n- អនុញ្ញាតការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 4"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 3"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 2"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n- មិនបន្លឺសំឡេង ឬញ័រ \n\n"<b>"កម្រិត 1"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n- មិនបន្លឺសំឡេង ឬញ័រ \n- លាក់ពីអេក្រង់ចាក់សោ និងរបារស្ថានភាព \n- បង្ហាញនៅផ្នែកខាងក្រោមបញ្ជីជូនដំណឹង \n\n"<b>"កម្រិត 0"</b>" \n- រារាំងការជូនដំណឹងទាំងអស់ពីកម្មវិធី"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"ការ​ជូនដំណឹង"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"អ្នក​នឹង​មិន​ទទួល​បាន​ការ​ជូនដំណឹង​ទាំងនេះ​ទៀត​ទេ។"</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"ការ​ជូន​ដំណឹង​របស់ <xliff:g id="APP">%s</xliff:g> សម្រាប់"</string>
+    <string name="min_importance" msgid="7559703098688382595">"ទាប"</string>
+    <string name="low_importance" msgid="6891335321576225228">"មធ្យម"</string>
+    <string name="default_importance" msgid="6400766013567512061">"ខ្ពស់"</string>
+    <string name="high_importance" msgid="730741630855788381">"បន្ទាន់"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"គ្មាន​សំឡេង ឬ​ការរំខាន​ដល់​ការ​មើល​ឡើយ"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"បង្ហាញ​ស្ងាត់ៗ"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"បន្លឺ​សំឡេង"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"បន្លឺ​សំឡេង និង​លេច​ឡើង​នៅ​លើ​អេក្រង់"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"ការកំណត់ច្រើនទៀត"</string>
     <string name="notification_done" msgid="5279426047273930175">"រួចរាល់"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"អង្គគ្រប់គ្រងការជូនដំណឹង <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 4073cc4..f13b8fde 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ಲಾಕ್‌ ಪರದೆ."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"ಸಮಗ್ರ ನೋಟ."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"ಕೆಲಸದ ಲಾಕ್ ಪರದೆ"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"ಮುಚ್ಚು"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"ವೈಫೈ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಎಚ್ಚರಿಕೆ"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"ಕೆಲಸದ ಮೋಡ್"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ನೈಟ್ ಲೈಟ್"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ನೀವು ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿರುವಿರಿ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ಅಡ್ಡಲಾಗಿ ವಿಭಜಿಸಿದ"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ಲಂಬವಾಗಿ ವಿಭಜಿಸಿದ"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ಕಸ್ಟಮ್ ವಿಭಜಿಸಿದ"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"ವಜಾಗೊಳಿಸಿ"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"ತೆರೆಯಿರಿ"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ಮೇಲ್ಭಾಗಕ್ಕೆ ಪರದೆಯನ್ನು ವಿಭಜಿಸಿ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ಎಡಕ್ಕೆ ಪರದೆಯನ್ನು ವಿಭಜಿಸಿ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ಬಲಕ್ಕೆ ಪರದೆಯನ್ನು ವಿಭಜಿಸಿ"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN ಸಂಪರ್ಕಕಡಿತಗೊಳಿಸಿ"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"ನಿಮ್ಮ ಸಾಧನವನ್ನು <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ನಿಂದ ನಿರ್ವಹಿಸಲಾಗಿದೆ."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸಲು <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ಅನ್ನು ಬಳಸುತ್ತದೆ."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಸೆಟ್ಟಿಂಗ್‌ಗಳು, ಕಾರ್ಪೊರೇಟ್ ಪ್ರವೇಶ, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು, ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಡೇಟಾ ಮತ್ತು ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳ ಮಾಹಿತಿಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"ನಿರ್ವಾಹಕರು ಸೆಟ್ಟಿಂಗ್‌ಗಳು, ಕಾರ್ಪೊರೇಟ್ ಪ್ರವೇಶ, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು, ನಿಮ್ಮ ಸಾಧನದ ಡೇಟಾ ಮತ್ತು ಸ್ಥಳ ಮಾಹಿತಿಯ ನಿಗಾವಣೆ ಮತ್ತು ನಿರ್ವಹಣೆ ಮಾಡಬಹುದು."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"ನೀವು ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾದ, <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ನೀವು ಸಂಪರ್ಕಗೊಂಡಿರುವಿರಿ."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಟ್ರಾಫಿಕ್ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ನೆಟ್‌ವರ್ಕ್ ಲಾಗಿನ್ ಮಾಡುವಿಕೆಯನ್ನು ಆನ್ ಮಾಡಿದ್ದಾರೆ.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗೆ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ನೆಟ್‌ವರ್ಕ್ ಲಾಗಿಂಗ್ ಆನ್ ಮಾಡಿದ್ದಾರೆ. ಇದು ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿನ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ನಿಗಾ ಇರಿಸುತ್ತದೆ.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗೆ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"ನೀವು VPN ಸಂಪರ್ಕ ಹೊಂದಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿ ನೀಡಿರುವಿರಿ.\n\nಈ ಅಪ್ಲಿಕೇಶನ್ ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"ನಿಮ್ಮ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು <xliff:g id="ORGANIZATION">%1$s</xliff:g> ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ.\n\nನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡುವ ಸಾಮರ್ಥ್ಯವನ್ನು ಹೊಂದಿದ್ದಾರೆ.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ, ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ.\n\nನಿಮ್ಮ ವೈಯಕ್ತಿಕ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾದ VPN ಗೆ ಕೂಡಾ ನೀವು ಸಂಪರ್ಕಗೊಂಡಿರುವಿರಿ."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್ ಅನ್ನು <xliff:g id="ORGANIZATION">%1$s</xliff:g> ನಿರ್ವಹಿಸುತ್ತಿದೆ.\n\nಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳೂ ಸೇರಿದಂತೆ ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್‌ ಚಟುವಟಿಕೆಯ ಮೇಲೆ ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಗಾ ಇರಿಸಬಲ್ಲರು.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ, ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ.\n\nಅಲ್ಲದೇ, ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್‌ ಚಟುವಟಿಕೆಯ ನಿಗಾ ವಹಿಸುವ VPN ಗೂ ಸಹ ನೀವು ಸಂಪರ್ಕಗೊಂಡಿರುವಿರಿ."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"ನೀವು ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾದ <xliff:g id="APPLICATION">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿರುವಿರಿ."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"ನೀವು ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾದ <xliff:g id="APPLICATION">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿರುವಿರಿ."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"ನೀವು ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು, ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾದ <xliff:g id="APPLICATION">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿರುವಿರಿ."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು <xliff:g id="ORGANIZATION">%1$s</xliff:g> ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ. ಇದು ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು, ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನಿಮ್ಮ ಕೆಲಸದ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾದ <xliff:g id="APPLICATION">%2$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ, ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್ ಅನ್ನು <xliff:g id="ORGANIZATION">%1$s</xliff:g> ನಿರ್ವಹಿಸುತ್ತಿದೆ. ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳೂ ಸೇರಿದಂತೆ ನಿಮ್ಮ ಕೆಲಸದ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲೆ ನಿಗಾ ಇರಿಸಬಲ್ಲ <xliff:g id="APPLICATION">%2$s</xliff:g> ಗೆ ಇದು ಸಂಪರ್ಕ ಹೊಂದಿದೆ.\n\nಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ, ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು <xliff:g id="ORGANIZATION">%1$s</xliff:g> ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ. ಇದು ಇಮೇಲ್‌ಗಳು, ಅಪ್ಲಿಕೇಶನ್‌ಗಳು, ಮತ್ತು ವೆಬ್‌ಸೈಟ್‌ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನಿಮ್ಮ ಕೆಲಸದ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾದ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> ಗೆ ಸಂಪರ್ಕಿತಗೊಂಡಿದೆ.\n\nನೀವು ಕೂಡಾ ವೈಯಕ್ತಿಕ ನೆಟ್‌ವರ್ಕ್ ಚಟುವಟಿಕೆಯ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದಾದ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ಗೆ ಸಂಪರ್ಕಿತಗೊಂಡಿರುವಿರಿ."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"ನೀವಾಗಿಯೇ ಅನ್‌ಲಾಕ್‌ ಮಾಡುವವರೆಗೆ ಸಾಧನವು ಲಾಕ್‌ ಆಗಿಯೇ ಇರುತ್ತದೆ"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"ವೇಗವಾಗಿ ಅಧಿಸೂಚನೆಗಳನ್ನು ಪಡೆದುಕೊಳ್ಳಿ"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"ವಿಸ್ತರಿಸು"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ಸಂಕುಚಿಸು"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"ಪರದೆಯನ್ನು ಪಿನ್ ಮಾಡಲಾಗಿದೆ"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"ನೀವು ಅನ್‌ಪಿನ್ ಮಾಡುವವರೆಗೆ ಅದನ್ನು ವೀಕ್ಷಣೆಯಲ್ಲಿಡುತ್ತದೆ. ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ ಹಾಗೂ ಅನ್‌ಪಿನ್ ಮಾಡಲು ಅವಲೋಕಿಸಿ."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"ನೀವು ಅನ್‌ಪಿನ್ ಮಾಡುವವರೆಗೆ ಅದನ್ನು ವೀಕ್ಷಣೆಯಲ್ಲಿಡುತ್ತದೆ. ಅನ್‌ಪಿನ್ ಮಾಡಲು ಅವಲೋಕನವನ್ನು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"ತಿಳಿಯಿತು"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"ಧನ್ಯವಾದಗಳು"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ಮರೆಮಾಡುವುದೇ?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ಆನ್"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ಆಫ್"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"ಪವರ್ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳ ಮೂಲಕ, ನೀವು ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಅಧಿಸೂಚನೆಗಳನ್ನು 0 ರಿಂದ 5 ರವರೆಗಿನ ಹಂತಗಳ ಪ್ರಾಮುಖ್ಯತೆಯನ್ನು ಹೊಂದಿಸಬಹುದು. \n\n"<b>"ಹಂತ 5"</b>" \n- ಮೇಲಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ಅನುಮತಿಸಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ \n\n"<b>"ಹಂತ 4"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ\n\n"<b>"ಹಂತ 3"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n\n"<b>"ಹಂತ 2"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n\n"<b>"ಹಂತ 1"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n- ಸ್ಥಿತಿ ಪಟ್ಟಿ ಮತ್ತು ಲಾಕ್ ಪರದೆಯಿಂದ ಮರೆಮಾಡಿ \n- ಕೆಳಗಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n\n"<b>"ಹಂತ 0"</b>" \n- ಅಪ್ಲಿಕೇಶನ್‌ನಿಂದ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"ಅಧಿಸೂಚನೆಗಳು"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"ನೀವು ಇನ್ನು ಮುಂದೆ ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಪಡೆಯುವುದಿಲ್ಲ."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"ಇದಕ್ಕೆ <xliff:g id="APP">%s</xliff:g> ಅಧಿಸೂಚನೆಗಳಿಗೆ"</string>
+    <string name="min_importance" msgid="7559703098688382595">"ಕಡಿಮೆ"</string>
+    <string name="low_importance" msgid="6891335321576225228">"ಮಧ್ಯಮ"</string>
+    <string name="default_importance" msgid="6400766013567512061">"ಅಧಿಕ"</string>
+    <string name="high_importance" msgid="730741630855788381">"ತುರ್ತು"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"ಯಾವುದೇ ಧ್ವನಿ ಅಥವಾ ದೃಶ್ಯ ಅಡಚಣೆಗಳಿಲ್ಲ"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"ಮೌನವಾಗಿ ತೋರಿಸಿ"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"ಧ್ವನಿ ಮಾಡಿ"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"ಪರದೆಯ ಮೇಲೆ ಧ್ವನಿಮಾಡಿ ಮತ್ತು ಪಾಪ್ ಮಾಡಿ"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="notification_done" msgid="5279426047273930175">"ಮುಗಿದಿದೆ"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index abea176..fc60f7a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"화면을 잠급니다."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"설정"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"최근 사용"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"업무용 잠금 화면"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"닫기"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi가 사용 중지되었습니다."</string>
@@ -324,6 +323,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 경고"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"작업 모드"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"야간 조명"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"최근 항목이 없습니다."</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"모든 항목을 삭제했습니다."</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"애플리케이션 정보"</string>
@@ -337,6 +342,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"수평 분할"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"수직 분할"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"맞춤 분할"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"닫기"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"열기"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"위쪽으로 화면 분할"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"왼쪽으로 화면 분할"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"오른쪽으로 화면 분할"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"충전됨"</string>
@@ -417,20 +427,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN 연결 해제"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>에서 관리하는 기기입니다."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>이(가) <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>을(를) 사용하여 내 기기를 관리합니다."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"관리자는 설정, 기업 액세스, 앱, 기기 관련 데이터 및 기기의 위치 정보를 모니터링하고 관리할 수 있습니다."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"자세히 알아보기"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"공개 VPN 설정"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"관리자가 기기 트래픽을 모니터링하는 네트워크 로깅을 사용 설정했습니다.\n\n자세한 정보는 관리자에게 문의하세요."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN 연결을 설정할 수 있는 권한을 앱에 부여했습니다.\n\n이 앱에서 이메일, 앱, 웹사이트와 같은 내 네트워크 활동 및 기기를 모니터링할 수 있습니다."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"직장 프로필은 <xliff:g id="ORGANIZATION">%1$s</xliff:g>에서 관리합니다.\n\n관리자는 이메일, 앱, 웹사이트와 같은 네트워크 활동을 모니터링할 수 있습니다.\n\n자세한 내용은 관리자에게 문의하세요.\n\n또한 VPN에 연결되어 있으며 여기에서 내 네트워크 활동을 모니터링할 수 있습니다."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"<xliff:g id="APPLICATION">%1$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 개인 네트워크 활동을 모니터링할 수 있습니다."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"직장 프로필은 <xliff:g id="ORGANIZATION">%1$s</xliff:g>에서 관리합니다. 이는 <xliff:g id="APPLICATION">%2$s</xliff:g>에 연결되어 있으며 여기에서 이메일, 앱, 웹사이트와 같은 직장 네트워크 활동을 모니터링할 수 있습니다.\n\n자세한 내용은 관리자에게 문의하세요."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"직장 프로필은 <xliff:g id="ORGANIZATION">%1$s</xliff:g>에서 관리합니다. 이는 <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>에 연결되어 있으며 여기에서 이메일, 앱, 웹사이트와 같은 직장 네트워크 활동을 모니터링할 수 있습니다.\n\n또한 <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>에 연결되어 있으며, 여기에서 내 개인 네트워크 활동을 모니터링할 수 있습니다."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"수동으로 잠금 해제할 때까지 기기가 잠금 상태로 유지됩니다."</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"알림을 더욱 빠르게 받기"</string>
@@ -442,10 +456,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"펼치기"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"접기"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"화면 고정됨"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"고정 해제할 때까지 계속 표시됩니다. 고정 해제하려면 뒤로 및 최근 사용을 길게 터치하세요."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"고정 해제할 때까지 계속 표시됩니다. 고정 해제하려면 최근 사용을 길게 터치하세요."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"확인"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"거부"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>을(를) 숨기시겠습니까?"</string>
@@ -512,28 +524,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"사용"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"사용 안함"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"전원 알림 컨트롤을 사용하면 앱 알림 관련 중요도를 0부터 5까지로 설정할 수 있습니다. \n\n"<b>"레벨 5"</b>" \n- 알림 목록 상단에 표시 \n- 전체 화면일 경우 알림 표시 허용 \n- 항상 엿보기 표시 \n\n"<b>"레벨 4"</b>" \n- 전체 화면에 알림 표시 금지 \n- 항상 엿보기 표시 \n\n"<b>"레벨 3"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n\n"<b>"레벨 2"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n- 소리나 진동으로 알리지 않음 \n\n"<b>"레벨 1"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n- 소리나 진동으로 알리지 않음 \n- 잠금 화면 및 상태 표시줄에서 숨김 \n- 알림 목록 하단에 표시 \n\n"<b>"레벨 0"</b>" \n- 앱의 모든 알림 차단"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"알림"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"더 이상 다음의 알림을 받지 않습니다."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"다음 채널의 <xliff:g id="APP">%s</xliff:g> 알림"</string>
+    <string name="min_importance" msgid="7559703098688382595">"낮음"</string>
+    <string name="low_importance" msgid="6891335321576225228">"중간"</string>
+    <string name="default_importance" msgid="6400766013567512061">"높음"</string>
+    <string name="high_importance" msgid="730741630855788381">"긴급"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"소리나 시각적인 방해 없음"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"조용히 표시"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"소리로 알림"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"소리 및 화면 표시로 알림"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"설정 더보기"</string>
     <string name="notification_done" msgid="5279426047273930175">"완료"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 관리"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 804511a..90ae2c6 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Кулпуланган экран."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Жөндөөлөр"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Көз жүгүртүү."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Жумуштун кулпуланган экраны"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Жабуу"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi өчүрүлдү."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> эскертүү"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Иштөө режими"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Түнкү жарык"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC өчүрүлгөн"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC иштетилген"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Акыркы колдонмолор жок"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Баарын тазаладыңыз"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Колдонмо жөнүндө маалымат"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Туурасынан бөлүү"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Тигинен бөлүү"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Ыңгайлаштырылган бөлүү"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Этибарга албоо"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Ачуу"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Экранды өйдө жакка бөлүү"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Экранды сол жакка бөлүү"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Экранды оң жакка бөлүү"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Кубатталды"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN\'ди ажыратуу"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Түзмөгүңүз <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> тарабынан башкарылат."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"Түзмөгүңүздү башкаруу үчүн <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> уюму <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> колдонмосун колдонот."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Администраторуңуз жөндөөлөрдү, корпоративдик кирүү мүмкүнчүлүгүн, колдонмолорду, уруксаттарды жана ушул түзмөкө байланыштуу дайындарды, ошондой эле түзмөгүңүздүн жайгашкан жери тууралуу маалыматты көзөмөлдөп жана башкара алат."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Администраторуңуз жөндөөлөрдү, корпоративдик кирүү мүмкүнчүлүгүн, колдонмолорду, уруксаттарды жана ушул түзмөкө байланыштуу дайындарды, ошондой эле түзмөгүңүздүн жайгашкан жери тууралуу маалыматты көзөмөлдөп жана башкара алат."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Кеңири маалымат"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактагы аракеттериңизди тескей турган <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташып турасыз."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN жөндөөлөрүн ачуу"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Администраторуңуз тармактын таржымалын алууну иштетти, андыктан түзмөгүңүздөгү трафик көзөмөлгө алынды.\n\nКеңири маалымат алуу үчүн администраторуңузга кайрылыңыз."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Администраторуңуз тармактын таржымалын алууну иштетти, андыктан түзмөгүңүздөгү трафик көзөмөлгө алынды.\n\nКеңири маалымат алуу үчүн администраторуңузга кайрылыңыз."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Колдонмого VPN туташуусун орнотууга уруксат бердиңиз.\n\nБул колдонмо түзмөгүңүздү жана электрондук почталар, колдонмолор жана вебсайттар сыяктуу тармактагы аракеттериңизди көзөмөлдөй алат."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Жумуш профилиңизди <xliff:g id="ORGANIZATION">%1$s</xliff:g> башкарат.\n\nАдминистраторуңуз электрондук почталар, колдонмолор жана вебсайттар сыяктуу тармактагы аракеттериңизди тескей алат.\n\nКөбүрөөк маалымат алуу үчүн, администраторуңузга кайрылыңыз.\n\nМындан тышкары, тармактагы аракеттериңизди тескей турган VPN\'ге да туташып турасыз."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Жумуш профилиңизди <xliff:g id="ORGANIZATION">%1$s</xliff:g> башкарат.\n\nАдминистраторуңуздун тармактагы аракетиңизди, анын ичинде электрондук почталар, колдонмолор жана вебсайттарды көзөмөлдөө мүмкүнчүлүгү бар.\n\nКөбүрөөк маалымат үчүн, администраторуңузга кайрылыңыз.\n\nСиз тармактагы жеке аракетиңизди көзөмөлдөй турган VPN\'ге да туташкансыз."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактык аракеттерди көзөмөлдөй турган <xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосуна туташып турасыз."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактагы жеке аракеттериңизди көзөмөлдөй турган <xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосуна туташып турасыз."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактагы жеке аракеттериңизди тескей турган <xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосуна туташып турасыз."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Жумуш профилиңизди <xliff:g id="ORGANIZATION">%1$s</xliff:g> башкарат. Ал электрондук почта, колдонмолор жана вебсайттар сыяктуу жумуш тармагыңыздагы аракеттерди көзөмөлдөй турган <xliff:g id="APPLICATION">%2$s</xliff:g> менен туташкан.\n\nКөбүрөөк маалымат алуу үчүн администраторуңузга кайрылыңыз."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Жумуш профилиңизди <xliff:g id="ORGANIZATION">%1$s</xliff:g> башкарат. Ал электрондук почта, колдонмолор жана вебсайттар сыяктуу жумуш тармагыңыздагы аракеттерди көзөмөлдөй турган <xliff:g id="APPLICATION">%2$s</xliff:g> менен туташкан.\n\nКөбүрөөк маалымат алуу үчүн администраторуңузга кайрылыңыз."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Жумуш профилиңизди <xliff:g id="ORGANIZATION">%1$s</xliff:g> башкарат. Ал электрондук почта, колдонмолор жана вебсайттар сыяктуу жумуш тармагыңыздагы аракеттерди көзөмөлдөй турган <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> менен туташкан.\n\nМындан тышкары, тармактагы жеке аракеттериңизди көзөмөлдөгөн <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> колдонмосуна туташып турасыз."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Түзмөктүн кулпусу кол менен ачылмайынча кулпуланган бойдон алат"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Эскертмелерди тезирээк алуу"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Жайып көрсөтүү"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Жыйнап коюу"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Экран кадалган"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Ал бошотулмайынча көрүнө берет. Бошотуу үчүн, \"Артка\" жана \"Карап чыгуу\" баскычтарын басып, кармап туруңуз."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ал бошотулмайынча көрүнө берет. Бошотуу үчүн, \"Карап чыгуу\" баскычын басып, кармап туруңуз."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Түшүндүм"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Жок, рахмат"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> жашырылсынбы?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Күйүк"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Өчүк"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Бул функциянын жардамы менен ар бир колдонмо үчүн эскертменин маанилүүлүк деңгээлин 0дон 5ке чейин койсоңуз болот. \n\n"<b>"5-деңгээл"</b>" \n- Эскертмелер тизмесинин башында көрсөтүлсүн \n- Эскертмелер толук экранда көрсөтүлсүн \n- Калкып чыгуучу эскертмелерге уруксат берилсин \n\n"<b>"4-деңгээл"</b>" \n- Эскертмелер толук экранда көрсөтүлбөсүн \n- Калкып чыгуучу эскертмелерге уруксат берилсин \n\n"<b>"3-деңгээл"</b>" \n- Эскертмелер толук экранда көрсөтүлбөсүн \n- Калкып чыгуучу эскертмелерге тыюу салынсын \n\n"<b>"2-деңгээл"</b>" \n- Эскертмелер толук экранда көрсөтүлбөсүн \n- Калкып чыгуучу эскертмелерге тыюу салынсын \n- Эч качан добуш чыгып же дирилдебесин \n\n"<b>"1-деңгээл"</b>" \n- Эскертмелер толук экранда көрсөтүлбөсүн \n- Калкып чыгуучу эскертмелерге тыюу салынсын \n- Эч качан добуш чыгып же дирилдебесин \n- Кулпуланган экрандан жана абал тилкесинен жашырылсын \n- Эскертмелер тизмесинин аягында көрсөтүлсүн \n\n"<b>"0-деңгээл"</b>" \n- Колдонмодон алынган бардык эскертмелер бөгөттөлсүн"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Эскертмелер"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Мындан ары бул эскертмелер сизге жөнөтүлбөйт."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> колдонмосунун төмөнкү каналга жөнөткөн эскертмелери"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Төмөн"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Орточо"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Жогору"</string>
+    <string name="high_importance" msgid="730741630855788381">"Шашылыш"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Добуш да чыгарбасын, экранда да көрсөтүлбөсүн"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Үнсүз көрсөтүлсүн"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Добуш чыгарсын"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Добуш менен экранга калкып чыксын"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Дагы жөндөөлөр"</string>
     <string name="notification_done" msgid="5279426047273930175">"Бүттү"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> эскертмесин башкаруу каражаттары"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 7eefe2e..c639fa3 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ລັອກ​ໜ້າ​ຈໍ."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ການ​ຕັ້ງ​ຄ່າ"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"​ພາບ​ຮວມ."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"ໜ້າຈໍລັອກວຽກ"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"ປິດ"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"ປິດ Wi-Fi ແລ້ວ."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"ຄຳ​ເຕືອນ <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"ໂໝດການເຮັດວຽກ"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ແສງກາງຄືນ"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is disabled"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is enabled"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ທ່ານລຶບລ້າງທຸກຢ່າງແລ້ວ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"​ຂໍ້​ມູນ​ແອັບ​ພ​ລິ​ເຄ​ຊັນ"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ການ​ແຍກ​ລວງ​ຂວາງ"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ການ​ແຍກ​ລວງ​ຕັ້ງ"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ການ​ແຍກ​ກຳ​ນົດ​ເອງ"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"ປິດໄວ້"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"ເປີດ"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Split screen to the top"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Split screen to the left"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Split screen to the right"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ສາກເຕັມແລ້ວ."</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"ຕັດ​ການ​ເຊື່ອມ​ຕໍ່ VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"ອຸປະກອນຂອງທ່ານແມ່ນຈັດການໂດຍ <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ໃຊ້ <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ເພື່ອຈັດການອຸປະກອນຂອງທ່ານ."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານສາມາດຕິດຕາມ ແລະ ຈັດການການຕັ້ງຄ່າ, ການເຂົ້າເຖິງອົງກອນ, ແອັບ, ຂໍ້ມູນທີ່ເຊື່ອມໂຍງກັບອຸປະກອນຂອງທ່ານແລະ ຂໍ້ມູນສະຖານທີ່ຂອງອຸປະກອນທ່ານໄດ້."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານສາມາດຕິດຕາມ ແລະ ຈັດການການຕັ້ງຄ່າ, ການເຂົ້າເຖິງອົງກອນ, ແອັບ, ຂໍ້ມູນທີ່ເຊື່ອມໂຍງກັບອຸປະກອນຂອງທ່ານແລະ ຂໍ້ມູນສະຖານທີ່ຂອງອຸປະກອນທ່ານໄດ້."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"ສຶກສາເພີ່ມເຕີມ"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"ທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="VPN_APP">%1$s</xliff:g> ແລ້ວ, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍ, ຮວມທັງອີເມວ, ແອັບ ແລະ ເວັບໄຊຕ່າງໆໄດ້."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"ເປີດການຕັ້ງຄ່າ VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານໄດ້ເປີດໃຊ້ການບັນທຶກເຄືອຂ່າຍໄວ້, ເຊິ່ງຈະກວດສອບທຣາບຟິກໃນອຸປະກອນຂອງທ່ານ.\n\nສຳລັບຂໍ້ມູນເພີ່ມເຕີມໃຫ້ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information, contact your admin."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"ທ່ານໄດ້ອະນຸຍາດໃຫ້ແອັບຕັ້ງການເຊື່ອມຕໍ່ VPN.\n\nແອັບນີ້ສາມາດຕິດຕາມການເຄື່ອນໄຫວຂອງອຸປະກອນ ແລະເຄືອຂ່າຍຂອງທ່ານ ເຊິ່ງລວມທັງອີເມວ, ແອັບ ແລະເວັບໄຊທ໌."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"ໂປ​ຣ​ໄຟ​ລ໌​ວຽກ​ຂອງ​ທ່ານ​ຖືກ​ຄຸ້ມ​ຄອງ​ໂດຍ <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nຜູ້​ຄວບ​ຄຸມ​ຂອງ​ທ່ານ​ສາ​ມາດ​ຕິດ​ຕາມການ​ເຄື່ອນ​ໄຫວ​ເຄືອ​ຂ່າຍ​ຂອງ​ທ່ານ​ໄດ້​ ລວມ​ທັງ​ອີ​ເມວ, ແອັບ, ແລະ​ເວັບ​ໄຊ​ທ໌​.\n\nສຳ​ລັບ​ຂໍ້​ມູນ​ເພີ່ມ​ເຕີມ, ຕິດ​ຕໍ່​ຜູ້​ຄວບ​ຄຸມ​ຂອງ​ທ່ານ.\n\nທ່ານ​ຍັງ​ເຊື່ອມ​ຕໍ່​ກັບ VPN, ເຊິ່ງ​ສາ​ມາດ​ຕິດ​ຕາມ​ກິດ​ຈະ​ກຳ​ເຄືອ​ຂ່າຍ​​ຂອງ​ທ່ານ."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nYour admin is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your admin.\n\nYou\'re also connected to a VPN, which can monitor your network activity."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"ທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="APPLICATION">%1$s</xliff:g>, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍຂອງທ່ານ ລວມທັງອີເມວ, ​ແອັບ ແລະເວັບໄຊທ໌."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"ທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="APPLICATION">%1$s</xliff:g>, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍສ່ວນຕົວຂອງທ່ານ ລວມທັງອີເມວ, ​ແອັບ ແລະເວັບໄຊທ໌."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"ທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="APPLICATION">%1$s</xliff:g> ແລ້ວ, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍສ່ວນຕົວຂອງທ່ານ ຮວມທັງອີເມວ, ​ແອັບ ແລະເວັບໄຊໄດ້."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານຖືກຄວບຄຸມໂດຍ <xliff:g id="ORGANIZATION">%1$s</xliff:g>. ມັນຖືກເຊື່ອມຕໍ່ກັບ <xliff:g id="APPLICATION">%2$s</xliff:g>, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍບ່ອນເຮັດວຽກຂອງທ່ານ ລວມທັງອີເມວ,​ ແອັບ ແລະເວັບໄຊທ໌.\n\nສຳລັບຂໍ້ມູນເພີ່ມເຕີມ, ໃຫ້ຕິດຕໍ່ຜູ້ບໍລິຫານຂອງທ່ານ."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Your work profile is managed by <xliff:g id="ORGANIZATION">%1$s</xliff:g>. It is connected to <xliff:g id="APPLICATION">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nFor more information, contact your admin."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານຖືກຈັດການໂດຍ <xliff:g id="ORGANIZATION">%1$s</xliff:g>. ມັນເຊື່ອມຕໍ່ກັບ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍບ່ອນເຮັດວຽກຂອງທ່ານ ລວມທັງອີເມວ, ແອັບ ແລະເວັບໄຊທ໌.\n\nທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍສ່ວນຕົວຂອງທ່ານ."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Device will stay locked until you manually unlock"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"ຮັບເອົາການ​ແຈ້ງເຕືອນ​ໄວຂຶ້ນ"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"ຂະຫຍາຍ"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ຫຍໍ້ລົງ"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"ປັກ​ໝຸດໜ້າ​ຈໍ​ແລ້ວ"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"ນີ້ຈະສະແດງມັນໃນໜ້າຈໍຈົນກວ່າທ່ານຈະເຊົາປັກມຸດ. ໃຫ້ແຕະປຸ່ມກັບຄືນ ແລະ ປຸ່ມພາບຮວມຄ້າງໄວ້ເພື່ອຍົກເລີກການປັກມຸດ."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"ນີ້ຈະສະແດງມັນໃນໜ້າຈໍຈົນກວ່າທ່ານຈະເຊົາປັກມຸດ. ໃຫ້ແຕະປຸ່ມພາບຮວມຄ້າງໄວ້ເພື່ອຍົກເລີກການປັກມຸດ."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"ເຂົ້າໃຈແລ້ວ"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"ບໍ່, ຂອບໃຈ"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ເຊື່ອງ <xliff:g id="TILE_LABEL">%1$s</xliff:g> ຫຼື​ບໍ່?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ເປີດ"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ປິດ"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"ດ້ວຍການຄວບຄຸມການແຈ້ງເຕືອນ, ທ່ານສາມາດຕັ້ງລະດັບຄວາມສຳຄັນຈາກ 0 ຮອດ 5 ໃຫ້ກັບການແຈ້ງເຕືອນແອັບໃດໜຶ່ງໄດ້. \n\n"<b>"ລະດັບ 5"</b>" \n- ສະແດງຢູ່ເທິງສຸດຂອງລາຍການແຈ້ງເຕືອນ \n- ອະນຸຍາດໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ແນມເບິ່ງທຸກເທື່ອ \n\n"<b>"ລະດັບ 4"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ແນມເບິ່ງທຸກເທື່ອ \n\n"<b>"ລະດັບ 3"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n\n"<b>"ລະດັບ 2"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n- ບໍ່ມີສຽງ ແລະ ບໍ່ມີການສັ່ນເຕືອນ \n\n"<b>"ລະດັບ 1"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n- ບໍ່ມີສຽງ ແລະ ບໍ່ມີການສັ່ນເຕືອນ \n- ເຊື່ອງຈາກໜ້າຈໍລັອກ ແລະ ແຖບສະຖານະ \n- ສະແດງຢູ່ລຸ່ມສຸດຂອງລາຍການແຈ້ງເຕືອນ \n\n"<b>"ລະດັບ 0"</b>" \n- ປິດກັ້ນການແຈ້ງເຕືອນທັງໝົດຈາກແອັບ"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"ການແຈ້ງເຕືອນ"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"ທ່ານຈະບໍ່ໄດ້ຮັບການແຈ້ງເຕືອນເຫຼົ່ານີ້ອີກຕໍ່ໄປ."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"ການແຈ້ງເຕືອນ <xliff:g id="APP">%s</xliff:g> ສຳລັບ"</string>
+    <string name="min_importance" msgid="7559703098688382595">"ຕໍ່າ"</string>
+    <string name="low_importance" msgid="6891335321576225228">"ປານກາງ"</string>
+    <string name="default_importance" msgid="6400766013567512061">"ສູງ"</string>
+    <string name="high_importance" msgid="730741630855788381">"ດ່ວນ"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"ບໍ່ມີສຽງ ຫຼື ການລົບກວນໃນໜ້າຈໍ"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"ສະແດງແບບງຽບໆ"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"ເຮັດສຽງ"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"ເຮັດສຽງດັງ ແລະ ສະແດງຂຶ້ນໃນໜ້າຈໍ"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"​ການ​ຕັ້ງ​ຄ່າ​ເພີ່ມ​ເຕີມ"</string>
     <string name="notification_done" msgid="5279426047273930175">"ສຳເລັດແລ້ວ"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"ການຄວບຄຸມການແຈ້ງເຕືອນ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 649e42a..a40bcb6 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Užrakinimo ekranas."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Nustatymai"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Apžvalga."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Darbo profilio užrakinimo ekranas"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Uždaryti"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"„Wi-Fi“ ryšys išjungtas."</string>
@@ -326,6 +325,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> įspėjimas"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Darbo režimas"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nakties šviesa"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"ALR"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"ALR išjungtas"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"ALR įjungtas"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nėra jokių naujausių elementų"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Viską išvalėte"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programos informacija"</string>
@@ -339,6 +341,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontalus skaidymas"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikalus skaidymas"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tinkintas skaidymas"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Atsisakyti"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Atidaryti"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Skaidyti ekraną į viršų"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Skaidyti ekraną į kairę"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Skaidyti ekraną į dešinę"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Įkrautas"</string>
@@ -419,20 +426,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Atjungti VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Įrenginį tvarko „<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>“."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"„<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>“ naudoja „<xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>“ įrenginiui tvarkyti."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administr. gali stebėti ir tvark. nustat., įmonės prieigos par., progr., su įreng. susietus duomenis ir įreng. vietovės inform."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Administrat. gali stebėti ir tvark. nustat., įmonės prieigos par., progr., su įreng. susietus duomenis ir įreng. vietovės inform."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Sužinoti daugiau"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Esate prisijungę prie programos „<xliff:g id="VPN_APP">%1$s</xliff:g>“, kuri gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Atidaryti VPN nustatymus"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administratorius įjungė tinklo duomenų įrašymą į žurnalą. Įjungus šią funkciją stebimas srautas jūsų įrenginyje.\n\nJei reikia daugiau informacijos, susisiekite su administratoriumi."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administratorius įjungė tinklo duomenų įrašymą į žurnalą. Įjungus šią funkciją stebimas srautas jūsų įrenginyje.\n\nJei reikia daugiau informacijos, susisiekite su administratoriumi."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Suteikėte programai leidimą nustatyti VPN ryšį.\n\nŠi programa gali stebėti įrenginio ir tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Jūsų darbo profilį tvarko „<xliff:g id="ORGANIZATION">%1$s</xliff:g>“.\n\nAdministratorius gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines.\n\nDaugiau informacijos galite gauti susisiekę su administratoriumi.\n\nBe to, esate prisijungę prie VPN, kuris gali stebėti tinklo veiklą."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Jūsų darbo profilį tvarko „<xliff:g id="ORGANIZATION">%1$s</xliff:g>“.\n\nJūsų administratorius gali stebėti jūsų tinklo veiklą, įskaitant el. laiškus, programas ir svetaines.\n\nJei reikia daugiau informacijos, susisiekite su administratoriumi.\n\nTaip pat esate prisijungę prie VPN, kuris gali stebėti jūsų tinklo veiklą."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Esate prisijungę prie programos „<xliff:g id="APPLICATION">%1$s</xliff:g>“, kuri gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Esate prisijungę prie programos „<xliff:g id="APPLICATION">%1$s</xliff:g>“, kuri gali stebėti asmeninio profilio tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Esate prisijungę prie programos „<xliff:g id="APPLICATION">%1$s</xliff:g>“, kuri gali stebėti asmeninio tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Darbo profilį tvarko „<xliff:g id="ORGANIZATION">%1$s</xliff:g>“. Jis susietas su programa „<xliff:g id="APPLICATION">%2$s</xliff:g>“, kuri gali stebėti darbo profilio tinklo veiklą, įskaitant el. laiškus, programas ir svetaines.\n\nDaugiau informacijos galite gauti susisiekę su administratoriumi."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Jūsų darbo profilį tvarko „<xliff:g id="ORGANIZATION">%1$s</xliff:g>“. Jis susietas su programa „<xliff:g id="APPLICATION">%2$s</xliff:g>“, kuri negali stebėti jūsų tinklo veiklos, įskaitant el. laiškus, programas ir svetaines.\n\nJei reikia daugiau informacijos, susisiekite su administratoriumi."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Darbo profilį tvarko „<xliff:g id="ORGANIZATION">%1$s</xliff:g>“. Jis susietas su programa „<xliff:g id="APPLICATION_WORK">%2$s</xliff:g>“, kuri gali stebėti darbo profilio tinklo veiklą, įskaitant el. laiškus, programas ir svetaines.\n\nTaip pat esate prisijungę prie programos „<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>“, kuri gali stebėti asmeninio profilio tinklo veiklą."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Įrenginys liks užrakintas, kol neatrakinsite jo neautomatiniu būdu"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Greičiau gaukite pranešimus"</string>
@@ -444,10 +451,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Išskleisti"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sutraukti"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekranas prisegtas"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Tai bus rodoma, kol atsegsite. Palieskite ir palaikykite „Atgal“ ir „Apžvalga“, kad atsegtumėte."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Tai bus rodoma, kol atsegsite. Palieskite ir palaikykite „Apžvalga“, kad atsegtumėte."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Supratau"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, ačiū"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Slėpti „<xliff:g id="TILE_LABEL">%1$s</xliff:g>“?"</string>
@@ -514,28 +519,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Įjungta"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Išjungta"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Naudodami pranešimų valdiklius galite nustatyti programos pranešimų svarbos lygį nuo 0 iki 5. \n\n"<b>"5 lygis"</b>" \n– Rodyti pranešimų sąrašo viršuje \n– Leisti pertraukti, kai veikia viso ekrano režimas \n– Visada rodyti pranešimus \n\n"<b>"4 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Visada rodyti pranešimus \n\n"<b>"3 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n\n"<b>"2 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n– Niekada neleisti garso ir nevibruoti \n\n"<b>"1 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n– Niekada neleisti garso ir nevibruoti \n– Slėpti užrakinimo ekrane ir būsenos juostoje \n– Rodyti pranešimų sąrašo apačioje \n\n"<b>"0 lygis"</b>" \n– Blokuoti visus programos pranešimus"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Pranešimai"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Nebegausite šių pranešimų."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"„<xliff:g id="APP">%s</xliff:g>“ pranešimai, skirti"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Nelabai svarbus"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Vidutiniškai svarbus"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Svarbus"</string>
+    <string name="high_importance" msgid="730741630855788381">"Skubus"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Neskambėti ir nepertraukti vaizdo"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Rodyti tyliai"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Skambėti"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Skambėti ir iššokti ekrane"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Daugiau nustatymų"</string>
     <string name="notification_done" msgid="5279426047273930175">"Atlikta"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index c230313..8ce3890 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -186,8 +186,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Bloķēšanas ekrāns."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Iestatījumi"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Pārskats."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Darba profila bloķēšanas ekrāns"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Aizvērt"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi ir izslēgts."</string>
@@ -324,6 +323,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> brīdinājums"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Darba režīms"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nakts režīms"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Nav nesenu vienumu"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Visi uzdevumi ir notīrīti"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informācija par lietojumprogrammu"</string>
@@ -337,6 +342,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontāls dalījums"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikāls dalījums"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Pielāgots dalījums"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Noraidīt"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Atvērt"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Sadalīt ekrānu augšdaļā"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Sadalīt ekrānu kreisajā pusē"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Sadalīt ekrānu labajā pusē"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulators uzlādēts"</string>
@@ -417,20 +427,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Atvienot VPN tīklu"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Jūsu ierīci pārvalda <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> izmanto lietotni <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> jūsu ierīces pārvaldībai."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administrators var pārraudzīt un pārvaldīt iestatījumus, korporatīvo piekļuvi, lietotnes, ierīces datus un informāciju par ierīces atrašanās vietu."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Uzzināt vairāk"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Ir izveidots savienojums ar lietotni <xliff:g id="VPN_APP">%1$s</xliff:g>, kas var pārraudzīt jūsu darbības tīklā, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Atvērt VPN iestatījumus"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administrators ir ieslēdzis tīkla reģistrēšanu, kuru izmanto, lai pārraudzītu datplūsmu jūsu ierīcē.\n\nLai iegūtu plašāku informāciju, sazinieties ar administratoru."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Jūs piešķīrāt lietotnei atļauju izveidot savienojumu ar VPN tīklu.\n\nŠī lietotne var pārraudzīt jūsu ierīcē un tīklā veiktās darbības, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Jūsu darba profilu pārvalda <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nJūsu administrators var pārraudzīt jūsu tīklā veiktās darbības, tostarp e-pasta ziņojumus, lietotnes un vietnes.\n\nLai iegūtu plašāku informāciju, sazinieties ar administratoru.\n\nIerīcē ir arī izveidots savienojums ar VPN tīklu, kurā var tikt pārraudzītas jūsu tīklā veiktās darbības."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Ir izveidots savienojums ar lietotni <xliff:g id="APPLICATION">%1$s</xliff:g>, kura var pārraudzīt jūsu tīklā veiktās darbības, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Ir izveidots savienojums ar lietotni <xliff:g id="APPLICATION">%1$s</xliff:g>, kura var pārraudzīt jūsu tīklā veiktās privātās darbības, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Ir izveidots savienojums ar lietotni <xliff:g id="APPLICATION">%1$s</xliff:g>, kas var pārraudzīt jūsu tīklā veiktās privātās darbības, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Jūsu darba profilu pārvalda <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Tas ir saistīts ar lietojumprogrammu <xliff:g id="APPLICATION">%2$s</xliff:g>, kura var pārraudzīt jūsu tīklā veiktās darbības, tostarp e-pasta ziņojumus, lietotnes un vietnes.\n\nLai iegūtu plašāku informāciju, sazinieties ar administratoru."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Jūsu darba profilu pārvalda <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Tas ir saistīts ar lietojumprogrammu <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, kura var pārraudzīt jūsu tīklā veiktās darbības, tostarp e-pasta ziņojumus, lietotnes un vietnes.\n\nIr piesaistīta arī lietojumprogramma <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, kas var pārraudzīt jūsu tīklā veiktās privātās darbības."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Ierīce būs bloķēta, līdz to manuāli atbloķēsiet."</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Saņemiet paziņojumus ātrāk"</string>
@@ -442,10 +456,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Izvērst"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Sakļaut"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekrāns ir piesprausts"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Šādi tas būs redzams līdz brīdim, kad to atspraudīsiet. Lai atspraustu, pieskarieties pogām Atpakaļ un Pārskats un turiet tās."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Šādi tas būs redzams līdz brīdim, kad to atspraudīsiet. Lai atspraustu, pieskarieties pogai Pārskats un turiet to."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Sapratu!"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nē, paldies"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vai paslēpt vienumu <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +524,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Ieslēgts"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Izslēgts"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Izmantojot barošanas paziņojumu vadīklas, varat lietotnes paziņojumiem iestatīt svarīguma līmeni (no 0 līdz 5). \n\n"<b>"5. līmenis"</b>" \n- Tiek rādīts paziņojumu saraksta augšdaļā \n- Tiek atļauta pilnekrāna režīma pārtraukšana \n- Ieskats vienmēr atļauts \n\n"<b>"4. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats vienmēr atļauts \n\n"<b>"3. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n\n"<b>"2. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n- Nav atļautas skaņas un vibrosignāls \n\n"<b>"1. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n- Nav atļautas skaņas un vibrosignāls \n- Paziņojumi tiek paslēpti bloķēšanas ekrānā un statusa joslā \n- Paziņojumi tiek rādīti paziņojumu saraksta apakšdaļā \n\n"<b>"0. līmenis"</b>" \n- Visi lietotnes paziņojumi tiek bloķēti"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Paziņojumi"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Jūs vairs nesaņemsiet šos paziņojumus."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Lietotnes <xliff:g id="APP">%s</xliff:g> paziņojumi par"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Mazsvarīgs"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Vidēji svarīgs"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Ļoti svarīgs"</string>
+    <string name="high_importance" msgid="730741630855788381">"Steidzams"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Bez skaņas signāla vai vizuāla paziņojuma"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Rādīt bez skaņas signāla"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Atskaņot skaņas signālu"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Atskaņot skaņas signālu un īslaicīgi parādīt ekrānā"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Citi iestatījumi"</string>
     <string name="notification_done" msgid="5279426047273930175">"Gatavs"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumu vadīklas"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 110812b..fa9489d 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Заклучи екран."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Поставки"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Краток преглед."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Работен заклучен екран"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Затвори"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi е исклученo."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Предупредување за <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Режим на работа"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ноќно светло"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC е оневозможено"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC е овозможено"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Нема неодамнешни ставки"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Исчистивте сѐ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информации за апликацијата"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Раздели хоризонтално"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Раздели вертикално"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Раздели прилагодено"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Отфрлете"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Отворете"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Поделен екран во горниот дел"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Поделен екран на левата страна"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Поделен екран на десната страна"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Наполнета"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Исклучи ВПН"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> управува со уредов."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ја користи <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> за да управува со вашиот уред."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Админ. може да следи и да управува со: поставки, корпоративен пристап, апликации, податоци за уредот и информации за локација."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Администраторот може да следи и да управува со: поставки, корпоративен пристап, апликации, податоци за уредот и информации за локација."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Дознајте повеќе"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Поврзани сте на <xliff:g id="VPN_APP">%1$s</xliff:g>, којашто може да ја следи вашата активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-сајтовите."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Отворете „Поставки за VPN“"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Вашиот администратор вклучил евиденција на мрежата, што подразбира следење на сообраќајот на вашиот уред.\n\nЗа повеќе информации, контактирајте со администраторот."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Вашиот администратор вклучил евиденција на мрежата, што подразбира следење на сообраќајот на вашиот уред.\n\nЗа повеќе информации, контактирајте со администраторот."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Дозволивте апликацијата да постави поврзување преку ВПН.\n\nАпликацијата може да го следи уредот и активноста на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> управува со вашиот работен профил.\n\nАдминистратор е во можност да ја следи вашата активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите.\n\nЗа повеќе информации, контактирајте со администраторот.\n\nИсто така, поврзани сте на ВПН, којашто може да ја следи вашата активност на мрежата."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> управува со вашиот работен профил.\n\nАдминистратор е во можност да ја следи вашата активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите.\n\nЗа повеќе информации, контактирајте со администраторот.\n\nYИсто така, поврзани сте на VPN којашто може да ја следи вашата активност на мрежата."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"ВПН"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Поврзани сте на <xliff:g id="APPLICATION">%1$s</xliff:g>, којашто може да ја следи вашата активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Поврзани сте на <xliff:g id="APPLICATION">%1$s</xliff:g>, којашто може да ја следи вашата лична активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Поврзани сте на <xliff:g id="APPLICATION">%1$s</xliff:g>, којашто може да ја следи вашата лична активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> управува со вашиот работен профил. Истиот е поврзан на <xliff:g id="APPLICATION">%2$s</xliff:g>, којашто може да ја следи вашата работна активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите.\n\nЗа повеќе информации, контактирајте со администраторот."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> управува со вашиот работен профил. Истиот е поврзан на <xliff:g id="APPLICATION">%2$s</xliff:g>, којашто може да ја следи вашата работна активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите.\n\nЗа повеќе информации, контактирајте со администраторот."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> управува со вашиот работен профил. Истиот е поврзан на <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, којашто може да ја следи вашата работна активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-локациите.\n\nВие исто така сте поврзани на <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, којашто може да ја следи вашата лична активност на мрежата."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Уредот ќе остане заклучен додека рачно не го отклучите"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Добивајте известувања побрзо"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Прошири"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Собери"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Екранот е прикачен"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Ќе се гледа сѐ додека не го откачите. Допрете и држете „Назад“ и „Краток преглед“ за откачување."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ќе се гледа сѐ додека не го откачите. Допрете и држете „Краток преглед“ за откачување."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Сфатив"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Не, фала"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Сокриј <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Вклучено"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Исклучено"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Со контролите за известувањата за напојување, може да поставите ниво на важност од 0 до 5 за известувањата на која било апликација. \n\n"<b>"Ниво 5"</b>" \n- Прикажувај на врвот на списокот со известувања \n- Дозволи прекин во цел екран \n- Секогаш користи појавување \n\n"<b>"Ниво 4"</b>" \n- Спречи прекин во цел екран \n- Секогаш користи појавување \n\n"<b>"Ниво 3"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n\n"<b>"Ниво 2"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n- Без звук и вибрации \n\n"<b>"Ниво 1"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n- Без звук и вибрации \n- Сокриј од заклучен екран и статусна лента \n- Прикажувај на дното на списокот со известувања \n\n"<b>"Ниво 0"</b>" \n- Блокирај ги сите известувања од апликацијата"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Известувања"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Веќе нема да ги добивате овие известувања."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Известувања од <xliff:g id="APP">%s</xliff:g> за"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Ниско"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Средно"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Високо"</string>
+    <string name="high_importance" msgid="730741630855788381">"Итно"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Без звук или визуелен прекин"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Прикажи тивко"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Испушти звук"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Испушти звук и прикажи го на екранот"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Повеќе поставки"</string>
     <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Контроли за известувања на <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 4c4a99b..313bf9c 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ലോക്ക് സ്‌ക്രീൻ."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ക്രമീകരണം"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"കാഴ്ച."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"ഔദ്യോഗിക ലോക്ക് സ്ക്രീൻ"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"അടയ്‌ക്കുക"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"വൈഫൈ ഓഫാക്കി."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> മുന്നറിയിപ്പ്"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"പ്രവർത്തന മോഡ്"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"നൈറ്റ് ലൈറ്റ്"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC പ്രവർത്തനരഹിതമാക്കി"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC പ്രവർത്തനക്ഷമമാക്കി"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"നിങ്ങൾ എല്ലാം മായ്ച്ചിരിക്കുന്നു"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ആപ്പ് വിവരം"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"തിരശ്ചീനമായി വേർതിരിക്കുക"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ലംബമായി വേർതിരിക്കുക"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ഇഷ്‌ടാനുസൃതമായി വേർതിരിക്കുക"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"ഒഴിവാക്കുക"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"തുറക്കുക"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"സ്ക്രീൻ മുകളിലേക്ക് പിളർത്തുക"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"സ്ക്രീൻ ഇടതുവശത്തേക്ക് പിളർത്തുക"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"സ്ക്രീൻ വലതുവശത്തേക്ക് പിളർത്തുക"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ചാർജായി"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN വിച്‌ഛേദിക്കുക"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"നിങ്ങളുടെ ഉപകരണം മാനേജുചെയ്യുന്നത് <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ആണ്."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"നിങ്ങളുടെ ഉപകരണം മാനേജുചെയ്യാൻ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ഉപയോഗിക്കുന്നത് <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ആണ്."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"ഈ ഉപകരണവുമായി ബന്ധപ്പെട്ട ക്രമീകരണം, കോർപ്പറേറ്റ് ആക്‌സസ്സ്, ആപ്‌സ്, വിവരങ്ങൾ എന്നിവയും  ഉപകരണത്തിന്റെ ലൊക്കേഷൻ വിവരവും നിരീക്ഷിക്കാനും മാനേജുചെയ്യാനും അഡ്‌മിനിസ്‌ട്രേറ്റർക്ക് കഴിയും."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"ക്രമീകരണങ്ങൾ, കോർപ്പറേറ്റ് ആക്‌സസ്സ്, ആപ്‌സ്, ഉപകരണത്തിന്റെ ഡാറ്റ, ലൊക്കേഷൻ എന്നിവ നിരീക്ഷിക്കാനും നിയന്ത്രിക്കാനും അഡ്‌മിന് കഴിയും."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" 5"</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"കൂടുതലറിയുക"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"നിങ്ങൾ <xliff:g id="VPN_APP">%1$s</xliff:g> ആപ്പിലേക്ക് കണക്റ്റുചെയ്‌തിരിക്കുന്നു, ഇമെയിലുകൾ, ആപ്‌സ്, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ നെറ്റ്‌വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ ഈ ആപ്പിന് കഴിയും."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" 5"</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN ക്രമീകരണം തുറക്കുക"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"നിങ്ങളുടെ അഡ്മിൻ, ഉപകരണത്തിലെ ട്രാഫിക്ക് നിരീക്ഷിക്കുന്ന നെറ്റ്‌വർക്ക് ലോഗിംഗ് ഓണാക്കിയിട്ടുണ്ട്.\n\nകൂടുതൽ വിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"നിങ്ങളുടെ ഉപകരണത്തിലെ ട്രാഫിക്ക് നിരീക്ഷിക്കുന്ന നെറ്റ്‌വർക്ക് ലോഗിംഗ് അഡ്‌മിൻ ഓണാക്കിയിട്ടുണ്ട്.\n\nകൂടുതൽ ‌വിവരങ്ങൾക്ക് അഡ്‌മിനുമായി‌ ബന്ധപ്പെടുക."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN കണക്ഷൻ സജ്ജീകരിക്കാൻ നിങ്ങൾ ഒരു ആപ്പിന് അനുമതി നൽകി.\n\nഈ ആപ്പിന് നിങ്ങളുടെ ഇമെയിലുകളും ആപ്സും വെബ്‌സൈറ്റുകളും ഉൾപ്പെടെ, ഉപകരണവും നെറ്റ്‌വർക്ക് പ്രവർത്തനവും നിരീക്ഷിക്കാൻ കഴിയും."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ നിയന്ത്രിക്കുന്നത് <xliff:g id="ORGANIZATION">%1$s</xliff:g> ആണ്.\n\nനിങ്ങളുടെ അഡ്‌മിനിസ്ട്രേറ്റർക്ക് ഇമെയിലുകളും അപ്ലിക്കേഷനുകളും സുരക്ഷിത വെബ്‌സൈറ്റുകളും ഉൾപ്പെടെയുള്ള നെറ്റ്‌വർക്ക് പ്രവർത്തനം നിരീക്ഷിക്കാൻ കഴിയും.\n\nകൂടുതൽ വിവരങ്ങൾക്ക്, അഡ്‌മിനിസ്ട്രേറ്ററെ ബന്ധപ്പെടുക.\n\nനിങ്ങളുടെ നെറ്റ്‌വർക്ക് പ്രവർത്തനം നിരീക്ഷിക്കാനാകുന്ന ഒരു VPN-ലേക്കും നിങ്ങൾ കണക്റ്റുചെയ്തിരിക്കുന്നു."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ നിയന്ത്രിക്കുന്നത് <xliff:g id="ORGANIZATION">%1$s</xliff:g> ആണ്.\n\nഇമെയിലുകൾ, ആപ്‌സ്, വെബ്‌സൈറ്റുകൾ എന്നിവയടങ്ങുന്ന നിങ്ങളുടെ നെറ്റ്‌വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ അഡ്‌മിന് കഴിയും.\n\nകൂടുതൽ ‌വിവരങ്ങൾക്ക് അഡ്‌മിനുമായി‌ ബന്ധപ്പെടുക.\n\nനെറ്റ്‌വർക്ക് ആക്റ്റിവിറ്റി ‌നിരീക്ഷിക്കാൻ സാധിക്കുന്ന ഒരു VPN-ലേക്ക് കൂടി നിങ്ങൾ കണക്റ്റ് ‌ചെയ്യപ്പെട്ടിരിക്കുന്നു."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"നിങ്ങൾ <xliff:g id="APPLICATION">%1$s</xliff:g> എന്നതിലേക്ക് കണക്റ്റുചെയ്‌തിരിക്കുന്നു, അതിന് ഇമെയിലുകൾ, ആപ്സ്, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ നെറ്റ്‌വർക്ക് പ്രവർത്തനം നിരീക്ഷിക്കാനാകും."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"നിങ്ങൾ <xliff:g id="APPLICATION">%1$s</xliff:g> എന്നതിലേക്ക് കണക്റ്റുചെയ്‌തിരിക്കുന്നു, അതിന് ഇമെയിലുകൾ, ആപ്സ്, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ സ്വകാര്യ നെറ്റ്‌വർക്ക് പ്രവർത്തനം നിരീക്ഷിക്കാനാകും."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"നിങ്ങൾ <xliff:g id="APPLICATION">%1$s</xliff:g> ആപ്പിലേക്ക് കണക്റ്റുചെയ്‌തിരിക്കുന്നു, ഇമെയിലുകൾ, ആപ്‌സ്, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നെറ്റ്‌വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ ഈ ആപ്പിന് കഴിയും."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ നിയന്ത്രിക്കുന്നത് <xliff:g id="ORGANIZATION">%1$s</xliff:g> ആണ്. നിങ്ങൾ <xliff:g id="APPLICATION">%2$s</xliff:g> എന്നതിലേക്ക് കണക്റ്റുചെയ്‌തിരിക്കുന്നു, അതിന് ഇമെയിലുകൾ, ആപ്സ്, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ ഔദ്യോഗിക നെറ്റ്‌വർക്ക് പ്രവർത്തനം നിരീക്ഷിക്കാനാകും.\n\nകൂടുതൽ വിവരങ്ങൾക്ക്, നിങ്ങളുടെ അഡ്‌മിനിസ്‌ട്രേറ്ററെ ബന്ധപ്പെടുക."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ നിയന്ത്രിക്കുന്നത് <xliff:g id="ORGANIZATION">%1$s</xliff:g> ആണ്. ഇമെയിൽ, ആപ്പുകൾ, ‌വെബ്‌സൈറ്റുകൾ എന്നിവയടങ്ങുന്ന നിങ്ങളുടെ നെറ്റ്‌വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ സാധിക്കുന്ന <xliff:g id="APPLICATION">%2$s</xliff:g> എന്നതിലേക്ക് ഇത് ‌കണക്റ്റ്‌ ചെയ്യപ്പെട്ടിരിക്കുന്നു.\n\nകൂടുതൽ ‌വിവരങ്ങൾക്ക് അഡ്‌മിനുമായി‌ ബന്ധപ്പെടുക."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ നിയന്ത്രിക്കുന്നത് <xliff:g id="ORGANIZATION">%1$s</xliff:g> ആണ്. നിങ്ങൾ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> എന്നതിലേക്ക് കണക്റ്റുചെയ്‌തിരിക്കുന്നു, അതിന് ഇമെയിലുകൾ, ആപ്സ്, വെബ്‌സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ ഔദ്യോഗിക നെറ്റ്‌വർക്ക് പ്രവർത്തനം നിരീക്ഷിക്കാനാകും.\n\nനിങ്ങൾ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> എന്നതിലേക്കും കണക്റ്റുചെയ്‌തിരിക്കുന്നു, അതിന് നിങ്ങളുടെ സ്വകാര്യ നെറ്റ്‌വർക്ക് പ്രവർത്തനം നിരീക്ഷിക്കാനാകും."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"നിങ്ങൾ സ്വമേധയാ അൺലോക്കുചെയ്യുന്നതുവരെ ഉപകരണം ലോക്കുചെയ്‌തതായി തുടരും"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"അറിയിപ്പുകൾ വേഗത്തിൽ സ്വീകരിക്കുക"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"വികസിപ്പിക്കുക"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ചുരുക്കുക"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"സ്‌ക്രീൻ പിൻ ചെയ്‌തു"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"നിങ്ങൾ അൺപിൻ ചെയ്യുന്നതുവരെ ഇത് കാണുന്ന വിധത്തിൽ നിലനിർത്തും. അൺപിൻ ചെയ്യാൻ \'തിരികെ\', \'ചുരുക്കവിവരണം\' എന്നിവ സ്‌പർശിച്ച് പിടിക്കുക."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"നിങ്ങൾ അൺപിൻ ചെയ്യുന്നതുവരെ ഇത് കാണുന്ന വിധത്തിൽ നിലനിർത്തും. അൺപിൻ ചെയ്യാൻ \'ചുരുക്കവിവരണം\' സ്‌പർശിച്ച് പിടിക്കുക."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"മനസ്സിലായി"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"വേണ്ട, നന്ദി"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> എന്നത് മറയ്‌ക്കണോ?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ഓൺ"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ഓഫ്"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"പവർ അറിയിപ്പ് നിയന്ത്രണം ഉപയോഗിച്ച്, ഒരു ആപ്പിനായുള്ള അറിയിപ്പുകൾക്ക് 0 മുതൽ 5 വരെയുള്ള പ്രാധാന്യ ലെവലുകളിലൊന്ന് നിങ്ങൾക്ക് സജ്ജമാക്കാവുന്നതാണ്. \n\n"<b>"ലെവൽ 5"</b>" \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ മുകളിൽ കാണിക്കുക \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം അനുവദിക്കുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 4"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 3"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും സൃശ്യമാക്കരുത് \n\n"<b>"ലെവൽ 2"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n\n"<b>"ലെവൽ 1"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n- ലോക്ക് സ്ക്രീനിൽ നിന്നും സ്റ്റാറ്റസ് ബാറിൽ നിന്നും മറയ്ക്കുക \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ അടിയിൽ കാണിക്കുക \n\n"<b>"ലെവൽ 0"</b>" \n- ആപ്പിൽ നിന്നുള്ള എല്ലാ അറിയിപ്പുകളും ബ്ലോക്കുചെയ്യുക"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"അറിയിപ്പുകൾ"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"നിങ്ങൾക്ക് ഈ അറിയിപ്പുകൾ ഇനിയങ്ങോട്ട് ലഭിക്കില്ല."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"ഇനിപ്പറയുന്നതിനുള്ള <xliff:g id="APP">%s</xliff:g> അറിയിപ്പുകൾ:"</string>
+    <string name="min_importance" msgid="7559703098688382595">"കുറഞ്ഞ പ്രാധാന്യം"</string>
+    <string name="low_importance" msgid="6891335321576225228">"ഇടത്തരം പ്രാധാന്യം"</string>
+    <string name="default_importance" msgid="6400766013567512061">"ഉയർന്ന പ്രാധാന്യം"</string>
+    <string name="high_importance" msgid="730741630855788381">"അടിയന്തിര പ്രാധാന്യം"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"ശബ്ദപരമോ ദൃശ്യപരമോ ആയ തടസ്സമില്ല"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"നിശബ്ദമായി കാണിക്കുക"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"ശബ്ദമുണ്ടാക്കുക"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"ശബ്ദമുണ്ടാക്കുക, സ്ക്രീനിൽ കാണിക്കുക"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"കൂടുതൽ ക്രമീകരണം"</string>
     <string name="notification_done" msgid="5279426047273930175">"പൂർത്തിയായി"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> അറിയിപ്പ് നിയന്ത്രണങ്ങൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 0433950..873f5a5 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -183,8 +183,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Дэлгэц түгжих."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Тохиргоо"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Тойм"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Ажлын түгжигдсэн дэлгэц"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Хаах"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi унтраасан."</string>
@@ -320,6 +319,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> анхааруулга"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Ажлын горим"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Шөнийн гэрэл"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC-г цуцалсан"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC-г идэвхжүүлсэн"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Сүүлийн үеийн зүйл байхгүй"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Та бүгдийг нь устгасан"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Аппликешны мэдээлэл"</string>
@@ -333,6 +335,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хэвтээ чиглэлд хуваах"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Босоо чиглэлд хуваах"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Хүссэн хэлбэрээр хуваах"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Хаах"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Нээх"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Дэлгэцийг дээд хэсэгт хуваах"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Дэлгэцийг зүүн хэсэгт хуваах"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Дэлгэцийг баруун хэсэгт хуваах"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Цэнэглэгдсэн"</string>
@@ -413,20 +420,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN таслах"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Таны төхөөрөмжийг <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> удирддаг."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> таны төхөөрөмжийг удирдахын тулд <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>-г ашигладаг."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Таны админ тохиргоо, байгууллагын хандалт, апп, төхөөрөмжтэй холбоотой өгөгдөл болон төхөөрөмжийн байршлын мэдээллийг хянах, удирдах боломжтой."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Таны админ тохиргоо, байгууллагын хандалт, апп, төхөөрөмжтэй холбоотой өгөгдөл болон таны төхөөрөмжийн байршлын мэдээллийг хянах, удирдах боломжтой."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Дэлгэрэнгүй үзэх"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Таны имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN тохиргоог нээх"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Таны админ төхөөрөмжийн ачааллыг хянадаг сүлжээний логийг асаасан байна.\n\nДэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Таны админ төхөөрөмжийн ачааллыг хянадаг сүлжээний логийг асаасан байна.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Та апп-д VPN холболт хийхийг зөвшөөрсөн байна.\n\nЭнэхүү апп нь таны имэйл, апп, вэбсайт зэрэг төхөөрөмж болон сүлжээний үйл ажиллагааг хянах боломжтой."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг.\n\nАдмин нь таны имэйл,апп болон вэбсайт зэрэг сүлжээний үйл ажиллагааг хянадаг. \n\n Дэлгэрэнгүй мэдээлэл авахыг хүсвэл админтайгаа холбогдоно уу. \n\nМөн та VPN-д холбогдсон бөгөөд ингэснээр өөрийн сүлжээний үйл ажиллагааг хянах боломжтой байна."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> таны ажлын профайлыг удирддаг.\n\nТаны админ имэйл, апп болон вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу.\n\nТа сүлжээний үйл ажиллагааг хянах боломжтой VPN-д холбогдсон байна."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вэбсайт зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вэбсайт зэрэг сүлжээний хувийн үйл ажиллагааг хянах боломжтой."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Та имэйл, апп, вэб хуудас зэрэг хувийн сүлжээнийхээ үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон байна."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь <xliff:g id="APPLICATION">%2$s</xliff:g>-тэй холбогдсон бөгөөд таны  имэйл, апп, вэбсайт зэрэг сүлжээний үйл ажиллагааг хянах боломжтой.\n\nДэлгэрэнгүй мэдээлэл авахыг хүсвэл админтайгаа холбогдоно уу."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> таны ажлын профайлыг удирддаг. Энэ нь таны имэйл, апп, вэб хуудас зэрэг ажлын сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%2$s</xliff:g>-тэй холбогдсон.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>-тай холбогдсон бөгөөд таны имэйл, апп, вэбсайт зэрэг ажлын сүлжээний үйл ажиллагааг хянах боломжтой.\n\nМөн та <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны сүлжээний хувийн үйл ажиллагааг хянаж чадна."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Таныг гараар онгойлгох хүртэл төхөөрөмж түгжээтэй байх болно"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Мэдэгдлийг хурдан авах"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index fef7d65..1beae47 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"लॉक स्क्रीन."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"सेटिंग्ज"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"विहंगावलोकन."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"कार्य लॉक स्क्रीन"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"बंद करा"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi बंद झाले."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावणी"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"कार्य मोड"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"रात्रीचा प्रकाश"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC अक्षम केले आहे"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC सक्षम केले आहे"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"अलीकडील कोणतेही आयटम नाहीत"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"आपण सर्वकाही साफ केले"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"अनुप्रयोग माहिती"</string>
@@ -335,6 +337,16 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज विभाजित करा"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"अनुलंब विभाजित करा"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"सानुकूल विभाजित करा"</string>
+    <!-- no translation found for recents_accessibility_dismissed (2354459747918667050) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_open (1651449827614876864) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_top (9056056469282256287) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_left (8987144699630620019) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_right (275069779299592867) -->
+    <skip />
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज झाली"</string>
@@ -415,20 +427,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN डिस्कनेक्ट करा"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"आपले डिव्हाइस <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ने व्यवस्थापित केले आहे."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"आपले डिव्हाइस व्यवस्थापित करण्यासाठी <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> वापरते."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"आपला प्रशासक सेटिंग्ज, कॉर्पोरेट प्रवेश, अॅप्स, आपल्या डिव्हाइसशी संबद्ध डेटा आणि आपल्या डिव्हाइसच्या स्थान माहितीचे परीक्षण करू आणि ते व्यवस्थापित करू शकतो."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"आपला प्रशासक सेटिंग्ज, कॉर्पोरेट प्रवेश, अॅप्स, आपल्या डिव्हाइशी संबंधित डेटा आणि डिव्हाइसच्या स्थान माहितीचे निरीक्षण आणि व्यवस्थापन करू शकतो."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"अधिक जाणून घ्या"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"आपण <xliff:g id="VPN_APP">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जो ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN सेटिंग्ज उघडा"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"आपल्या प्रशासकाने नेटवर्क लॉगिंग चालू केले आहे, जे आपल्या डिव्हाइस वरील रहदारीचे परीक्षण करते.\n\nअधिक माहितीसाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"आपल्या प्रशासकाने नेटवर्क लॉगिंग चालू केले आहे, जे आपल्या डिव्हाइसवरील रहदारीचे निरीक्षण करते.\n\nअधिक माहितीसाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"आपण VPN कनेक्शन सेट करण्यासाठी अ‍ॅपला परवानगी दिली.\n\nहा अ‍ॅप ईमेल, अ‍ॅप्स आणि वेबसाइटसह, आपल्या डिव्हाइस आणि नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"आपले कार्य प्रोफाईल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारे व्यवस्थापित केले आहे.\n\nआपला प्रशासक ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्या नेटवर्क क्रियाकलापाचे परीक्षण करण्यास सक्षम आहे.\n\nअधिक माहितीसाठी, आपल्या प्रशासकाशी संपर्क साधा.\n\nआपण एका VPN शी देखील कनेक्ट केले आहे, जो आपल्या नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"आपले कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारे व्यवस्थापित केले जाते.\n\nआपला प्रशासक ईमेल, अॅप्स आणि वेबसाइटच्या समावेशासह आपल्या नेटवर्क क्रियाकलापाचे निरीक्षण करण्यास सक्षम आहे.\n\nअधिक माहितीसाठी आपल्या प्रशासकाशी संपर्क साधा.\n\nआपण VPN शी देखील कनेक्ट आहात, जे आपल्या नेटवर्क क्रियाकलापाचे निरीक्षण करू शकते."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"आपण <xliff:g id="APPLICATION">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जो ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"आपण <xliff:g id="APPLICATION">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जो ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या वैयक्तिक नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"आपण <xliff:g id="APPLICATION">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जो ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या वैयक्तिक नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"आपले कार्य प्रोफाईल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारे व्यवस्थापित केले आहे. ते <xliff:g id="APPLICATION">%2$s</xliff:g> शी कनेक्ट केले आहे, जो ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या कार्य नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो.\n\nअधिक माहितीसाठी, आपल्‍या प्रशासकाशी संपर्क साधा."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"आपले कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारे व्यवस्थापित केले जाते. ते <xliff:g id="APPLICATION">%2$s</xliff:g> शी कनेक्ट केलेले आहे, जे ईमेल, अॅप्स आणि वेबसाइटच्या समावेशासह आपल्या कार्य नेटवर्क क्रियाकलापाचे निरीक्षण करते. \n\nअधिक माहितीसाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"आपले कार्य प्रोफाईल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारे व्यवस्थापित केले आहे. ते <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> शी कनेक्ट केले आहे, जे ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या कार्य नेटवर्क क्रियाकलापाचे परीक्षण करू शकते.\n\nआपण <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> शी देखील कनेक्‍ट केले आहे, जे आपल्‍या वैयक्तिक नेटवर्क क्रियाकलापाचे परीक्षण करू शकते."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"आपण व्यक्तिचलितपणे अनलॉक करेपर्यंत डिव्हाइस लॉक केलेले राहील"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"सूचना अधिक जलद मिळवा"</string>
@@ -440,10 +452,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"विस्तृत करा"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"संकुचित करा"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"स्क्रीन पिन केलेली आहे"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"आपण अनपिन करेर्यंत हे यास दृश्यामध्ये ठेवते. अनपिन करण्‍यासाठी परत आणि विहंगावलोकनास स्पर्श करा आणि धरून ठेवा."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"आपण अनपिन करेर्यंत हे यास दृश्यामध्ये ठेवते. अनपिन करण्‍यासाठी विहंगावलोकनास स्पर्श करा आणि धरून ठेवा."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"समजले"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"नाही धन्यवाद"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> लपवायचे?"</string>
@@ -510,28 +520,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"चालू"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"उर्जा सूचना नियंत्रणांसह, आपण अॅपच्या सूचनांसाठी महत्त्व स्तर 0 ते 5 पर्यंत सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दर्शवा \n- पूर्ण स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा कंपन करू नका \n\n"<b>"स्तर 1"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा कंपन करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना अवरोधित करा"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"आपल्याला यापुढे या सूचना प्राप्त होणार नाहीत."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"यासाठी <xliff:g id="APP">%s</xliff:g> सूचना"</string>
+    <string name="min_importance" msgid="7559703098688382595">"निम्न"</string>
+    <string name="low_importance" msgid="6891335321576225228">"मध्‍यम"</string>
+    <string name="default_importance" msgid="6400766013567512061">"सर्वाधिक"</string>
+    <string name="high_importance" msgid="730741630855788381">"त्वरित"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"कोणताही ध्वनी किंवा व्हिज्युअल व्यत्यय नाही"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"शांतपणे दर्शवा"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"ध्वनी करा"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"ध्वनी करा आणि स्क्रीनवर पॉप करा"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"अधिक सेटिंग्ज"</string>
     <string name="notification_done" msgid="5279426047273930175">"पूर्ण झाले"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> सूचना नियंत्रणे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index d6c90a9..21873fe1 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Kunci skrin."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Tetapan"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Ikhtisar."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Skrin kunci kerja"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Tutup"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi dimatikan."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Amaran <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Mod kerja"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Cahaya Malam"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Tiada item terbaharu"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Anda telah mengetepikan semua item"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maklumat Aplikasi"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Mendatar Terpisah"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Menegak Terpisah"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tersuai Terpisah"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Ketepikan"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Buka"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Pisahkan skrin ke atas"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Pisahkan skrin ke kiri"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Pisahkan skrin ke kanan"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Sudah dicas"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Putuskan sambungan VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Peranti anda diurus oleh <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> menggunakan <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> untuk mengurus peranti anda."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Pentadbir anda boleh memantau dan mengurus tetapan, akses korporat, apl, data yang dikaitkan dengan peranti dan maklumat lokasi peranti anda."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Ketahui lebih lanjut"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Anda disambungkan ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang boleh memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Buka Tetapan VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Pentadbir anda telah menghidupkan pengelogan rangkaian yang memantau trafik pada peranti anda.\n\nUntuk mendapatkan maklumat lanjut, hubungi pentadbir anda."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Anda memberikan kebenaran kepada apl untuk menyediakan sambungan VPN.\n\nApl ini boleh memantau aktiviti peranti dan rangkaian anda, termasuk e-mel, apl dan tapak web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Profil kerja anda diurus oleh <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nPentadbir anda boleh memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web.\n\nUntuk mendapatkan maklumat lanjut, hubungi pentadbir anda.\n\nAnda turut disambungkan ke VPN, yang boleh memantau aktiviti rangkaian anda."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Anda disambungkan ke <xliff:g id="APPLICATION">%1$s</xliff:g>, yang boleh memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Anda disambungkan ke <xliff:g id="APPLICATION">%1$s</xliff:g>, yang boleh memantau aktiviti rangkaian peribadi anda, termasuk e-mel, apl dan tapak web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Anda disambungkan ke <xliff:g id="APPLICATION">%1$s</xliff:g>, yang boleh memantau aktiviti rangkaian peribadi anda, termasuk e-mel, apl dan tapak web."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Profil kerja anda diurus oleh <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profil ini disambungkan ke <xliff:g id="APPLICATION">%2$s</xliff:g>, yang boleh memantau aktiviti rangkaian kerja anda, termasuk e-mel, apl dan tapak web.\n\nUntuk mendapatkan maklumat lanjut, sila hubungi pentadbir anda."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Profil kerja anda diurus oleh <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profil disambungkan ke <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, yang boleh memantau aktiviti rangkaian kerja anda, termasuk e-mel, apl dan tapak web.\n\nAnda turut disambungkan ke <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, yang boleh memantau aktiviti rangkaian peribadi anda."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Peranti akan kekal terkunci sehingga anda membuka kunci secara manual"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Dapatkan pemberitahuan lebih cepat"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Kembangkan"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Runtuhkan"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skrin telah disemat"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Tindakan ini memastikan skrin kelihatan sehingga anda menyahsemat. Sentuh &amp; tahan Kembali dan Ikhtisar untuk menyahsemat."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Tindakan ini memastikan skrin kelihatan sehingga anda menyahsemat. Sentuh &amp; tahan Ikhtisar untuk menyahsemat."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Faham"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Tidak"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Sembunyikan <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Hidup"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Mati"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Dengan kawalan pemberitahuan berkuasa, anda boleh menetapkan tahap kepentingan dari 0 hingga 5 untuk pemberitahuan apl. \n\n"<b>"Tahap 5"</b>" \n- Tunjukkan pada bahagian atas senarai pemberitahuan \n- Benarkan gangguan skrin penuh \n- Sentiasa intai \n\n"<b>"Tahap 4"</b>" \n- Halang gangguan skrin penuh \n- Sentiasa intai \n\n"<b>"Tahap 3"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n\n"<b>"Tahap 2"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n- Jangan berbunyi dan bergetar \n\n"<b>"Tahap 1"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n- Jangan berbunyi atau bergetar \n- Sembunyikan daripada skrin kunci dan bar status \n- Tunjukkan di bahagian bawah senarai pemberitahuan \n\n"<b>"Tahap 0"</b>" \n- Sekat semua pemberitahuan daripada apl"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Pemberitahuan"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Anda tidak akan menerima pemberitahuan ini lagi."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Pemberitahuan <xliff:g id="APP">%s</xliff:g> untuk"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Rendah"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Sederhana"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Tinggi"</string>
+    <string name="high_importance" msgid="730741630855788381">"Segera"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Tiada gangguan bunyi atau visual"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Tunjukkan secara senyap"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Berbunyi"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Berbunyi dan paparkan pada skrin"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Lagi tetapan"</string>
     <string name="notification_done" msgid="5279426047273930175">"Selesai"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Kawalan pemberitahuan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 17eeacf..b920279 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"မျက်နှာပြင် သော့ပိတ်ရန်"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ဆက်တင်များ"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"ခြုံကြည့်မှု။"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"အလုပ်သုံး လော့ခ်မျက်နှာပြင်"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"ပိတ်ရန်"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>။"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"ကြိုးမဲ့ ပိတ်ထား။"</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> သတိပေးချက်"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"အလုပ် မုဒ်"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ညအလင်းရောင်"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ကို ပိတ်ထားသည်"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ကို ဖွင့်ထားသည်"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"သင်အားလုံးကို ရှင်းလင်းပြီးပါပြီ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"အပလီကေးရှင်းအင်ဖို"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ရေပြင်ညီ ပိုင်းမည်"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ဒေါင်လိုက်ပိုင်းမည်"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"စိတ်ကြိုက် ပိုင်းမည်"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"ပယ်ရန်"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"ဖွင့်ရန်"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"မျက်နှာပြင်ကို အပေါ်သို့ ခွဲရန်"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"မျက်နှာပြင်ကို ဘယ်ဘက်သို့ ခွဲရန်"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"မျက်နှာပြင်ကို ညာဘက်သို့ ခွဲရန်"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"အားသွင်းပြီး"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN ကို အဆက်ဖြတ်ရန်"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"သင့်စက်ပစ္စည်းကို <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> က စီမံခန့်ခွဲထားပါသည်။"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> သည် သင့်စက်ပစ္စည်းကို စီမံခန့်ခွဲရန် <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ကို အသုံးပြုပါသည်။"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"စီမံသူသည် ဆက်တင်၊ ကော်ပိုရိတ် သုံးခွင့်၊ အက်ပ်၊ စက်ပစ္စည်းနှင့် ဆက်စပ်ဒေတာနှင့် ၎င်း၏တည်နေရာအချက်အလက်ကိုစောင့်ကြည့်စီမံနိုင်ပါသည်။"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"စီမံသူသည် ဆက်တင်၊ ကော်ပိုရိတ်သုံးခွင့်၊ အက်ပ်၊ စက်ပစ္စည်းနှင့်ဆိုင်သောဒေတာနှင့် ၎င်း၏တည်နေရာအချက်အလက်ကိုစောင့်ကြည့်စီမံနိုင်ပါသည်။"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"ပိုမိုလေ့လာရန်"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="VPN_APP">%1$s</xliff:g> သို့ သင်သည် ချိတ်ဆက်ထားပါသည်။"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Open VPN ဆက်တင်များ"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"သင့်စီမံခန့်ခွဲသူသည် သင့်စက်ပစ္စည်းပေါ်ရှိ ဒေတာအသွားအလာကို စောင့်ကြည့်နိုင်သည့် ကွန်ရက်မှတ်တမ်းတင်ခြင်းကို ဖွင့်ထားပါသည်။\n\nထပ်မံလေ့လာရန်အတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"သင့်စီမံခန့်ခွဲသူသည် စက်ပစ္စည်းပေါ်ရှိ ဒေတာအသွားအလာကို စောင့်ကြည့်နိုင်သည့် ကွန်ရက်အတွက် မှတ်တမ်းတင်ခြင်းကို ဖွင့်ထားပါသည်။\n\nနောက်ထပ် အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN ချိတ်ဆက်မှုပြုလုပ်ရန် အက်ပ်ကို သင်ခွင့်ပြုလိုက်သည်။ \n\n ဤအက်ပ်သည် အီးမေးလ်များ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်များကို စောင့်ကြည့်နိုင်သည်။"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"သင့်အလုပ်ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g>မှ စီမံခန့်ခွဲပါသည်။ \n\nသင့်စီမံခန့်ခွဲသူသည် အီးမေးလ်များ၊ အက်ပ်များ၊ နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောက်ချက်ကို စောင့်ကြည့်နိုင်သည်။ \n\nအချက်အလက်များ ပိုမိုရယူရန် သင်၏ စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။ \n\nသင်သည် VPN တစ်ခုသို့ပါ ချိတ်ဆက်ထားပြီး ၎င်းကပါ သင်၏ ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်ပါသည်။"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"သင့်အလုပ်ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> က စီမံခန့်ခွဲထားပါသည်။\n\nသင့်စီမံခန့်ခွဲသူသည် အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်ပါသည်။\n\nနောက်ထပ် အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။\n\nသင်သည် သင့်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည့် VPN သို့လည်း ချိတ်ဆက်ထားပါသေးသည်။"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"သင်သည် <xliff:g id="APPLICATION">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးများ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်များကို စောင့်ကြည့်နိုင်သည်။"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"သင်သည် <xliff:g id="APPLICATION">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်။ ၎င်းသည် အီးမေးလ်များ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည်။"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"သင်သည် အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကိုယ်ရေးကိုယ်တာ ကွန်ရက်အသုံးပြုမှုကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="APPLICATION">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်။"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"သင့်အလုပ်ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> မှစီမံခန့်ခွဲသည်။ ၎င်းကို <xliff:g id="APPLICATION">%2$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်များ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်အလုပ်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည်။ \n\nအချက်အလက်များ ပိုမိုရယူရန် သင်၏ စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"သင့်အလုပ်ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> က စီမံခန့်ခွဲထားပါသည်။ ၎င်းသည် အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်ကွန်ရက်လုပ်ဆောင်ချက်များကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="APPLICATION">%2$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်။\n\nနောက်ထပ် အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"သင့်အလုပ် ပရိုဖိုင်ကို <xliff:g id="ORGANIZATION">%1$s</xliff:g> မှစီမံခန့်ခွဲသည်။ ၎င်းကို <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> သို့ ချိတ်ဆက်ထားသည်၊ ၎င်းသည် အီးမေးလ်များ၊ အက်ပ်များနှင့် ဝဘ်ဆိုက်များအပါအဝင် သင့်အလုပ်ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည်။ \n\n သင်သည်<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ကိုလည်း ချိတ်ဆက်ထားသည်၊ ၎င်းသည် သင့်ကိုယ်ပိုင်ကွန်ရက်လုပ်ဆောင်ချက်များကို စောင့်ကြည့်နိုင်သည်။"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"သင်က လက်ဖြင့် သော့မဖွင့်မချင်း ကိရိယာမှာ သော့ပိတ်လျက် ရှိနေမည်"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"အကြောင်းကြားချက်များ မြန်မြန်ရရန်"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"တိုးချဲ့ရန်"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ခေါက်သိမ်းရန်..."</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"မျက်နှာပြင် ပင်ထိုးပြီးပါပြီ"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"သင်ပင်မဖြုတ်မခြင်း ၎င်းကို ပြသထားပါမည်။ ပင်ဖြုတ်ရန် Back နှင့် Overview ကို ထိ၍ဖိထားပါ။"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"သင်ပင်မဖြုတ်မချင်း ၎င်းကိုပြသထားပါမည်။ ပင်ဖြုတ်ရန် Overview ကိုထိပြီး ဖိထားပါ။"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"အဲဒါ ရပါပြီ"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"မလိုတော့ပါ"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ဝှက်မည်လား?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ဖွင့်ပါ"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ပိတ်ပါ"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"ပါဝါအကြောင်းကြားချက် ထိန်းချုပ်မှုများကိုအသုံးပြုပြီး အက်ပ်တစ်ခု၏ အကြောင်းကြားချက် အရေးပါမှု ၀ မှ ၅ အထိသတ်မှတ်ပေးနိုင်သည်။ \n\n"<b>"အဆင့် ၅"</b>" \n- အကြောင်းကြားချက်စာရင်း၏ ထိပ်ဆုံးတွင် ပြသည် \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်းကို ခွင့်ပြုသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၄"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၃"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n\n"<b>"အဆင့် ၂"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n\n"<b>"အဆင့် ၁"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n- လော့ခ်ချထားသည့် မျက်နှာပြင်နှင့် အခြေအနေဘားတန်းတို့တွင် မပြပါ \n- အကြောင်းကြားချက်စာရင်း အောက်ဆုံးတွင်ပြသည် \n\n"<b>"အဆင့် ၀"</b>" \n- အက်ပ်မှ အကြောင်းကြားချက်များ အားလုံးကို ပိတ်ဆို့သည်"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"အကြောင်းကြားချက်များ"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"သင်သည် ဤအကြောင်းကြားချက်များကို လက်ခံရရှိတော့မည် မဟုတ်ပါ။"</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"ဤအရာအတွက် <xliff:g id="APP">%s</xliff:g> အကြောင်းကြားချက်များ"</string>
+    <string name="min_importance" msgid="7559703098688382595">"အရေးသိပ်မကြီးပါ"</string>
+    <string name="low_importance" msgid="6891335321576225228">"အတော်အသင့်"</string>
+    <string name="default_importance" msgid="6400766013567512061">"အရေးကြီးသည်"</string>
+    <string name="high_importance" msgid="730741630855788381">"အရေးပေါ်"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"မည်သည့်အသံ သို့မဟုတ် ရုပ်ပုံ ကြားဝင်နှောင့်ယှက်ခြင်း မရှိပါ"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"တိတ်တဆိတ်ပြပါ"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"အသံဖွင့်ပါ"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"အသံဖွင့်၍ မျက်နှာပြင်ပေါ်တွင် ပြပါ"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"နောက်ထပ် ဆက်တင်များ"</string>
     <string name="notification_done" msgid="5279426047273930175">"ပြီးပါပြီ"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> အကြောင်းကြားချက် ထိန်းချုပ်မှုများ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index b5f915d..bb08967 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Låseskjerm."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Innstillinger"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Oversikt."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Låseskjerm for arbeid"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Lukk"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi er slått av."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advarsel for <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Arbeidsmodus"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nattlys"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC er slått av"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC er slått på"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Ingen nylige elementer"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har fjernet alt"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformasjon"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Del horisontalt"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Del vertikalt"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Del tilpasset"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Avvis"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Åpne"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Delt skjerm øverst"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Delt skjerm til venstre"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Delt skjerm til høyre"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Oppladet"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Koble fra VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Enheten din administreres av <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> bruker <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> til å administrere enheten din."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administratoren din kan overvåke og administrere innstillinger, bedriftstilgang, apper, data som er tilknyttet denne enheten, og enhetens posisjonsinformasjon."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Administratoren din kan overvåke og administrere innstillinger, bedriftstilgang, apper, data som er tilknyttet denne enheten, og enhetens posisjonsinformasjon."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Finn ut mer"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Enheten er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Åpne VPN-innstillingene"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administratoren din har slått på loggføring av nettverk, som overvåker trafikken på enheten din.\n\nKontakt administratoren for mer informasjon."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administratoren din har slått på loggføring av nettverk, som overvåker trafikken på enheten din.\n\nKontakt administratoren for mer informasjon."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Du ga en app tillatelse til å konfigurere en VPN-tilkobling.\n\nDenne appen kan overvåke enheten og nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Work-profilen din administreres av <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministratoren din kan overvåke nettverksaktiviteten din, inkludert e-post, apper og nettsteder.\n\nFor mer informasjon, ta kontakt med administratoren.\n\nDu er også koblet til et VPN, som kan overvåke nettverksaktiviteten din."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Jobbprofilen din administreres av <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministratoren din kan overvåke nettverksaktiviteten din, inkludert e-post, apper og nettsteder.\n\nKontakt administratoren for mer informasjon.\n\nDu er også tilkoblet en VPN som kan overvåke nettverksaktiviteten din."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Enheten er koblet til <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Enheten er koblet til <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan overvåke den personlige nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Enheten er koblet til <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan overvåke den personlige nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Work-profilen din administreres av <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Den er koblet til <xliff:g id="APPLICATION">%2$s</xliff:g>, som kan overvåke nettverksaktiviteten din på jobben, inkludert e-post, apper og nettsteder.\n\nFor å få mer informasjon, ta kontakt med administratoren."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Jobbprofilen din administreres av <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Den er koblet til <xliff:g id="APPLICATION">%2$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-poster, apper og nettsteder.\n\nKontakt administratoren din for mer informasjon."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Work-profilen din administreres av <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Den er koblet til <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, som kan overvåke nettverksaktiviteten din på jobben, inkludert e-post, apper og nettsteder.\n\nDu er også koblet til <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, som kan overvåke den personlige nettverksaktiviteten din."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Enheten forblir låst til du låser den opp manuelt"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Motta varsler raskere"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Utvid"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skjul"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skjermen er låst"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"På denne måten blir skjermen synlig frem til du løsner den. Trykk og hold inne Tilbake og Oversikt for å løsne den."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"På denne måten blir skjermen synlig frem til du løsner den. Trykk og hold inne Oversikt for å løsne den."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Skjønner"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nei takk"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"På"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Av"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Med effektive varselinnstillinger kan du angi viktighetsnivåer fra 0 til 5 for appvarsler. \n\n"<b>"Nivå 5"</b>" \n– Vis øverst på varsellisten \n– Tillat forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 4"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 3"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n\n"<b>"Nivå 2"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri fort \n– Tillat aldri lyder eller vibrering \n\n"<b>"Nivå 1"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n– Tillat aldri lyder eller vibrering \n– Skjul fra låseskjermen og statusfeltet \n– Vis nederst på varsellisten \n\n"<b>"Nivå 0"</b>" \n– Blokkér alle varsler fra appen"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Varsler"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Du får ikke disse varslene lenger."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g>-varsler for"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Lav"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Middels"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Høy"</string>
+    <string name="high_importance" msgid="730741630855788381">"Haster"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Ingen lyd eller visuell forstyrrelse"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Vis uten lyd"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Lag lyd"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Lag lyd og vis i forgrunnen"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Flere innstillinger"</string>
     <string name="notification_done" msgid="5279426047273930175">"Ferdig"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Varselinnstillinger for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 0d20b04..da856a6 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"स्क्रीन बन्द गर्नुहोस्।"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"सेटिङहरू"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"सारांश।"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"कार्य प्रोफाइलको लक स्क्रिन"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"बन्द गर्नुहोस्"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>।"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi बन्द गरियो।"</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी दिँदै"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"कार्य मोड"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"रात्रिको प्रकाश"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC लाई असक्षम पारिएको छ"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC लाई सक्षम पारिएको छ"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"हालका कुनै पनि वस्तुहरू छैनन्"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"तपाईँले सबै कुरा खाली गर्नुभएको छ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"अनुप्रयोग जानकारी"</string>
@@ -335,6 +337,16 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"तेर्सो रूपमा विभाजन गर्नुहोस्"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ठाडो रूपमा विभाजन गर्नुहोस्"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"आफू अनुकूल विभाजन गर्नुहोस्"</string>
+    <!-- no translation found for recents_accessibility_dismissed (2354459747918667050) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_open (1651449827614876864) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_top (9056056469282256287) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_left (8987144699630620019) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_right (275069779299592867) -->
+    <skip />
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज भयो"</string>
@@ -415,20 +427,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"विच्छेद VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"तपाईंको यन्त्र <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> द्वारा व्यवस्थापन गरिएको छ।"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ले तपाईंको यन्त्रको व्यवस्थापन गर्न <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> को प्रयोग गर्दछ।"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"तपाईंको प्रशासकले सेटिङ, संस्थागत पहुँच, अनुप्रयोग, तपाईंको यन्त्रसँग सम्बन्धित डेटा र तपाईंको यन्त्रको स्थान सम्बन्धी जानकारीको अनुगमन तथा व्यवस्थापन गर्न सक्नुहुन्छ।"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"तपाईँको प्रशासकले सेटिङहरू, संस्थागत पहुँच, अनुप्रयोग, तपाईँको यन्त्रसँग सम्बन्धित डेटा र तपाईँको यन्त्रको स्थान सम्बन्धी जानकारीको निगरानी तथा व्यवस्थापन गर्न सक्नुहुन्छ।"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"थप जान्नुहोस्"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"तपाईं <xliff:g id="VPN_APP">%1$s</xliff:g> मा जोडिनुभएको छ जसले इमेल, अनुप्रयोग र वेबसाइटहरू लगायत तपाईंको नेटवर्क सम्बन्धी गतिविधिको अनुगमन गर्न सक्छ।"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN सम्बन्धी सेटिङहरू खोल्नुहोस्"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"तपाईंको प्रशासकले तपाईंको यन्त्रमा ट्राफिकको अनुगमन गर्ने नेटवर्कको लगिङलाई सक्रिय गर्नुभएको छ।\n\nथप जानकरीका लागि आफ्नो प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"तपाईँको प्रशासकले तपाईँको यन्त्रमा ट्राफिकको निगरानी गर्ने नेटवर्कको लगिङलाई सक्रिय पार्नुभएको छ।\n\nथप जानकारीका लागि आफ्नो प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"तपाईँले VPN जडान गर्न अनुप्रयोगलाई अनुमति दिनुभयो।\n\nयो अनुप्रयोगले तपाईँका यन्त्र र  नेटवर्क गतिविधि लगायत इमेल, अनुप्रयोग र वेबसाइटहरू अनुगमन गर्न सक्छ।"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"तपाईँको कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g>द्वारा व्यवस्थापन गरिन्छ।.\n\nतपाईँको प्रशासक इमेल, अनुप्रयोगहरू, र सुरक्षित वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधि अनुगमन गर्न सक्षम छ।\n\nथप जानकारीको लागि, आफ्नो प्रशासकसँग सम्पर्क राख्नुहोस्।\n\nतपाईँ VPN सँग पनि जडित हुनुहुन्छ, जसले तपाईँको नेटवर्क गतिविधि अनुगमन गर्न सक्छ।"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"तपाईँको कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> ले व्यवस्थापन गर्दछ।\n\nतपाईँको प्रशासकले तपाईँको इमेल, अनुप्रयोग र वेबसाइट सहित नेटवर्कमा तपाईँको गतिविधिको निगरानी गर्न सक्नुहुन्छ। \n\nथप जानकारीका लागि आफ्नो प्रशासकलाई सम्पर्क गर्नुहोस्।\n\n तपाईँ एउटा VPN मा जडित हुनुहुन्छ। यस VPN ले नेटवर्कमा तपाईँको गतिविधिको निगरानी गर्न सक्छ।"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"तपाईँ <xliff:g id="APPLICATION">%1$s</xliff:g> सँग जडित हुनुहुन्छ जसले इ-मेल, अनुप्रयोगहरू र वेबसाइट लगायतका तपाईँका नेटवर्क गतिविधिको अनुगमन गर्न सक्छ।"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"तपाईँ <xliff:g id="APPLICATION">%1$s</xliff:g> सँग जडित हुनुहुन्छ जसले इ-मेल, अनुप्रयोगहरू र वेबसाइट लगायतका तपाईँको निजी नेटवर्क गतिविधिका अनुगमन गर्न सक्छ।"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"तपाईं <xliff:g id="APPLICATION">%1$s</xliff:g> मा जोडिनुभएको छ जसले इमेल, अनुप्रयोग र वेबसाइटहरू लगायतको तपाईंको  व्यक्तिगत नेटवर्क सम्बन्धी गतिविधिको अनुगमन गर्न सक्छ।"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"तपाईँको कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारा व्यवस्थापन गरिन्छ। यो <xliff:g id="APPLICATION">%2$s</xliff:g> सँग जोडिएको छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधि अनुगमन गर्न सक्छ।\n\nथप जानकारीको लागि, आफ्नो प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"तपाईँको कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> ले व्यवस्थापन गर्दछ। तपाईँको कार्य प्रोफाइल <xliff:g id="APPLICATION">%2$s</xliff:g> मा जोडिएको छ। यो अनुप्रयोगले तपाईँको इमेल, अन्य अनुप्रयोग र वेबसाइटहरू सहित नेटवर्कमा तपाईँको गतिविधिको निगरानी गर्न सक्छ।\n\nथप जानकारीका लागि आफ्नो प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"तपाईँको कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारा व्यवस्थापन गरिन्छ। यो <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> सँग जोडिएको छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधि अनुगमन गर्न सक्छ।\n\nतपाईँ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> सँग पनि जडित हुनुहुन्छ, जसले तपाईँको व्यक्तिगत नेटवर्क गतिविधि अनुगमन गर्न सक्छ।"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"तपाईँले नखोले सम्म उपकरण बन्द रहनेछ"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"छिटो सूचनाहरू प्राप्त गर्नुहोस्"</string>
@@ -440,10 +452,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"विस्तार गर्नुहोस्"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"संक्षिप्त पार्नुहोस्"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"पर्दा राखेका छ"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"बुझेँ"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"धन्यवाद पर्दैन"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"लुकाउनुहुन्छ <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +520,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"सक्रिय"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"निष्क्रिय"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईँ अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- अनुप्रयोगका सबै सूचनाहरूलाई रोक्ने"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"सूचनाहरू"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"तपाईंले अबदेखि यी सूचनाहरू प्राप्त गर्नुहुने छैन।"</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"निम्न कुराका लागि <xliff:g id="APP">%s</xliff:g> का सूचनाहरू"</string>
+    <string name="min_importance" msgid="7559703098688382595">"न्यून"</string>
+    <string name="low_importance" msgid="6891335321576225228">"मध्यम"</string>
+    <string name="default_importance" msgid="6400766013567512061">"उच्च"</string>
+    <string name="high_importance" msgid="730741630855788381">"जरुरी"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"कुनै आवाज ननिकाल्ने वा दृश्य सम्बन्धी अवरोध नपुर्याउने"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"मौन रूपमा देखाउने"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"आवाज निकाल्ने"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"आवाज निकाल्ने र स्क्रिनमा पपअप देखाउने"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"थप सेटिङहरू"</string>
     <string name="notification_done" msgid="5279426047273930175">"सम्पन्‍न भयो"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचनाका लागि नियन्त्रणहरू"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 9c6fbbd..87c0860 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Vergrendelingsscherm."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Instellingen"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Overzicht."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Vergrendelingsscherm voor werk"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Sluiten"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi uitgeschakeld."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Waarschuwing voor <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Werkmodus"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nachtverlichting"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is uitgeschakeld"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is ingeschakeld"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Geen recente items"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Je hebt alles gewist"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-informatie"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontaal splitsen"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verticaal splitsen"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Aangepast splitsen"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Sluiten"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Openen"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Scherm bovenaan gesplitst"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Scherm links gesplitst"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Scherm rechts gesplitst"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opgeladen"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Verbinding met VPN verbreken"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Je apparaat wordt beheerd door <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> gebruikt <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> om je apparaat te beheren."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Je beheerder kan instellingen, zakelijke toegang, apps, aan je apparaat gekoppelde gegevens en de locatiegegevens van je apparaat controleren en beheren."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Je beheerder kan instellingen, zakelijke toegang, apps, aan je apparaat gekoppelde gegevens en de locatiegegevens van je apparaat controleren en beheren."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Meer informatie"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Je bent verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>, waarmee je netwerkactiviteit (waaronder e-mails, apps en websites) kan worden gecontroleerd."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN-instellingen openen"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Je beheerder heeft netwerkregistratie ingeschakeld, waarmee verkeer op je apparaat wordt bijgehouden.\n\nNeem voor meer informatie contact op met je beheerder."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Je beheerder heeft netwerkregistratie ingeschakeld, waarmee verkeer op je apparaat wordt bijgehouden.\n\nNeem contact op met je beheerder voor meer informatie."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Je hebt een app toestemming gegeven voor het instellen van een VPN-verbinding.\n\nMet deze app kan je apparaat- en netwerkactiviteit worden gecontroleerd, inclusief e-mails, apps en websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Je werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nJe beheerder kan je netwerkactiviteit controleren, inclusief e-mails, apps en websites.\n\nNeem contact op met je beheerder voor meer informatie.\n\nU bent ook verbonden met een VPN waarmee je netwerkactiviteit kan worden gecontroleerd."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Je werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nJe beheerder kan je netwerkactiviteit controleren, inclusief e-mails, apps en websites.\n\nNeem contact op met je beheerder voor meer informatie.\n\nJe bent ook verbonden met een VPN, waarmee je netwerkactiviteit kan worden gecontroleerd."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"U bent verbonden met <xliff:g id="APPLICATION">%1$s</xliff:g>, waarmee je netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"U bent verbonden met <xliff:g id="APPLICATION">%1$s</xliff:g>, waarmee je persoonlijke netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Je bent verbonden met <xliff:g id="APPLICATION">%1$s</xliff:g>, waarmee je persoonlijke netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Je werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Deze is verbonden met <xliff:g id="APPLICATION">%2$s</xliff:g>, waarmee je werkgerelateerde netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nNeem contact op met je beheerder voor meer informatie."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Je werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Het is gekoppeld aan <xliff:g id="APPLICATION">%2$s</xliff:g>, waarmee je werkgerelateerde netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nNeem contact op met je beheerder voor meer informatie."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Je werkprofiel wordt beheerd door <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Deze is verbonden met <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, waarmee je werkgerelateerde netwerkactiviteit kan worden gecontroleerd, inclusief e-mails, apps en websites.\n\nU bent ook verbonden met <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, waarmee je persoonlijke netwerkactiviteit kan worden gecontroleerd."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Het apparaat blijft vergrendeld totdat u het handmatig ontgrendelt"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Sneller meldingen ontvangen"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Uitvouwen"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Samenvouwen"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Scherm is vastgezet"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Het scherm blijft zichtbaar totdat je het losmaakt. Tik op Terug en Overzicht en houd deze vast om het scherm los te maken."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Het scherm blijft zichtbaar totdat je het losmaakt. Tik op Overzicht en houd dit vast om het scherm los te maken."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Ik snap het"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nee, bedankt"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> verbergen?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Aan"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Uit"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Met beheeropties voor meldingen met betrekking tot stroomverbruik kun je een belangrijkheidsniveau van 0 tot 5 instellen voor de meldingen van een app. \n\n"<b>"Niveau 5"</b>" \n- Boven aan de lijst met meldingen weergeven \n- Onderbreking op volledig scherm toestaan \n- Altijd korte weergave \n\n"<b>"Niveau 4"</b>" \n- Geen onderbreking op volledig scherm \n- Altijd korte weergave \n\n"<b>"Niveau 3"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n\n"<b>"Niveau 2"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n\n"<b>"Niveau 1"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n- Verbergen op vergrendelingsscherm en statusbalk \n- Onder aan de lijst met meldingen weergeven \n\n"<b>"Niveau 0"</b>" \n- Alle meldingen van de app blokkeren"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Meldingen"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Je ontvangt deze meldingen niet meer."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g>-meldingen voor"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Laag"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Gemiddeld"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Hoog"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgent"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Geen geluid of visuele onderbreking"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Zonder geluid weergeven"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Geluid laten horen"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Geluid laten horen en op het scherm weergeven"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Meer instellingen"</string>
     <string name="notification_done" msgid="5279426047273930175">"Gereed"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Beheeropties voor <xliff:g id="APP_NAME">%1$s</xliff:g>-meldingen"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 3f0915b..f4e551d2 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ਲੌਕ ਸਕ੍ਰੀਨ।"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"ਰੂਪ-ਰੇਖਾ।"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"ਕਾਰਜ-ਸਥਾਨ ਲੌਕ ਸਕ੍ਰੀਨ"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"ਬੰਦ ਕਰੋ"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>।"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi ਬੰਦ ਕੀਤਾ।"</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ਚਿਤਾਵਨੀ"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"ਕੰਮ ਮੋਡ"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"ਰਾਤਰੀ ਲਾਈਟ"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ਨੂੰ ਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ਤੁਸੀਂ ਸਭ ਕੁਝ ਸਾਫ਼ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਣਕਾਰੀ"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ਹੌਰੀਜ਼ੌਂਟਲ ਸਪਲਿਟ"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ਵਰਟੀਕਲ ਸਪਲਿਟ"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ਕਸਟਮ ਸਪਲਿਟ"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"ਖਾਰਜ ਕਰੋ"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"ਖੋਲ੍ਹੋ"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ਸਕ੍ਰੀਨ ਨੂੰ ਉੱਪਰ ਵੱਲ ਖੰਡਿਤ ਕਰੋ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਪਾਸੇ ਖੰਡਿਤ ਕਰੋ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਪਾਸੇ ਖੰਡਿਤ ਕਰੋ"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ਚਾਰਜ ਹੋਇਆ"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"ਤੁਹਾਡੀ ਡੀਵਾਈਸ <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੀ ਜਾਂਦੀ ਹੈ।"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਦੇ ਪ੍ਰਬੰਧਨ ਲਈ <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਦੀ ਹੈ।"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"ਤੁਹਾਡਾ ਪ੍ਰਸ਼ਾਸਕ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਅਤੇ ਉਸਦੀ ਟਿਕਾਣਾ ਜਾਣਕਾਰੀ ਸਬੰਧੀ ਸੈਟਿੰਗਾਂ, ਕਾਰਪੋਰੇਟ ਪਹੁੰਚ, ਐਪਾਂ, ਡੈਟੇ ਦੀ ਨਿਗਰਾਨੀ ਅਤੇ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦਾ ਹੈ।"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"ਤੁਹਾਡਾ ਪ੍ਰਸ਼ਾਸਕ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਦੀਆਂ ਸੈਟਿੰਗਾਂ, ਕਾਰਪੋਰੇਟ ਪਹੁੰਚ, ਐਪਾਂ, ਡੈਟੇ ਅਤੇ ਟਿਕਾਣਾ ਜਾਣਕਾਰੀ ਦੀ ਨਿਗਰਾਨੀ ਅਤੇ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦਾ ਹੈ।"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"ਹੋਰ ਜਾਣੋ"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"ਤੁਸੀਂ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਨੈੱਟਵਰਕ ਲੌਗਿੰਗ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਹੋਇਆ ਹੈ, ਜੋ ਕਿ ਤੁਹਾਡੀ ਡੀਵਾਈਸ \'ਤੇ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਦਾ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਨੈੱਟਵਰਕ ਲੌਗਿੰਗ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਹੋਇਆ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਡੀਵਾਈਸ \'ਤੇ ਟ੍ਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਹੁੰਦੀ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"ਤੁਸੀਂ ਇੱਕ ਐਪ ਨੂੰ ਇੱਕ VPN ਕਨੈਕਸ਼ਨ ਸੈਟ ਅਪ ਕਰਨ ਦੀ ਅਨੁਮਤੀ ਦਿੱਤੀ ਹੈ।\n\nਇਹ ਐਪ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਅਤੇ ਨੈੱਟਵਰਕ ਗਤੀਵਿਧੀ ਦਾ ਨਿਰੀਖਣ ਕਰ ਸਕਦਾ ਹੈ, ਈਮੇਲਾਂ, ਐਪਸ ਅਤੇ ਸੁਰੱਖਿਅਤ ਵੈਬਸਾਈਟਾਂ ਸਮੇਤ।"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"ਤੁਹਾਡੀ ਕਾਰਜ ਪ੍ਰੋਫ਼ਾਈਲ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ਦੁਆਰਾ ਵਿਵਸਥਿਤ ਕੀਤੀ ਜਾਂਦੀ ਹੈ।\n\nਤੁਹਾਡਾ ਪ੍ਰਸ਼ਾਸਕ ਈਮੇਲ, ਐਪਸ, ਅਤੇ ਵੈੱਬਪੰਨੇ ਸੰਤੇ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਗਤੀਵਿਧੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰਨ ਦੇ ਸਮਰੱਥ ਹੈ।\n\nਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।\n\nਤੁਸੀਂ VPN ਨਾਲ ਵੀ ਕਨੈਕਟ ਹੋ, ਜੋ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਗਤੀਵਿਧੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ।"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫ਼ਾਈਲ ਦਾ ਪ੍ਰਬੰਧਨ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ਵੱਲੋਂ ਪ੍ਰਬੰਧਨ ਕੀਤਾ ਗਿਆ ਹੈ।\n\nਤੁਹਾਡਾ ਪ੍ਰਸ਼ਾਸਕ ਈਮੇਲ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰਨ ਵਿੱਚ ਸਮਰੱਥ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।\n\nਤੁਸੀਂ ਇੱਕ VPN ਨਾਲ ਵੀ ਕਨੈਕਟ ਹੋਂ, ਜੋ ਤੁਹਾਡੀ ਨੈਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ।"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"ਤੁਸੀਂ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲ, ਐਪਸ ਅਤੇ ਵੈਬਸਫ਼ਿਆਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਗਤੀਵਿਧੀ ਦਾ ਨਿਰੀਖਣ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"ਤੁਸੀਂ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲ, ਐਪਸ ਅਤੇ ਵੈਬਸਫ਼ਿਆਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਗਤੀਵਿਧੀ ਦਾ ਨਿਰੀਖਣ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"ਤੁਸੀਂ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨਿੱਜੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"ਤੁਹਾਡੀ ਕਾਰਜ ਪ੍ਰੋਫ਼ਾਈਲ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ਦੁਆਰਾ ਵਿਵਸਥਿਤ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਇਹ <xliff:g id="APPLICATION">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ, ਜੋ ਈਮੇਲ, ਐਪਸ ਅਤੇ ਵੈਬਸਫ਼ਿਆਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਗਤੀਵਿਧੀ ਦਾ ਨਿਰੀਖਣ ਕਰ ਸਕਦੀ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਪ੍ਰਬੰਧਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫ਼ਾਈਲ ਦਾ ਪ੍ਰਬੰਧਨ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ਵੱਲੋਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਇਹ <xliff:g id="APPLICATION">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਕਾਰਜ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।\n\nਹੋਰ ਜਾਣਕਾਰੀ ਲਈ, ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"ਤੁਹਾਡੀ ਕਾਰਜ ਪ੍ਰੋਫ਼ਾਈਲ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ਦੁਆਰਾ ਵਿਵਸਥਿਤ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਇਹ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ, ਜੋ ਈਮੇਲ, ਐਪਸ ਅਤੇ ਵੈਬਸਫ਼ਿਆਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਗਤੀਵਿਧੀ ਦਾ ਨਿਰੀਖਣ ਕਰ ਸਕਦੀ ਹੈ।\n\nਤੁਸੀਂ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ਨਾਲ ਵੀ ਕਨੈਕਟ ਹੋ, ਜੋ ਤੁਹਾਡੀ ਨਿੱਜੀ ਨੈੱਟਵਰਕ ਗਤੀਵਿਧੀ ਦਾ ਨਿਰੀਖਣ ਕਰ ਸਕਦਾ ਹੈ।"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"ਡੀਵਾਈਸ ਲੌਕ ਰਹੇਗੀ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਮੈਨੂਅਲੀ ਅਨਲੌਕ ਨਹੀਂ ਕਰਦੇ"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"ਤੇਜ਼ੀ ਨਾਲ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ਨਸ਼ਟ ਕਰੋ"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"ਸਕ੍ਰੀਨ ਪਿੰਨ ਕੀਤੀ"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"ਇਹ ਇਸ ਨੂੰ ਤਦ ਤੱਕ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਰੱਖਦਾ ਹੈ ਜਦ ਤੱਕ ਤੁਸੀਂ ਅਨਪਿੰਨ ਨਹੀਂ ਕਰਦੇ। ਅਨਪਿੰਨ ਕਰਨ ਲਈ \'ਪਿੱਛੇ\' ਅਤੇ \'ਰੂਪ-ਰੇਖਾ\' ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"ਇਹ ਇਸ ਨੂੰ ਤਦ ਤੱਕ ਦ੍ਰਿਸ਼ ਵਿੱਚ ਰੱਖਦਾ ਹੈ ਜਦ ਤੱਕ ਤੁਸੀਂ ਅਨਪਿੰਨ ਨਹੀਂ ਕਰਦੇ। ਅਨਪਿੰਨ ਕਰਨ ਲਈ \'ਰੂਪ-ਰੇਖਾ\' ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"ਸਮਝ ਲਿਆ"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ਕੀ <xliff:g id="TILE_LABEL">%1$s</xliff:g> ਨੂੰ ਲੁਕਾਉਣਾ ਹੈ?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ਚਾਲੂ"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ਬੰਦ"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"ਪਾਵਰ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨਾਲ, ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਦੀਆਂ ਸੂਚਨਾਵਾਂ ਲਈ ਮਹੱਤਤਾ ਪੱਧਰ ਨੂੰ 0 ਤੋਂ 5 ਤੱਕ ਸੈੱਟ ਕਰ ਸਕਦੇ ਹੋ। \n\n"<b>"ਪੱਧਰ 5"</b>" \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਸਿਖਰ \'ਤੇ ਵਿਖਾਓ \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਦੀ ਆਗਿਆ ਦਿਓ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਵਿਖਾਓ \n\n"<b>"ਪੱਧਰ 4"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਵਿਖਾਓ \n\n"<b>"ਪੱਧਰ 3"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਵਿਖਾਓ \n\n"<b>"ਪੱਧਰ 2"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਵਿਖਾਓ \n- ਕਦੇ ਵੀ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n\n"<b>"ਪੱੱਧਰ 1"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਵਿਖਾਓ \n- ਕਦੇ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n- ਲੌਕ ਸਕ੍ਰੀਨ ਅਤੇ ਸਥਿਤੀ ਪੱਟੀ ਤੋਂ ਲੁਕਾਓ \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਹੇਠਾਂ ਵਿਖਾਓ \n\n"<b>"ਪੱਧਰ 0"</b>" \n- ਐਪ ਤੋਂ ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬਲੌਕ ਕਰੋ"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"ਸੂਚਨਾਵਾਂ"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"ਤੁਸੀਂ ਹੁਣ ਇਹ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰੋਂਗੇ।"</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"ਇਸ ਦੇ ਲਈ <xliff:g id="APP">%s</xliff:g> ਸੂਚਨਾਵਾਂ"</string>
+    <string name="min_importance" msgid="7559703098688382595">"ਘੱਟ"</string>
+    <string name="low_importance" msgid="6891335321576225228">"ਔਸਤ"</string>
+    <string name="default_importance" msgid="6400766013567512061">"ਉੱਚ"</string>
+    <string name="high_importance" msgid="730741630855788381">"ਜ਼ਰੂਰੀ"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਦ੍ਰਿਸ਼ਟਾਂਤਕ ਰੁਕਾਵਟ ਨਹੀਂ"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"ਚੁੱਪਚਾਪ ਵਿਖਾਓ"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"ਧੁਨੀ ਵਜਾਓ"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"ਧੁਨੀ ਵਜਾਓ ਅਤੇ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਖਾਓ"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
     <string name="notification_done" msgid="5279426047273930175">"ਹੋ ਗਿਆ"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਸੂਚਨਾ ਕੰਟਰੋਲ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index abaf4bc..457f644 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Ekran blokady."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Ustawienia"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Przegląd."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Ekran blokady wyświetlany podczas działania"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Zamknij"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi wyłączone."</string>
@@ -326,6 +325,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Ostrzeżenie: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Tryb pracy"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Podświetlenie nocne"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Brak ostatnich elementów"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Wszystko zostało wyczyszczone"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacje o aplikacji"</string>
@@ -339,6 +344,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podziel poziomo"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podziel pionowo"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Podziel niestandardowo"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Zamknij"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Otwórz"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podziel ekran u góry"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podziel ekran z lewej"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podziel ekran z prawej"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Naładowana"</string>
@@ -419,20 +429,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Rozłącz z VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Twoim urządzeniem zarządza <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> używa aplikacji <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> do zarządzania Twoim urządzeniem."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administrator może monitorować ustawienia, dostęp firmowy, aplikacje, dane dotyczące urządzenia i lokalizacji oraz nimi zarządzać."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">"  "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Więcej informacji"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Łączysz się z aplikacją <xliff:g id="VPN_APP">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">"  "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Otwórz ustawienia VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administrator włączył rejestrowanie sieciowe, które pozwala monitorować ruch na Twoim urządzeniu.\n\nAby uzyskać więcej informacji, skontaktuj się z administratorem."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Aplikacja otrzymała od Ciebie uprawnienia do konfigurowania połączenia VPN.\n\nMoże ona monitorować Twoją aktywność na urządzeniu i w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Twoim profilem do pracy zarządza <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nSkontaktuj się z nim, by dowiedzieć się więcej.\n\nMasz też połączenie z siecią VPN, która może monitorować Twoją aktywność w sieci."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Masz połączenie z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Masz połączenie z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją prywatną aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Masz połączenie z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją prywatną aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Twoim profilem do pracy zarządza <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profil jest połączony z aplikacją <xliff:g id="APPLICATION">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci związaną z pracą, w tym e-maile, aplikacje i strony internetowe.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Twoim profilem do pracy zarządza <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profil jest połączony z aplikacją <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci związaną z pracą, w tym e-maile, aplikacje i strony internetowe.\n\nMasz też połączenie z aplikacją <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, która może monitorować Twoją prywatną aktywność w sieci."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Urządzenie pozostanie zablokowane, aż odblokujesz je ręcznie"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Szybszy dostęp do powiadomień"</string>
@@ -444,10 +458,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Rozwiń"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Zwiń"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekran jest przypięty"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Ekran będzie widoczny, dopóki go nie odepniesz. Aby to zrobić, kliknij i przytrzymaj Wstecz oraz Przegląd."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ekran będzie widoczny, dopóki go nie odepniesz. Aby to zrobić, kliknij i przytrzymaj Przegląd."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nie, dziękuję"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ukryć <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -514,28 +526,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Wł."</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Wył."</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Dzięki zaawansowanym ustawieniom możesz określić poziom ważności powiadomień z aplikacji w skali od 0 do 5. \n\n"<b>"Poziom 5"</b>" \n– Pokazuj u góry listy powiadomień \n– Zezwalaj na powiadomienia na pełnym ekranie \n– Zawsze pokazuj podgląd \n\n"<b>"Poziom 4"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Zawsze pokazuj podgląd \n\n"<b>"Poziom 3"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n\n"<b>"Poziom 2"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n– NIgdy nie powiadamiaj dźwiękiem ani wibracjami \n\n"<b>"Poziom 1"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n– NIgdy nie powiadamiaj dźwiękiem ani wibracjami \n– Ukrywaj na ekranie blokady i pasku stanu \n– Pokazuj u dołu listy powiadomień \n\n"<b>"Poziom 0"</b>" \n– Blokuj wszystkie powiadomienia aplikacji"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Powiadomienia"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Nie będziesz już otrzymywać tych powiadomień."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Powiadomienia aplikacji <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Niska"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Średnia"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Wysoka"</string>
+    <string name="high_importance" msgid="730741630855788381">"Pilna"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Brak dźwięku i komunikatów wizualnych"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Pokazuj dyskretnie"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Sygnalizacja dźwiękiem"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Sygnalizacja dźwiękiem i wyświetlenie komunikatu"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Więcej ustawień"</string>
     <string name="notification_done" msgid="5279426047273930175">"Gotowe"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> – ustawienia powiadomień"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 70618c9..7edebe0 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Tela de bloqueio."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Configurações"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Visão geral."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Tela de bloqueio de trabalho"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Fechar"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"O Wi-Fi foi desativado."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabalho"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Modo noturno"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"A NFC está desativada"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"A NFC está ativada"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Você limpou tudo"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do app"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Dispensar"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Abrir"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir a tela para a parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir a tela para a esquerda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir a tela para a direita"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Desconectar VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Seu dispositivo é gerenciado por <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> usa <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> para gerenciar seu dispositivo."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"O admin. pode monitorar e gerenciar config., acesso corporativo, apps, dados associados ao dispos. e as info. de local do dispos."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"O admin. pode monitorar e gerenciar config., acesso corporativo, apps, dados associados ao dispos. e info de local do dispositivo."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Saber mais"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Você está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade na rede, incluindo e-mails, apps e websites."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Abrir configurações de VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu dispositivo.\n\nPara ver mais informações, entre em contato com o administrador."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu dispositivo.\n\nPara ver mais informações, entre em contato com o administrador."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Você deu permissão para um app configurar uma conexão VPN.\n\nEsse app pode monitorar seu dispositivo e a atividade na rede, incluindo e-mails, apps e websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nSeu administrador pode monitorar suas atividades de rede, incluindo e-mails, apps e websites.\n\nPara mais informações, entre em contato com seu administrador.\n\nVocê também está conectado a uma VPN, a qual pode monitorar suas atividades de rede."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nSeu administrador pode monitorar sua atividade de rede, incluindo e-mails, apps e websites.\n\nPara ver mais informações, entre em contato com o administrador.\n\nVocê também está conectado a uma VPN, que pode monitorar sua atividade de rede."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Você está conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorar sua atividade na rede, incluindo e-mails, apps e websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Você está conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorar sua atividade pessoal na rede, incluindo e-mails, apps e websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Você está conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorar sua atividade pessoal na rede, incluindo e-mails, apps e websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ele está conectado a <xliff:g id="APPLICATION">%2$s</xliff:g>, que pode monitorar sua atividade profissional na rede, incluindo e-mails, apps e websites.\n\nPara ver mais informações, entre em contato com seu administrador."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ele está conectado ao app <xliff:g id="APPLICATION">%2$s</xliff:g>, que pode monitorar sua atividade profissional de rede, incluindo e-mails, apps e websites.\n\nPara ver mais informações, entre em contato com o administrador."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ele está conectado a <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, que pode monitorar sua atividade profissional na rede, incluindo e-mails, apps e websites.\n\nVocê também está conectado a <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, que pode monitorar sua atividade pessoal na rede."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"O dispositivo permanecerá bloqueado até que você o desbloqueie manualmente"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Receba notificações mais rápido"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Recolher"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"A tela está fixada"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Visão geral e mantenha essas opções pressionadas para liberar."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Entendi"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Ativado"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Você deixará de receber essas notificações."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notificações do app <xliff:g id="APP">%s</xliff:g> para"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Baixa"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Média"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Alta"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgente"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Sem som ou interrupção visual"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Mostrar de forma silenciosa"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Emitir som"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Emitir som e exibir na tela"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
     <string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificação do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 7102c64..d7b40ff 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Ecrã de bloqueio."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Definições"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Visão geral."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Ecrã de bloqueio de trabalho"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Fechar"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi desligado."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabalho"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Luz noturna"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"O NFC está desativado"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"O NFC está ativado"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Limpou tudo"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações da aplicação"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Ignorar"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Abrir"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ecrã dividido à parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ecrã dividido à esquerda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ecrã dividido à direita"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Desligar VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"O seu dispositivo é gerido pelo <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"A <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> utiliza o <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> para gerir o seu dispositivo."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"O administ. pode monitorizar e gerir definições, acesso empresarial, aplic. e dados associados ao dispositivo, bem como inf. de localiz. do disp."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"O administ. pode monitorizar e gerir definições, acesso empresarial, aplic. e dados associados ao dispositivo, bem como inf. de localiz. do disp."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Saiba mais"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Está ligado à rede <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorizar a sua atividade de rede, incluindo emails, aplicações e Websites."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Abrir as definições de VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"O seu administrador ativou os registos de rede, que monitorizam o tráfego no seu dispositivo.\n\nPara mais informações, contacte o administrador."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"O seu administrador ativou os registos de rede, que monitorizam o tráfego no seu dispositivo.\n\nPara obter mais informações, contacte o administrador."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Concedeu autorização a uma aplicação para configurar uma ligação VPN.\n\nEsta aplicação pode monitorizar a atividade do dispositivo e da rede, incluindo emails, aplicações e Websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"O seu perfil de trabalho é gerido por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nO seu administrador pode monitorizar a atividade da rede, incluindo emails, aplicações e Websites.\n\nPara obter mais informações, contacte o administrador.\n\nAlém disso, está ligado a uma VPN, que pode monitorizar a atividade da rede."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"O seu perfil de trabalho é gerido por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nO seu administrador tem a capacidade de monitorizar a sua atividade da rede, incluindo emails, aplicações e Websites.\n\nPara obter mais informações, contacte o administrador.\n\nAlém disso, está ligado a uma VPN, que pode monitorizar a sua atividade da rede."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Está ligado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorizar a atividade da rede, incluindo emails, aplicações e Websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Está ligado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorizar a atividade da rede pessoal, incluindo emails, aplicações e Websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Está ligado ao <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorizar a atividade da rede pessoal, incluindo emails, aplicações e Websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"O seu perfil de trabalho é gerido por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Está ligado a <xliff:g id="APPLICATION">%2$s</xliff:g>, que pode monitorizar a atividade da rede de trabalho, incluindo emails, aplicações e Websites.\n\nPara obter mais informações, contacte o administrador."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"O seu perfil de trabalho é gerido por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Está associado à aplicação <xliff:g id="APPLICATION">%2$s</xliff:g>, que pode monitorizar a sua atividade da rede de trabalho, incluindo emails, aplicações e Websites.\n\nPara obter mais informações, contacte o administrador."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"O seu perfil de trabalho é gerido por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Está ligado a <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, que pode monitorizar a atividade da rede de trabalho, incluindo emails, aplicações e Websites.\n\nTambém está ligado a <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, que pode monitorizar a atividade da rede pessoal."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"O dispositivo permanecerá bloqueado até ser desbloqueado manualmente"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Receber notificações mais rapidamente"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Reduzir"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"O ecrã está fixado"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Esta opção mantém o item visível até o soltar. Toque sem soltar em Anterior e em Vista geral para soltar."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Esta opção mantém o item visível até o soltar. Toque sem soltar em Vista geral para soltar."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Compreendi"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Pretende ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Ativado"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Com os controlos de notificações do consumo de energia, pode definir um nível de importância de 0 a 5 para as notificações de aplicações. \n\n"<b>"Nível 5"</b>" \n- Mostrar no início da lista de notificações \n- Permitir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre \n\n"<b>"Nível 4"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre\n\n"<b>"Nível 3"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n\n"<b>"Nível 2"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n- Ocultar do ecrã de bloqueio e da barra de estado \n- Mostrar no fim da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações da aplicação"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Já não recebe estas notificações."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notificações da aplicação <xliff:g id="APP">%s</xliff:g> para"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Baixa"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Média"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Alta"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgente"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Sem interrupção sonora ou visual"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Mostrar silenciosamente"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Emitir som"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Emitir som e aparecer no ecrã"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Mais definições"</string>
     <string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Controlos de notificações do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 70618c9..7edebe0 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Tela de bloqueio."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Configurações"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Visão geral."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Tela de bloqueio de trabalho"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Fechar"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"O Wi-Fi foi desativado."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabalho"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Modo noturno"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"A NFC está desativada"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"A NFC está ativada"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nenhum item recente"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Você limpou tudo"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do app"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Dispensar"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Abrir"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dividir a tela para a parte superior"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dividir a tela para a esquerda"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dividir a tela para a direita"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Desconectar VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Seu dispositivo é gerenciado por <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> usa <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> para gerenciar seu dispositivo."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"O admin. pode monitorar e gerenciar config., acesso corporativo, apps, dados associados ao dispos. e as info. de local do dispos."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"O admin. pode monitorar e gerenciar config., acesso corporativo, apps, dados associados ao dispos. e info de local do dispositivo."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Saber mais"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Você está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade na rede, incluindo e-mails, apps e websites."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Abrir configurações de VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu dispositivo.\n\nPara ver mais informações, entre em contato com o administrador."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu dispositivo.\n\nPara ver mais informações, entre em contato com o administrador."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Você deu permissão para um app configurar uma conexão VPN.\n\nEsse app pode monitorar seu dispositivo e a atividade na rede, incluindo e-mails, apps e websites."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nSeu administrador pode monitorar suas atividades de rede, incluindo e-mails, apps e websites.\n\nPara mais informações, entre em contato com seu administrador.\n\nVocê também está conectado a uma VPN, a qual pode monitorar suas atividades de rede."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nSeu administrador pode monitorar sua atividade de rede, incluindo e-mails, apps e websites.\n\nPara ver mais informações, entre em contato com o administrador.\n\nVocê também está conectado a uma VPN, que pode monitorar sua atividade de rede."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Você está conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorar sua atividade na rede, incluindo e-mails, apps e websites."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Você está conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorar sua atividade pessoal na rede, incluindo e-mails, apps e websites."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Você está conectado a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pode monitorar sua atividade pessoal na rede, incluindo e-mails, apps e websites."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ele está conectado a <xliff:g id="APPLICATION">%2$s</xliff:g>, que pode monitorar sua atividade profissional na rede, incluindo e-mails, apps e websites.\n\nPara ver mais informações, entre em contato com seu administrador."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ele está conectado ao app <xliff:g id="APPLICATION">%2$s</xliff:g>, que pode monitorar sua atividade profissional de rede, incluindo e-mails, apps e websites.\n\nPara ver mais informações, entre em contato com o administrador."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Seu perfil de trabalho é gerenciado por <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ele está conectado a <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, que pode monitorar sua atividade profissional na rede, incluindo e-mails, apps e websites.\n\nVocê também está conectado a <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, que pode monitorar sua atividade pessoal na rede."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"O dispositivo permanecerá bloqueado até que você o desbloqueie manualmente"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Receba notificações mais rápido"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Expandir"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Recolher"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"A tela está fixada"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Visão geral e mantenha essas opções pressionadas para liberar."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Entendi"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Não, obrigado"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Ativado"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Você deixará de receber essas notificações."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notificações do app <xliff:g id="APP">%s</xliff:g> para"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Baixa"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Média"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Alta"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgente"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Sem som ou interrupção visual"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Mostrar de forma silenciosa"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Emitir som"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Emitir som e exibir na tela"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
     <string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificação do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 2544849..0cfd530 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -188,8 +188,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Ecranul de blocare."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Setări"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Vizualizare generală"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Ecran de blocare pentru serviciu"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Închideți"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Conexiunea prin Wi-Fi este dezactivată."</string>
@@ -326,6 +325,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Avertizare: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modul de lucru"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Lumină de noapte"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Serviciul NFC este dezactivat"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Serviciul NFC este activat"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Niciun element recent"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ați șters tot"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informații despre aplicație"</string>
@@ -339,6 +341,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divizare pe orizontală"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divizare pe verticală"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divizare personalizată"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Respingeți"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Deschideți"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Divizați ecranul în partea de sus"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Divizați ecranul la stânga"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Divizați ecranul la dreapta"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Încărcată"</string>
@@ -419,20 +426,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Deconectați rețeaua VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Dispozitivul dvs. este gestionat de <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> folosește <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> pentru a vă gestiona dispozitivul."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administratorul dvs. poate monitoriza și gestiona setările, accesul la nivelul companiei, aplicațiile, datele asociate cu dispozitivul dvs. și informațiile despre locația dispozitivului."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Admin. dvs. poate monit. și gest. set., accesul la niv. companiei, ap., datele asoc. cu disp. dvs. și info. despre locația disp."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Aflați mai multe"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"V-ați conectat la aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>, care vă poate monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile accesate."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Deschideți Setări VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administratorul dvs. a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul de pe dispozitivul dvs.\n\nPentru mai multe informații, contactați administratorul."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administratorul dvs. a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul de pe dispozitivul dvs.\n\nPentru mai multe informații, contactați administratorul."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Ați acordat unei aplicații permisiunea de a configura o conexiune VPN.\n\nAceastă aplicație poate monitoriza activitatea de pe dispozitiv și în rețea, inclusiv e-mailurile, aplicațiile și site-urile."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Profilul de serviciu este gestionat de <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministratorul poate monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile.\n\nPentru mai multe informații, contactați administratorul.\n\nDe asemenea, sunteți conectat(ă) la o rețea VPN, care vă poate monitoriza activitatea în rețea."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Profilul dvs. de serviciu este gestionat de <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministratorul dvs. vă poate monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile.\n\nPentru mai multe informații, contactați administratorul.\n\nDe asemenea, sunteți conectat(ă) la o rețea VPN care vă poate monitoriza activitatea în rețea."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Sunteți conectat(ă) la <xliff:g id="APPLICATION">%1$s</xliff:g>, care vă poate monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Sunteți conectat(ă) la <xliff:g id="APPLICATION">%1$s</xliff:g>, care vă poate monitoriza activitatea în rețeaua personală, inclusiv e-mailurile, aplicațiile și site-urile."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"V-ați conectat la aplicația <xliff:g id="APPLICATION">%1$s</xliff:g>, care vă poate monitoriza activitatea personală în rețea, inclusiv e-mailurile, aplicațiile și site-urile accesate."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Profilul de serviciu este gestionat de <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Este conectat la <xliff:g id="APPLICATION">%2$s</xliff:g>, care vă poate monitoriza activitatea în rețeaua de serviciu, inclusiv e-mailurile, aplicațiile și site-urile.\n\nPentru mai multe informații, contactați administratorul."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Profilul dvs. de serviciu este gestionat de <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Acesta este conectat la <xliff:g id="APPLICATION">%2$s</xliff:g>, care vă poate monitoriza activitatea în rețeaua de serviciu, inclusiv e-mailurile, aplicațiile și site-urile.\n\nPentru mai multe informații, contactați administratorul."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Profilul de serviciu este gestionat de <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Este conectat la <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, care vă poate monitoriza activitatea în rețeaua de serviciu, inclusiv e-mailurile, aplicațiile și site-urile.\n\nDe asemenea, sunteți conectat(ă) la <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, care vă poate monitoriza activitatea în rețeaua personală."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Dispozitivul va rămâne blocat până când îl deblocați manual"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Obțineți notificări mai rapid"</string>
@@ -444,10 +451,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Extindeți"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Restrângeți"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ecranul este fixat"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunile Înapoi și Recente pentru a anula fixarea."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunea Recente pentru a anula fixarea."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Am înțeles"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nu, mulțumesc"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ascundeți <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -514,28 +519,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Activate"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Dezactivate"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Folosind comenzile de gestionare a notificărilor, puteți să setați un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificări"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Nu veți mai primi aceste notificări."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Notificări <xliff:g id="APP">%s</xliff:g> pentru"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Scăzută"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Medie"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Ridicată"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgentă"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Fără sunet sau întrerupere vizuală"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Se afișează fără sunet"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Se emite un sunet"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Se emite un sunet și se evidențiază pe ecran"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Mai multe setări"</string>
     <string name="notification_done" msgid="5279426047273930175">"Terminat"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Opțiuni privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index aa7695a..0686ca6 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -189,8 +189,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Заблокированный экран."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Настройки"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Обзор."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Заблокировано"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Закрыть"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Модуль Wi-Fi отключен."</string>
@@ -328,6 +327,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Предупреждение: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Рабочий режим"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ночной режим"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"Модуль NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"Модуль NFC отключен"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"Модуль NFC включен"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Недавних приложений нет"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Вы очистили всё"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Сведения о приложении"</string>
@@ -341,6 +343,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Разделить по горизонтали"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Разделить по вертикали"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Разделить по-другому"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Закрыть"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Открыть"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Разделить экран по верхнему краю"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Разделить экран по левому краю"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Разделить экран по правому краю"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Батарея заряжена"</string>
@@ -421,20 +428,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Отключить VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Этим устройством управляет приложение \"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>\""</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"Компания \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\" управляет устройством с помощью приложения \"<xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>\""</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Администратор контролирует настройки, приложения, доступ к ресурсам компании, связанные с устройством данные и передачу геоданных."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Администратор контролирует настройки, приложения, доступ к ресурсам компании, связанные с устройством данные и передачу геоданных."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Подробнее…"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Запущено приложение \"<xliff:g id="VPN_APP">%1$s</xliff:g>\". Оно может отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и сайтами."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Открыть настройки VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Администратор включил ведение сетевого журнала, чтобы отслеживать трафик на вашем устройстве.\n\nДля получения подробной информации обращайтесь к администратору."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Администратор включил ведение сетевого журнала, чтобы отслеживать трафик на вашем устройстве.\n\nДля получения подробной информации обращайтесь к администратору."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Вы разрешили приложению подключаться к сети VPN.\n\nОно может отслеживать ваши действия на устройстве и в Интернете, включая работу с электронной почтой, приложениями и веб-сайтами."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Вашим корпоративным профилем управляет <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nАдминистратор может отслеживать ваши действия в Интернете, включая работу с электронной почтой, приложениями и веб-сайтами.\n\nЗа подробностями обратитесь к нему.\n\nУстройство также подключено к сети VPN, в которой возможно отслеживание ваших действий."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Вашим корпоративным профилем управляет <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nАдминистратор может отслеживать ваши действия в сети, в том числе с электронной почтой, приложениями и веб-сайтами.\n\nДля получения подробной информации обращайтесь к администратору.\n\nВы также подключены к сети VPN, в которой можно отслеживать ваши действия."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"Сеть VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Запущено приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\", которое может отслеживать ваши действия в Интернете, включая работу с электронной почтой, приложениями и веб-сайтами."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Запущено приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\", которое может отслеживать ваши действия в Интернете (выполняемые в личном профиле), включая работу с электронной почтой, приложениями и веб-сайтами."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Запущено приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\", которое может отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и веб-сайтами."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Вашим рабочим профилем управляет \"<xliff:g id="ORGANIZATION">%1$s</xliff:g>\". Приложение \"<xliff:g id="APPLICATION">%2$s</xliff:g>\" может отслеживать ваши действия в Интернете, включая работу с электронной почтой, приложениями и веб-сайтами.\n\nЗа дополнительной информацией обратитесь к своему администратору."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Вашим корпоративным профилем управляет <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Приложение <xliff:g id="APPLICATION">%2$s</xliff:g> может отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и веб-сайтами.\n\nЗа подробностями обратитесь к своему администратору."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Вашим рабочим профилем управляет \"<xliff:g id="ORGANIZATION">%1$s</xliff:g>\". Приложение \"<xliff:g id="APPLICATION_WORK">%2$s</xliff:g>\" может отслеживать ваши действия в Интернете, включая работу с электронной почтой, приложениями и веб-сайтами.\n\nПриложение \"<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>\" может отслеживать ваши действия в Интернете, выполняемые в личном профиле."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Устройство необходимо будет разблокировать вручную"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Быстрый доступ к уведомлениям"</string>
@@ -446,10 +453,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Развернуть"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Свернуть"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Блокировка в приложении включена"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Приложение останется активным, пока вы не отмените блокировку, нажав и удерживая кнопки \"Назад\" и \"Обзор\"."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Приложение останется активным, пока вы не отмените блокировку, нажав и удерживая кнопку \"Обзор\"."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"ОК"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Нет, спасибо"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Скрыть параметр \"<xliff:g id="TILE_LABEL">%1$s</xliff:g>\"?"</string>
@@ -516,28 +521,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Включено"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Отключено"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"С помощью этой функции вы можете устанавливать уровень важности уведомлений от 0 до 5 для каждого приложения.\n\n"<b>"Уровень 5"</b>\n"‒ Помещать уведомления в начало списка.\n‒ Показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 4\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 3\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\nУровень 2\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\nУровень 1\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\n‒ Не показывать на экране блокировки и в строке состояния.\n‒ Помещать уведомления в конец списка.\nУровень 0\n"<b></b>\n"‒ Блокировать все уведомления приложения."</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Уведомления"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Вы больше не будете получать эти уведомления."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g>: уведомления"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Низкий приоритет"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Средний приоритет"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Высокий приоритет"</string>
+    <string name="high_importance" msgid="730741630855788381">"Срочно"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Без уведомлений"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Без звука"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Звук"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Звук и всплывающее окно"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Другие настройки"</string>
     <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Управление уведомлениями (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 4b540b9..2c752f2 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"අගුළු තිරය."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"සැකසීම්"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"දළ විශ්ලේෂණය."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"කාර්යාල අගුලු තිරය"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"වසන්න"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi අක්‍රියයි."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> අවවාද කිරීම"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"වැඩ ප්‍රකාරය"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"රාත්‍රී ආලෝකය"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"මෑත අයිතම නැත"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ඔබ සියලු දේ හිස් කර ඇත"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"යෙදුම් තොරතුරු"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"තිරස්ව වෙන් කරන්න"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"සිරස්ව වෙන් කරන්න"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"අභිමත ලෙස වෙන් කරන්න"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"අස් කරන්න"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"විවෘත"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"තිරය ඉහළට බෙදන්න"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"තිරය වමට බෙදන්න"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"තිරය දකුණට බෙදන්න"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"අරෝපිතයි"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN විසන්ධි කරන්න"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"ඔබගේ උපාංගය <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> මගින් කළමනාකරණය කෙරේ."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ඔබගේ උපාංගය කළමනාකරණය කිරීමට <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> භාවිත කරයි."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"ඔබේ උපාංගය සමඟ සම්බන්ධිත සැකසීම්, සංස්ථාපිත ප්‍රවේශය, යෙදුම්, දත්ත, සහ ඔබගේ උපාංගයේ ස්ථාන තොරතුරු නිරීක්ෂණය සහ කළමනාකරණය කිරීමට ඔබගේ පරිපාලකට හැකිය."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"තව දැන ගන්න"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි <xliff:g id="VPN_APP">%1$s</xliff:g>, වෙත ඔබ සම්බන්ධ වී ඇත."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN සැකසීම් විවෘත කරන්න"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"ඔබගේ පරිපාලක ඔබගේ උපාංගය මත තදබදය නිරීක්ෂණය කරන, ජාල ඇතුළු වීම ක්‍රියාත්මක කර ඇත.\n\nවැඩිදුර තොරතුරු සඳහා ඔබේ පරිපාලක අමතන්න."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"ඔබ VPN සම්බන්ධතාවක් පිහිටුවීමට යෙදුමකට අවසරයක් දී ඇත.\n\nමෙම යෙදුමට ඔබේ ඊ-තැපැල්, යෙදුම්, සහ වෙබ් අඩවි ඇතුළු, ඔබගේ උපාංග සහ ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කිරීමට හැකිය."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"ඔබේ කාර්ය පැතිකඩ කළමනාකරණය කරන්නේ <xliff:g id="ORGANIZATION">%1$s</xliff:g> විසිනි.\n\nඔබේ පරිපාලකට ඔබේ ඊ-තැපැල්, යෙදුම්, සහ ආරක්ෂාකාරී වෙබ් අඩවි ඇතුළු, ඔබගේ ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කිරීමට හැකියාව ඇත.\n\nවැඩිදුර තොරතුරු සඳහා, ඔබගේ පරිපාලක අමතන්න.\n\nඔබේ ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කිරීමට හැකි VPN සම්බන්ධතාවයකටද, ඔබ සම්බන්ධව ඇත."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි, <xliff:g id="APPLICATION">%1$s</xliff:g> වෙත ඔබ සම්බන්ධ වී ඇත."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ පෞද්ගලික ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි, <xliff:g id="APPLICATION">%1$s</xliff:g> වෙත ඔබ සම්බන්ධ වී ඇත."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ පෞද්ගලික ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි, <xliff:g id="APPLICATION">%1$s</xliff:g> වෙත ඔබ සම්බන්ධ වී ඇත."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"ඔබේ කාර්යාල පැතිකඩ කළමනාකරණය කරන්නේ <xliff:g id="ORGANIZATION">%1$s</xliff:g> විසිනි. එය ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ කාර්යාල ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි, <xliff:g id="APPLICATION">%2$s</xliff:g> වෙත ඔබ සම්බන්ධ වී ඇත.\n\nවැඩිදුර විස්තර සඳහා, ඔබේ පරිපාලක අමතන්න."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"ඔබේ කාර්යාල පැතිකඩ කළමනාකරණය කරන්නේ <xliff:g id="ORGANIZATION">%1$s</xliff:g> විසිනි. එය ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ කාර්යාල ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි, <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, වෙත ඔබ සම්බන්ධ වී ඇත.\n\nඔබ ඔබේ පෞද්ගලික ජාල ක්‍රියාකාරකම් නිරීක්ෂණය කළ හැකි, <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> වෙතද සම්බන්ධ වී ඇත."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"ඔබ අතින් අගුළු අරින තුරු උපකරණය අගුළු වැටි තිබේ"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"දැනුම්දීම් ඉක්මනින් ලබාගන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index b6b5361..4013f41 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -189,8 +189,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Uzamknutá obrazovka"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Nastavenia"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Prehľad"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Uzamknutá obrazovka pracovného profilu"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Zavrieť"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Pripojenie Wi-Fi je vypnuté."</string>
@@ -328,6 +327,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozornenie pri <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Pracovný režim"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nočný režim"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC je deaktivované"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC je aktivované"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Žiadne nedávne položky"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vymazali ste všetko"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informácie o aplikácii"</string>
@@ -341,6 +343,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Rozdeliť vodorovné"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Rozdeliť zvislé"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Rozdeliť vlastné"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Odmietnuť"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Otvoriť"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Rozdelená obrazovka hore"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Rozdelená obrazovka naľavo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Rozdelená obrazovka napravo"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabitá"</string>
@@ -421,20 +428,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Odpojiť sieť VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Vaše zariadenie spravuje aplikácia <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> spravuje vaše zariadenie pomocou aplikácie <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Správca môže sledovať a spravovať nastavenia, firemný prístup, aplikácie a údaje súvisiace s týmto zariadením vrátane informácií o polohe vášho zariadenia."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Správca môže sledovať a spravovať nastavenia, firemný prístup, aplikácie a údaje súvisiace s týmto zariadením vrátane informácií o polohe vášho zariadenia."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Ďalšie informácie"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Ste pripojený/-á k aplikácii <xliff:g id="VPN_APP">%1$s</xliff:g>, ktorá môže sledovať vašu aktivitu v sieti vrátane e-mailových správ, aplikácií a webových stránok."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Otvoriť Nastavenia pripojenia VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Váš správca aktivoval zapisovanie do denníka siete, ktoré sleduje premávku na vašom zariadení.\n\nĎalšie informácie vám poskytne správca."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Správca aktivoval zapisovanie do denníka siete, ktoré sleduje premávku na vašom zariadení.\n\nĎalšie informácie vám poskytne správca."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Určitej aplikácii ste udelili povolenie nastaviť pripojenie VPN.\n\nTáto aplikácia môže sledovať vaše zariadenie a aktivitu v sieti vrátane e-mailových správ, aplikácií a webových stránok."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Váš pracovný profil spravuje organizácia <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nSprávca môže sledovať vašu aktivitu v sieti vrátane e-mailových správ, aplikácií a webových stránok.\n\nĎalšie informácie získate od svojho správcu.\n\nSte tiež pripojený/-á k sieti VPN, ktorá môže sledovať vašu aktivitu v sieti."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Váš pracovný profil spravuje organizácia <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nSprávca môže sledovať vašu aktivitu v sieti vrátane e-mailov, aplikácií a webov.\n\nĎalšie informácie vám poskytne správca.\n\nMáte tiež aktívne pripojenie k sieti VPN, ktorá môže sledovať vašu aktivitu v rámci siete."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Ste pripojený/-á k aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g>, ktorá môže sledovať vašu aktivitu v sieti vrátane e-mailových správ, aplikácií a webových stránok."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Ste pripojený/-á k aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g>, ktorá môže sledovať vašu osobnú aktivitu v sieti vrátane e-mailových správ, aplikácií a webových stránok."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Ste pripojený/-á k aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g>, ktorá môže sledovať vašu osobnú aktivitu v sieti vrátane e-mailových správ, aplikácií a webových stránok."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Váš pracovný profil spravuje organizácia <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Je pripojený k aplikácii <xliff:g id="APPLICATION">%2$s</xliff:g>, ktorá môže sledovať vašu pracovnú aktivitu v sieti vrátane e-mailových správ, aplikácií a webových stránok.\n\nĎalšie informácie získate od svojho správcu."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Váš pracovný profil spravuje organizácia <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Je pripojený k aplikácii <xliff:g id="APPLICATION">%2$s</xliff:g>, ktorá môže sledovať vašu pracovnú aktivitu v sieti vrátane e-mailových správ, aplikácií a webov.\n\nĎalšie informácie vám poskytne správca."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Váš pracovný profil spravuje organizácia <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Je pripojený k aplikácii <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, ktorá môže sledovať vašu pracovnú aktivitu v sieti vrátane e-mailových správ, aplikácií a webových stránok.\n\nSte tiež pripojený/-á k aplikácii <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, ktorá môže sledovať vašu osobnú aktivitu v sieti."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Zariadenie zostane uzamknuté, dokým ho ručne neodomknete."</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Získavať upozornenia rýchlejšie"</string>
@@ -446,10 +453,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Rozbaliť"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Zbaliť"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Obrazovka je pripnutá"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Obsah bude pripnutý v zobrazení, dokým ho neuvoľníte. Uvoľníte ho stlačením a podržaním tlačidiel Späť a Prehľad."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Obsah bude pripnutý v zobrazení, dokým ho neuvoľníte. Uvoľníte ho stlačením a podržaním tlačidla Prehľad."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Dobre"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nie, vďaka"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skryť <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -516,28 +521,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Zapnuté"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Vypnuté"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Pomocou ovládacích prvkov zobrazovania upozornení môžete nastaviť pre upozornenia aplikácie úroveň dôležitosti od 0 do 5. \n\n"<b>"Úroveň 5"</b>" \n– Zobrazovať v hornej časti zoznamu upozornení. \n– Povoliť prerušenia na celú obrazovku. \n– Vždy zobrazovať čiastočne. \n\n"<b>"Úroveň 4"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Vždy zobrazovať čiastočne. \n\n"<b>"Úroveň 3"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n\n"<b>"Úroveň 2"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n– Nikdy nespúšťať zvuk ani vibrácie. \n\n"<b>"Úroveň 1"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n– Nikdy nespúšťať zvuk ani vibrácie. \n– Skryť na uzamknutej obrazovke a v stavovom riadku. \n– Zobraziť v dolnej časti zoznamu upozornení. \n\n"<b>"Úroveň 0"</b>" \n– Blokovať všetky upozornenia z aplikácie."</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Upozornenia"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Takéto upozornenia už nebudete dostávať."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Upozornenia aplikácie <xliff:g id="APP">%s</xliff:g>"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Nízka"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Stredná"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Vysoká"</string>
+    <string name="high_importance" msgid="730741630855788381">"Neodkladná"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Bez zvuku a vizuálneho vyrušenia"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Zobraziť bez zvukov"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Vydať zvukový signál"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Vydať zvukový signál a vyskočiť na obrazovku"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Ďalšie nastavenia"</string>
     <string name="notification_done" msgid="5279426047273930175">"Hotovo"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Ovládacie prvky pre upozornenia z aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index b9a8266..9236970 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -189,8 +189,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Zaklenjen zaslon"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Nastavitve"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Pregled."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Zaklenjen zaslon delovnega profila"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Zapri"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi je izklopljen."</string>
@@ -328,6 +327,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Opozorilo – <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Način za delo"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nočna svetloba"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Ni nedavnih elementov"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Vse te počistili"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Podatki o aplikaciji"</string>
@@ -341,6 +346,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Razdeli vodoravno"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Razdeli navpično"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Razdeli po meri"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Opusti"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Odpri"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Razdeljen zaslon na vrhu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Razdeljen zaslon na levi"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Razdeljen zaslon na desni"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulator napolnjen"</string>
@@ -421,20 +431,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Prekini povezavo z VPN-jem"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Napravo upravlja aplikacija <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> uporablja aplikacijo <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> za upravljanje naprave."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Skrbnik lahko nadzira in upravlja nastavitve, dostop za podjetje, aplikacije, z napravo povezane podatke in podatke o lokaciji."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Več o tem"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Povezani ste z aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>, ki lahko nadzira omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Odpri nastavitve omrežja VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Skrbnik je vklopil beleženje omrežnega prometa, ki nadzoruje promet v napravi.\n\nČe želite več informacij, se obrnite na skrbnika."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Aplikaciji ste dovolili vzpostavitev povezave VPN.\n\nTa aplikacija lahko nadzira napravo in omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Delovni profil upravlja organizacija <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nSkrbnik lahko nadzira omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti.\n\nČe želite več informacij, se obrnite na skrbnika.\n\nPovezani ste tudi z omrežjem VPN, ki lahko nadzira omrežno dejavnost."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Povezani ste z aplikacijo <xliff:g id="APPLICATION">%1$s</xliff:g>, ki lahko nadzira omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Povezani ste z aplikacijo <xliff:g id="APPLICATION">%1$s</xliff:g>, ki lahko nadzira vašo osebno omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Povezani ste z aplikacijo <xliff:g id="APPLICATION">%1$s</xliff:g>, ki lahko nadzira vašo osebno omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Delovni profil upravlja organizacija <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je z aplikacijo <xliff:g id="APPLICATION">%2$s</xliff:g>, ki lahko nadzira vašo delovno omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti.\n\nČe želite več informacij, se obrnite na skrbnika."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Delovni profil upravlja organizacija <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je z aplikacijo <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, ki lahko nadzira vašo delovno omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti.\n\nPovezani ste tudi z aplikacijo <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, ki lahko nadzira vašo osebno omrežno dejavnost."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Naprava bo ostala zaklenjena, dokler je ročno ne odklenete."</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Hitrejše prejemanje obvestil"</string>
@@ -446,10 +460,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Razširi"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Strni"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Zaslon je pripet"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"S tem ostane zaslon viden, dokler ga ne odpnete. Če ga želite odpeti, hkrati pridržite gumba za nazaj in pregled."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"S tem ostane zaslon viden, dokler ga ne odpnete. Če ga želite odpeti, pridržite gumb za pregled."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Razumem"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, hvala"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite skriti <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -516,28 +528,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Vklopljeno"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Izklopljeno"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"S kontrolniki za pomebnost obvestila je mogoče za obvestila aplikacije nastaviti stopnjo pomembnosti od 0 do 5. \n\n"<b>"Stopnja 5"</b>" \n– Prikaz na vrhu seznama obvestil \n– Omogočanje prekinitev v celozaslonskem načinu \n– Vedno prikaži hitre predoglede \n\n"<b>"Stopnja 4"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Vedno prikaži hitre predoglede \n\n"<b>"Stopnja 3"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n\n"<b>"Stopnja 2"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n– Nikoli ne uporabi zvoka in vibriranja \n\n"<b>"Stopnja 1"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n– Nikoli ne uporabi zvoka in vibriranja \n– Skrivanje na zaklenjenem zaslonu in v vrstici stanja \n– Prikaz na dnu seznama obvestil \n\n"<b>"Stopnja 0"</b>" \n– Blokiranje vseh obvestil aplikacije"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Obvestila"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Teh obvestil ne boste več prejemali."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Obvestila iz aplikacije <xliff:g id="APP">%s</xliff:g> za"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Nizka"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Srednja"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Visoka"</string>
+    <string name="high_importance" msgid="730741630855788381">"Nujno"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Brez zvočne ali vizualne prekinitve"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Prikaži brez zvoka"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Predvajaj zvok"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Predvajaj zvok in prikaži na zaslonu"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Več nastavitev"</string>
     <string name="notification_done" msgid="5279426047273930175">"Dokončano"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index d54b5a3..40b7c51d 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Ekrani i kyçjes."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Cilësimet"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Përmbledhja."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Ekrani i kyçjes së punës"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Mbylle"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi është i çaktivizuar."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Paralajmërim për kufirin prej <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modaliteti i punës"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Drita e natës"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC është çaktivizuar"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC është aktivizuar"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Nuk ka asnjë artikull të fundit"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"I ke pastruar të gjitha"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacioni i aplikacionit"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Ndaje horizontalisht"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Ndaj vertikalisht"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Ndaj të personalizuarën"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Hiqe"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Hap"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ndaje ekranin lart"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ndaje ekranin në të majtë"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ndaje ekranin në të djathtë"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"I ngarkuar"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Shkëput VPN-në"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Pajisja jote menaxhohet nga <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> përdor <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> për të menaxhuar pajisjen tënde."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administratori yt mund të monitorojë dhe të menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat në lidhje me pajisjen si dhe informacionet e vendndodhjes së pajisjes tënde."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Administratori yt mund të monitorojë dhe të menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat në lidhje me pajisjen si dhe informacionet e vendndodhjes së pajisjes tënde."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Mëso më shumë"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Je i lidhur me aplikacionin <xliff:g id="VPN_APP">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Hap cilësimet e VPN-së"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administratori yt ka aktivizuar regjistrimin e rrjetit, i cili monitoron trafikun në pajisjen tënde.\n\nPër më shumë informacione, kontakto me administratorin."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administratori yt ka aktivizuar regjistrimin e rrjetit, i cili monitoron trafikun në pajisjen tënde.\n\nPër më shumë informacione, kontakto me administratorin."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"I dhe leje një aplikacioni që të konfigurojë një lidhje VPN.\n\nKy aplikacion mund të monitorojë pajisjen tënde dhe aktivitetin e rrjetit, përfshirë mailet, aplikacionet dhe sajtet e uebit."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Profili yt i punës menaxhohet nga <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministratori yt mund të monitorojë aktivitetin tënd të rrjetit, duke përfshirë mail-at, aplikacionet dhe faqet e internetit.\n\nPër më shumë informacion, kontakto me administratorin tënd.\n\nJe i lidhur edhe me një VPN, që mund të monitorojë aktivitetin tënd të rrjetit."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Profili yt i punës menaxhohet nga <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministratori yt mund të monitorojë aktivitetin tënd të rrjetit, duke përfshirë email-et, aplikacionet dhe sajtet e uebit.\n\nPër më shumë informacion, kontakto me administratorin tënd.\n\nJe i lidhur edhe me një VPN, që mund të monitorojë aktivitetin tënd të rrjetit."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Je i lidhur me aplikacionin <xliff:g id="APPLICATION">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd në rrjet përfshirë mailet, aplikacionet dhe sajtet e uebit."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Je i lidhur me aplikacionin <xliff:g id="APPLICATION">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd personal në rrjet, përfshirë mailet, aplikacionet dhe sajtet e uebit."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Je i lidhur me aplikacionin <xliff:g id="APPLICATION">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd personal në rrjet, përfshirë mailet, aplikacionet dhe sajtet e uebit."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Profili yt i punës menaxhohet nga <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ai është i lidhur me <xliff:g id="APPLICATION">%2$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd të punës në rrjet, përfshirë mailet, aplikacionet dhe sajtet e uebit.\n\nPër më shumë informacione, kontakto me administratorin tënd."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Profili yt i punës menaxhohet nga <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ai është i lidhur me <xliff:g id="APPLICATION">%2$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd të punës në rrjet, përfshirë email-et, aplikacionet dhe sajtet e uebit.\n\nPër më shumë informacione, kontakto me administratorin tënd."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Profili yt i punës menaxhohet nga <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ai është i lidhur me <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd të punës në rrjet, përfshirë mailet, aplikacionet dhe sajtet e uebit.\n\nJe lidhur gjithashtu edhe me <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd personal në rrjet."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Pajisje do të qëndrojë e kyçur derisa ta shkyçësh manualisht"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Merr njoftime më shpejt"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Zgjeroje"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Mbylle"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekrani u gozhdua"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Kjo e ruan në pamje deri sa ta heqësh nga gozhdimi. Prek dhe mbaj të shtypur \"Prapa\" dhe \"Përmbledhje\" për ta hequr nga gozhdimi."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Kjo e ruan në pamje deri sa ta heqësh nga gozhdimi. Prek dhe mbaj të shtypur \"Përmbledhje\" për ta hequr nga gozhdimi."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"E kuptova!"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Jo, faleminderit!"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Të fshihet <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Aktiv"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Joaktiv"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Me kontrollet e njoftimit të energjisë, mund të caktosh një nivel rëndësie nga 0 në 5 për njoftimet e një aplikacioni. \n\n"<b>"Niveli 5"</b>" \n- Shfaq në krye të listës së njoftimeve \n- Lejo ndërprerjen e ekranit të plotë \n- Gjithmonë shfaq shpejt \n\n"<b>"Niveli 4"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Gijthmonë shfaq shpejt \n\n"<b>"Niveli 3"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n\n"<b>"Niveli 2"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n- Asnjëherë mos lësho tingull dhe dridhje \n\n"<b>"Niveli 1"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n- Asnjëherë mos lësho tingull ose dridhje \n- Fshih nga ekrani i kyçjes dhe shiriti i statusit \n- Shfaq në fund të listës së njoftimeve \n\n"<b>"Niveli 0"</b>" \n- Blloko të gjitha njoftimet nga aplikacioni"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Njoftime"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Këto njoftime nuk do t\'i marrësh më."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Njoftimet e <xliff:g id="APP">%s</xliff:g> për"</string>
+    <string name="min_importance" msgid="7559703098688382595">"I ulët"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Mesatar"</string>
+    <string name="default_importance" msgid="6400766013567512061">"I lartë"</string>
+    <string name="high_importance" msgid="730741630855788381">"Urgjent"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Asnjë tingull apo ndërprerje vizuale"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Shfaq në heshtje"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Bëj tingull"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Bëj një tingull dhe shfaq në ekran"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Cilësime të tjera"</string>
     <string name="notification_done" msgid="5279426047273930175">"U krye"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrollet e njoftimeve të <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 727c01e..d329f1b 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -186,8 +186,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Закључани екран."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Подешавања"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Преглед."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Закључани екран за посао"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Затвори"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi је искључен."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Упозорење за <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Режим рада"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ноћно светло"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC је онемогућен"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC је омогућен"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Нема недавних ставки"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Обрисали сте све"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информације о апликацији"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Подели хоризонтално"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Подели вертикално"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Прилагођено дељење"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Одбаци"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Отвори"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Подели екран нагоре"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Подели екран налево"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Подели екран надесно"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Напуњена је"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Прекини везу са VPN-ом"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Уређајем управља <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> користи <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> за управљање уређајем."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Администратор може да надгледа подешавања, корпоративни приступ, апликације, податке повезане са уређајем и информације о локацији уређаја, као и да управља њима."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Администратор може да надгледа подешавања, корпоративни приступ, апликације, податке повезане са уређајем и информације о локацији уређаја, као и да управља њима."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Сазнајте више"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Повезани сте са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>, која може да надгледа активности на мрежи, укључујући имејлове, апликације и веб-сајтове."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Отворите подешавања VPN-а"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Администратор је укључио евидентирање мреже, које прати саобраћај на уређају.\n\nКонтактирајте администратора за више информација."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Администратор је укључио евидентирање мреже, које прати саобраћај на уређају.\n\nКонтактирајте администратора за више информација."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Дали сте дозволу апликацији да подешава VPN везу.\n\nТа апликација може да надгледа активности на уређају и мрежи, укључујући имејлове, апликације и веб-сајтове."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Профилом за Work управља <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nАдминистратор може да надгледа активности на мрежи, укључујући имејлове, апликације и веб-сајтове.\n\nВише информација потражите од администратора.\n\nПовезани сте и на VPN, који може да надгледа активности на личној мрежи."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> управља профилом за Work.\n\nАдминистратор може да прати активности на мрежи, укључујући имејлове, апликације и веб-сајтове.\n\nКонтактирајте администратора за више информација.\n\nПовезани сте и са VPN-ом, који може да прати активности на мрежи."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Повезани сте са апликацијом <xliff:g id="APPLICATION">%1$s</xliff:g>, која може да надгледа активности на мрежи, укључујући имејлове, апликације и веб-сајтове."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Повезани сте са апликацијом <xliff:g id="APPLICATION">%1$s</xliff:g>, која може да надгледа активности на личној мрежи, укључујући имејлове, апликације и веб-сајтове."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Повезани сте са апликацијом <xliff:g id="APPLICATION">%1$s</xliff:g>, која може да надгледа активности на личној мрежи, укључујући имејлове, апликације и веб-сајтове."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Профилом за Work управља <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Повезан је са апликацијом <xliff:g id="APPLICATION">%2$s</xliff:g>, која може да надгледа активности на пословној мрежи, укључујући имејлове, апликације и веб-сајтове.\n\nВише информација потражите од администратора."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> управља профилом за Work. Он је повезан са апликацијом <xliff:g id="APPLICATION">%2$s</xliff:g>, која може да прати активности на пословној мрежи, укључујући имејлове, апликације и веб-сајтове.\n\nКонтактирајте администратора за више информација."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Профилом за Work управља <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Повезан је са апликацијом <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, која може да надгледа активности на пословној мрежи, укључујући имејлове, апликације и веб-сајтове.\n\nПовезани сте и са апликацијом <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, која може да надгледа активности на личној мрежи."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Уређај ће остати закључан док га не откључате ручно"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Брже добијајте обавештења"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Прошири"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Скупи"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Екран је закачен"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"На овај начин се ово стално приказује док га не откачите. Додирните и задржите Назад и Преглед да бисте га откачили."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"На овај начин се ово стално приказује док га не откачите. Додирните и задржите Преглед да бисте га откачили."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Важи"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Не, хвала"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Желите ли да сакријете <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Укључено"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Искључено"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Помоћу напредних контрола за обавештења можете да подесите ниво важности од 0. до 5. за обавештења апликације. \n\n"<b>"5. ниво"</b>" \n– Приказују се у врху листе обавештења \n- Дозволи прекид режима целог екрана \n– Увек завируј \n\n"<b>"4. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Увек завируј \n\n"<b>"3. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n\n"<b>"2. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n– Никада не производи звук или вибрацију \n\n"<b>"1. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n– Никада не производи звук или вибрацију \n– Сакриј на закључаном екрану и статусној траци \n– Приказују се у дну листе обавештења \n\n"<b>"0. ниво"</b>" \n– Блокирај сва обавештења из апликације"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Обавештења"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Више нећете да добијате ова обавештења."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Обавештења апликације <xliff:g id="APP">%s</xliff:g> за"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Ниско"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Средње"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Високо"</string>
+    <string name="high_importance" msgid="730741630855788381">"Хитно"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Без звучног сигнала или визуелног обавештења"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Приказује се без звучног сигнала"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Емитује се звучни сигнал"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Емитује се звучни сигнал и приказује се на екрану"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Још подешавања"</string>
     <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Контроле обавештења за апликацију <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index a04a761..4b58ff9 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Låsskärm."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Inställningar"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Översikt."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Låsskärm för arbete"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Stäng"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi har inaktiverats."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Varning <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Arbetsläge"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Nattljus"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Listan med de senaste åtgärderna är tom"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Du har tömt listan"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformation"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dela horisontellt"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dela vertikalt"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Dela anpassad"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Ignorera"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Öppna"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Delad skärm till överkanten"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Delad skärm åt vänster"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Delad skärm åt höger"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laddat"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Koppla från VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Enheten hanteras av <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> hanterar enheten med hjälp av <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administratören kan övervaka och hantera inställningar, företagsåtkomst, appar, data med koppling till enheten och enhetens plats."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Läs mer"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Du är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g> som kan övervaka din aktivitet på nätverket, inklusive e-postmeddelanden, appar och webbplatser."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Öppna VPN-inställningar"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administratören har aktiverat nätverksloggning, vilken övervakar trafik på enheten.\n\nKontakta administratören om du vill veta mer."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Du har gett en app behörighet att upprätta en VPN-anslutning.\n\nAppen kan bevaka aktivitet på enheten och nätverket, inklusive e-post, appar och webbplatser."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Jobbprofilen hanteras av <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministratören kan bevaka aktiviteten på nätverket, inklusive e-post, appar och webbplatser.\n\nKontakta administratören för mer information.\n\nDu är även ansluten till ett VPN-nätverk som kan bevaka aktiviteten på nätverket."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Du är ansluten till <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan bevaka aktivitet på nätverket, inklusive e-post, appar och webbplatser."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Du är ansluten till <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan bevaka din privata aktivitet på nätverket, inklusive e-post, appar och webbplatser."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Du är ansluten till <xliff:g id="APPLICATION">%1$s</xliff:g> som kan övervaka din privata aktivitet på nätverket, inklusive e-postmeddelanden, appar och webbplatser."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Jobbprofilen hanteras av <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Den är ansluten till <xliff:g id="APPLICATION">%2$s</xliff:g>, som kan hantera aktivitet på arbetsplatsens nätverk, inklusive e-post, appar och webbplatser.\n\nKontakta administratören för mer information."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Jobbprofilen hanteras av <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Den är ansluten till <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, som kan hantera aktivitet på arbetsplatsens nätverk, inklusive e-post, appar och webbplatser.\n\nDu är även ansluten till <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, som kan hantera privat aktivitet på nätverket."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Enheten förblir låst tills du låser upp den manuellt"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Få aviseringar snabbare"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Utöka"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Komprimera"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skärmen har fästs"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Skärmen visas tills du lossar den. Tryck länge på Tillbaka och Översikt om du vill lossa skärmen."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Skärmen visas tills du lossar den. Tryck länge på Översikt om du vill lossa skärmen."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nej tack"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vill du dölja <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Av"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"På"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Med aviseringsinställningarna kan du ange prioritetsnivå från 0 till 5 för aviseringar från en app. \n\n"<b>"Nivå 5"</b>" \n– Visa högst upp i aviseringslistan\n– Tillåt avbrott i helskärmsläge \n– Snabbvisa alltid \n\n"<b>"Nivå 4"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa alltid \n\n"<b>"Nivå 3"</b>" \n- Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n\n"<b>"Nivå 2"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n– Aldrig med ljud eller vibration \n\n"<b>"Nivå 1"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n– Aldrig med ljud eller vibration \n– Visa inte på låsskärmen och i statusfältet \n– Visa längst ned i aviseringslistan \n\n"<b>"Nivå 0"</b>" \n– Blockera alla aviseringar från appen"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Aviseringar"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Inga fler aviseringar av det här slaget visas."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Aviseringar från <xliff:g id="APP">%s</xliff:g> –"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Låg"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Medelhög"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Hög"</string>
+    <string name="high_importance" msgid="730741630855788381">"Brådskande"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Spela inte upp ljud och visa inte"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Visa utan ljud"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Spela upp ljud"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Spela upp ljud och visa på skärmen"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Fler inställningar"</string>
     <string name="notification_done" msgid="5279426047273930175">"Klar"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Inställningar för <xliff:g id="APP_NAME">%1$s</xliff:g>-aviseringar"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 49c1860..d8a4d42 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Skrini iliyofungwa."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Mipangilio"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Muhtasari."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Skrini iliyofungwa ya kazini"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Funga"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wifi imezimwa."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Onyo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Hali ya kazi"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Mwanga wa Usiku"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC imezimwa"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC imewashwa"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Hakuna vipengee vya hivi karibuni"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Umeondoa vipengee vyote"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maelezo ya Programu"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gawanya Mlalo"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Gawanya Wima"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Maalum Iliyogawanywa"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Ondoa"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Fungua"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Gawa skrini kuelekea juu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Gawa skrini upande wa kushoto"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Gawa skrini upande wa kulia"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Betri imejaa"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Ondoa VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Kifaa chako kinadhibitiwa na <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> inatumia <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> kudhibiti kifaa chako."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Msimamizi wako anaweza kufuatilia na kudhibiti mipangilio, ufikiaji wa mashirika, programu, data inayohusiana na kifaa chako na maelezo ya eneo la kifaa chako."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Msimamizi wako anaweza kufuatilia na kudhibiti mipangilio, ufikiaji wa mashirika, programu, data inayohusiana na kifaa chako na maelezo ya eneo la kifaa chako."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Pata maelezo zaidi"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Umeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Fungua Mipangilio ya VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Msimamizi wako amewasha kumbukumbu ya kuingia mtandaoni ambayo huchunguza trafiki kwenye kifaa chako.\n\nIli kupata maelezo zaidi, wasiliana na msimamizi wako."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Msimamizi wako amewasha kumbukumbu ya kuingia mtandaoni ambayo hufuatilia trafiki kwenye kifaa chako.\n\nKwa maelezo zaidi, wasiliana na msimamizi wako."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Uliruhusu programu iweke muunganisho wa VPN.\n\nProgramu hii inaweza kufuatilia shughuli za kifaa na mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Wasifu wako wa kazini unasimamiwa na <xliff:g id="ORGANIZATION">%1$s</xliff:g>. \n\nMsimamizi wako ana uwezo wa kufuatilia shughuli ya mtandao wako ikiwa ni pamoja na barua pepe, programu, na tovuti. \n\nKwa maelezo zaidi, wasiliana na msimamizi wako.\n\nUmeunganishwa pia kwenye VPN, ambayo inaweza kufuatilia shughuli za mtandao wako."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Wasifu wako wa kazini unadhibitiwa na <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nMsimamizi wako anaweza kufuatilia shughuli za mtandaoni, ikiwa ni pamoja na barua pepe, programu na tovuti.\n\nKwa maelezo zaidi, wasiliana na msimamizi wako.\n\nUmeunganishwa pia kwenye VPN, ambayo inaweza kufuatilia shughuli zako mtandaoni."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Umeunganishwa kwenye <xliff:g id="APPLICATION">%1$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Umeunganishwa kwenye <xliff:g id="APPLICATION">%1$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Umeunganishwa kwenye <xliff:g id="APPLICATION">%1$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Wasifu wako wa kazini unasimamiwa na <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Wasifu huu umeunganishwa kwenye <xliff:g id="APPLICATION">%2$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu, na tovuti. \n\nKwa maelezo zaidi, wasiliana na msimamizi wako."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Wasifu wako wa kazini unadhibitiwa na <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Umeunganishwa kwenye <xliff:g id="APPLICATION">%2$s</xliff:g>, ambayo inaweza kufuatilia shughuli zako za mtandao, ikiwa ni pamoja na barua pepe, programu na tovuti.\n\nKwa maelezo zaidi, wasiliana na msimamizi wako."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Wasifu wako wa kazini unasimamiwa na <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Wasifu huu umeunganishwa kwenye <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, ambayo inaweza kufuatilia mtandao wako wa kazini, ikiwa ni pamoja na barua pepe, programu na tovuti. \n\n Wewe pia umeunganishwa kwenye <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako kibinafsi."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Kifaa kitaendelea kuwa katika hali ya kufungwa hadi utakapokifungua mwenyewe"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Pata arifa kwa haraka"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Panua"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Kunja"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skrini imebandikwa"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Hali hii huifanya ionekane hadi utakapoibandua. Gusa na ushikilie kipengele cha Nyuma na Muhtasari ili ubandue."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Hali hii huifanya ionekane hadi utakapoibandua. Gusa na ushikilie kipengele cha Muhtasari ili ubandue."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Nimeelewa"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Hapana, asante"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ungependa kuficha <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Imewashwa"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Imezimwa"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Ukiwa na udhibiti wa arifa, unaweza kuweka kiwango cha umuhimu wa arifa za programu kuanzia 0 hadi 5. \n\n"<b>"Kiwango cha 5"</b>" \n- Onyesha katika sehemu ya juu ya orodha ya arifa \n- Ruhusu ukatizaji wa skrini nzima \n- Ruhusu arifa za kuchungulia kila wakati\n\n"<b>"Kiwango cha 4"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Ruhusu arifa za kuchungulia kila wakati \n\n"<b>"Kiwango cha 3"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia\n\n"<b>"Kiwango cha 2"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti au mtetemo \n\n"<b>"Kiwango cha 1"</b>" \n- Zuia ukatizaji wa skrini nzima \n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti na mtetemo \n- Usionyeshe skrini iliyofungwa na sehemu ya arifa \n- Onyesha katika sehemu ya chini ya orodha ya arifa \n\n"<b>"Kiwango cha 0"</b>" \n- Zuia arifa zote kutoka programu"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Arifa"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Hutapokea arifa hizi tena."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Arifa za <xliff:g id="APP">%s</xliff:g> za"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Chini"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Wastani"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Juu"</string>
+    <string name="high_importance" msgid="730741630855788381">"Dharura"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Hakuna kukatizwa kwa sauti au maonyesho"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Onyesha chinichini"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Toa sauti"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Toa sauti na ibukizi kwenye skrini"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Mipangilio zaidi"</string>
     <string name="notification_done" msgid="5279426047273930175">"Nimemaliza"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Vidhibiti vya arifa za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index fd147cd..63b9d75 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -34,7 +34,7 @@
     <bool name="config_keyguardUserSwitcher">true</bool>
 
     <!-- Nav bar button default ordering/layout -->
-    <string name="config_navBarLayout" translatable="false">space;back,home,recent;menu_ime</string>
+    <string name="config_navBarLayout" translatable="false">left;back,home,recent;right</string>
 
     <!-- Animation duration when using long press on recents to dock -->
     <integer name="long_press_dock_anim_duration">290</integer>
diff --git a/packages/SystemUI/res/values-sw900dp/config.xml b/packages/SystemUI/res/values-sw900dp/config.xml
index d8f9ef4..221b013 100644
--- a/packages/SystemUI/res/values-sw900dp/config.xml
+++ b/packages/SystemUI/res/values-sw900dp/config.xml
@@ -19,6 +19,6 @@
 <resources>
 
     <!-- Nav bar button default ordering/layout -->
-    <string name="config_navBarLayout" translatable="false">back,home;space;menu_ime,recent</string>
+    <string name="config_navBarLayout" translatable="false">back,home,left;space;right,recent</string>
 
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index de395b8..5b17c54 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"பூட்டுத் திரை."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"அமைப்பு"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"மேலோட்டப் பார்வை."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"பணிப் பூட்டுத் திரை"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"மூடு"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"வைஃபை முடக்கப்பட்டது."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> எச்சரிக்கை"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"பணிப் பயன்முறை"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"இரவு ஒளி"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"சமீபத்திய பணிகள் இல்லை"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"எல்லாவற்றையும் அழித்துவிட்டீர்கள்"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"பயன்பாட்டு தகவல்"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"கிடைமட்டமாகப் பிரி"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"செங்குத்தாகப் பிரி"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"தனிவிருப்பத்தில் பிரி"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"நிராகரி"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"திற"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"திரையை மேல்புறமாகப் பிரி"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"திரையை இடப்புறமாகப் பிரி"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"திரையை வலப்புறமாகப் பிரி"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"சார்ஜ் செய்யப்பட்டது"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPNஐத் துண்டி"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"உங்கள் சாதனத்தை நிர்வகிப்பது: <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"உங்கள் சாதனத்தை நிர்வகிக்க, <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> பயன்பாட்டை <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> பயன்படுத்தும்."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"உங்கள் நிர்வாகியால் அமைப்புகள், நிறுவன அணுகல், பயன்பாடுகள், சாதனத்துடன் தொடர்புடைய தரவு, சாதன இருப்பிடத் தகவல் ஆகியவற்றைக் கண்காணிக்கவும் நிர்வகிக்கவும் முடியும்."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"மேலும் அறிக"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்தப் பயன்பாட்டால் மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN அமைப்புகளைத் திற"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"உங்கள் நிர்வாகி நெட்வொர்க் பதிவெடுத்தலை இயக்கியுள்ளார், இது சாதனத்தில் ட்ராஃபிக்கைக் கண்காணிக்கும்.\n\nமேலும் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN இணைப்பை அமைக்க, பயன்பாட்டிற்கு அனுமதி வழங்கியுள்ளீர்கள்.\n\nஇந்தப் பயன்பாட்டால் மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட, உங்கள் சாதனத்தையும் நெட்வொர்க் செயல்பாட்டையும் கண்காணிக்க முடியும்."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"உங்கள் பணி சுயவிவரத்தை நிர்வகிப்பது: <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nமின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டை நிர்வாகியால் கண்காணிக்க முடியும்.\n\nகூடுதல் தகவலுக்கு, நிர்வாகியைத் தொடர்புகொள்ளவும்.\n\nஉங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய VPN இலும் இணைக்கப்பட்டுள்ளீர்கள்."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்தப் பயன்பாட்டால், மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்தப் பயன்பாட்டால், மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்தப் பயன்பாட்டால் மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"உங்கள் பணி சுயவிவரத்தை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது. <xliff:g id="APPLICATION">%2$s</xliff:g> உடன் இணைக்கப்பட்டதால், மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட உங்கள் பணியிட நெட்வொர்க் செயல்பாட்டை அதனால் கண்காணிக்க முடியும்.\n\nகூடுதல் தகவலுக்கு, நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"உங்கள் பணி சுயவிவரத்தை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது. <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளதால், மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட உங்கள் பணியிட நெட்வொர்க் செயல்பாட்டை அதனால் கண்காணிக்க முடியும்.\n\nமேலும் <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளதால், உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டையும் அதனால் கண்காணிக்க முடியும்."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"நீங்கள் கைமுறையாகத் திறக்கும் வரை, சாதனம் பூட்டப்பட்டிருக்கும்"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"விரைவாக அறிவிப்புகளைப் பெறுதல்"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"விரிவாக்கு"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"சுருக்கு"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"திரை பொருத்தப்பட்டது"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"பொருத்தியதை அகற்றும் வரை இதைக் காட்சியில் வைக்கும். அகற்ற, முந்தையது மற்றும் மேலோட்டப் பார்வையைத் தொட்டுப் பிடிக்கவும்."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"பொருத்தியதை அகற்றும் வரை இதைக் காட்சியில் வைக்கும். அகற்ற, மேலோட்டப் பார்வையைத் தொட்டுப் பிடிக்கவும்."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"புரிந்தது"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"வேண்டாம்"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>ஐ மறைக்கவா?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"இயக்கத்தில்"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"முடக்கத்தில்"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"ஆற்றல்மிக்க அறிவிப்புக் கட்டுப்பாடுகள் மூலம், பயன்பாட்டின் அறிவிப்புகளுக்கு முக்கியத்துவ நிலையை (0-5) அமைக்கலாம். \n\n"<b>"நிலை 5"</b>" \n- அறிவிப்புப் பட்டியலின் மேலே காட்டும் \n- முழுத் திரைக் குறுக்கீட்டை அனுமதிக்கும் \n- எப்போதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டும் \n\n"<b>"நிலை 4"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- எப்போதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டும் \n\n"<b>"நிலை 3"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n\n"<b>"நிலை 2"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n- ஒருபோதும் ஒலி எழுப்பாது, அதிர்வுறாது \n\n"<b>"நிலை 1"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n- ஒருபோதும் ஒலி எழுப்பாது அல்லது அதிர்வுறாது \n- பூட்டுத்திரை மற்றும் நிலைப்பட்டியிலிருந்து மறைக்கும் \n- அறிவிப்புகள் பட்டியலின் கீழே காட்டும் \n\n"<b>"நிலை 0"</b>" \n- பயன்பாட்டின் எல்லா அறிவிப்புகளையும் தடுக்கும்"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"அறிவிப்புகள்"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"இந்த அறிவிப்புகளை இனி பெறமாட்டீர்கள்."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> அறிவிப்புகள்"</string>
+    <string name="min_importance" msgid="7559703098688382595">"குறைவான முக்கியத்துவம்"</string>
+    <string name="low_importance" msgid="6891335321576225228">"நடுத்தர முக்கியத்துவம்"</string>
+    <string name="default_importance" msgid="6400766013567512061">"அதிக முக்கியத்துவம்"</string>
+    <string name="high_importance" msgid="730741630855788381">"அவசரம்"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"ஒலியெழுப்பாது அல்லது காட்சிக் குறுக்கீடு செய்யாது"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"ஒலிக்காமல் காட்டும்"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"ஒலியெழுப்பும்"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"ஒலியெழுப்பி, திரையில் காட்டும்"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"மேலும் அமைப்புகள்"</string>
     <string name="notification_done" msgid="5279426047273930175">"முடிந்தது"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> அறிவிப்புக் கட்டுப்பாடுகள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 2254043..701dc2d 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"లాక్ స్క్రీన్."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"సెట్టింగ్‌లు"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"అవలోకనం."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"కార్యాలయ లాక్ స్క్రీన్"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"మూసివేస్తుంది"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"వైఫై ఆఫ్ చేయబడింది."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> హెచ్చరిక"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"పని మోడ్"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"రాత్రి కాంతి"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC నిలిపివేయబడింది"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ప్రారంభించబడింది"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"మీరు అన్నింటినీ తీసివేసారు"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"అనువర్తన సమాచారం"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"సమతలంగా విభజించు"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"లంబంగా విభజించు"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"అనుకూలంగా విభజించు"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"తీసివేయి"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"తెరువు"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"స్క్రీన్‌ని ఎగువకు విభజించు"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"స్క్రీన్‌ని ఎడమ వైపుకి విభజించు"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"స్క్రీన్‌ని కుడి వైపుకి విభజించు"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ఛార్జ్ చేయబడింది"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPNను డిస్‌కనెక్ట్ చేయి"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"మీ పరికరం <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ద్వారా నిర్వహించబడుతోంది."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> మీ పరికరాన్ని నిర్వహించడానికి <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>ని ఉపయోగిస్తుంది."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"మీ నిర్వాహకులు సెట్టింగ్‌లు, కార్పొరేట్ ప్రాప్యత, అనువర్తనాలు, మీ పరికరం అనుబంధిత డేటా మరియు స్థాన సమాచారం పర్యవేక్షించగలరు మరియు నిర్వహించగలరు."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"సెట్టింగ్‌లు, కార్పొరేట్ ప్రాప్యత, అనువర్తనాలు, మీ పరికరంతో అనుబంధించబడిన డేటా మరియు మీ పరికరం యొక్క స్థాన సమాచారాన్ని మీ నిర్వాహకులు పర్యవేక్షించగలరు మరియు నిర్వహించగలరు."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"మరింత తెలుసుకోండి"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN సెట్టింగ్‌లను తెరవండి"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"మీ నిర్వాహకులు మీ పరికరంలో ట్రాఫిక్‌ను పర్యవేక్షించే నెట్‌వర్క్ లాగింగ్‌ను ఆన్ చేసారు.\n\nమరింత సమాచారం కోసం మీ నిర్వాహకులను సంప్రదించండి."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేసారు.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"మీరు VPN కనెక్షన్ సెటప్ చేయడానికి ఒక అనువర్తనానికి అనుమతి ఇచ్చారు.\n\nఈ అనువర్తనం ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ పరికరం మరియు నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"మీ కార్యాలయ ప్రొఫైల్‌ను <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహిస్తోంది.\n\nమీ నిర్వాహకుడు ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలరు.\n\nమరింత సమాచారం కోసం, మీ నిర్వాహకుడిని సంప్రదించండి.\n\nమీరు VPNకి కూడా కనెక్ట్ చేయబడ్డారు, ఇది మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> ద్వారా మీ కార్యాలయ ప్రొఫైల్ నిర్వహించబడుతోంది.\n\nఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల సామర్థ్యం మీ నిర్వాహకులకు ఉంది.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి.\n\nమీరు VPNకి కూడా కనెక్ట్ అయ్యారు, ఇది మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌‍సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"మీరు <xliff:g id="APPLICATION">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"మీ కార్యాలయ ప్రొఫైల్‌ను <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహిస్తోంది. అలాగే, మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="APPLICATION">%2$s</xliff:g>కి కనెక్ట్ చేయబడింది, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు.\n\nమరింత సమాచారం కోసం, మీ నిర్వాహకుడిని సంప్రదించండి."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> ద్వారా మీ కార్యాలయ ప్రొఫైల్ నిర్వహించబడుతోంది. ఇది <xliff:g id="APPLICATION">%2$s</xliff:g>కు కనెక్ట్ చేయబడింది, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను నిర్వహించగలదు.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"మీ కార్యాలయ ప్రొఫైల్‌ను <xliff:g id="ORGANIZATION">%1$s</xliff:g> నిర్వహిస్తోంది. అలాగే, మీ కార్యాలయ ప్రొఫైల్ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>కి కనెక్ట్ చేయబడింది, ఇది ఇమెయిల్‌లు, అనువర్తనాలు మరియు వెబ్‌సైట్‌లతో సహా మీ కార్యాలయ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు.\n\nమీరు <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>కి కూడా కనెక్ట్ చేయబడ్డారు, ఇది మీ వ్యక్తిగత నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"మీరు మాన్యువల్‌గా అన్‌లాక్ చేస్తే మినహా పరికరం లాక్ చేయబడి ఉంటుంది"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"నోటిఫికేషన్‌లను వేగంగా పొందండి"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"విస్తరింపజేయండి"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"కుదించండి"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"స్క్రీన్ పిన్ చేయబడింది"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"దీని వలన మీరు అన్‌పిన్ చేసే వరకు ఇది వీక్షణలో ఉంచబడుతుంది. అన్‌పిన్ చేయడానికి వెనుకకు మరియు స్థూలదృష్టి తాకి &amp; అలాగే పట్టుకోండి."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"దీని వలన మీరు అన్‌పిన్ చేసే వరకు ఇది వీక్షణలో ఉంచబడుతుంది. అన్‌పిన్ చేయడానికి స్థూలదృష్టిని తాకి &amp; అలాగే పట్టుకోండి."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"అర్థమైంది"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"వద్దు, ధన్యవాదాలు"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>ని దాచాలా?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ఆన్‌లో ఉన్నాయి"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ఆఫ్‌లో ఉన్నాయి"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"పవర్ నోటిఫికేషన్ నియంత్రణలతో, మీరు అనువర్తన నోటిఫికేషన్‌ల కోసం ప్రాముఖ్యత స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ జాబితా పైభాగంలో చూపబడతాయి \n- పూర్తి స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్ మరియు స్థితి పట్టీ నుండి దాచబడతాయి \n- నోటిఫికేషన్ జాబితా దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- అనువర్తనం నుండి అన్ని నోటిఫికేషన్‌లు బ్లాక్ చేయబడతాయి"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"నోటిఫికేషన్‌లు"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"మీరు ఇకపై ఈ నోటిఫికేషన్‌లను పొందరు."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"దీని కోసం <xliff:g id="APP">%s</xliff:g> నోటిఫికేషన్‌లు"</string>
+    <string name="min_importance" msgid="7559703098688382595">"తక్కువ"</string>
+    <string name="low_importance" msgid="6891335321576225228">"మధ్యస్థం"</string>
+    <string name="default_importance" msgid="6400766013567512061">"అధికం"</string>
+    <string name="high_importance" msgid="730741630855788381">"అత్యవసరం"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"శబ్ద లేదా దృశ్య అంతరాయం కలిగించవద్దు"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"నిశ్శబ్దంగా చూపు"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"శబ్దం చేయి"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"శబ్దం చేసి, స్క్రీన్‌పై చూపు"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"మరిన్ని సెట్టింగ్‌లు"</string>
     <string name="notification_done" msgid="5279426047273930175">"పూర్తయింది"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> నోటిఫికేషన్ నియంత్రణలు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 258ccf6..a051e3a 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ล็อกหน้าจอ"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"การตั้งค่า"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"ภาพรวม"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"หน้าจอล็อกของโปรไฟล์งาน"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"ปิด"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"ปิด Wi-Fi แล้ว"</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"คำเตือน <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"โหมดการทำงาน"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"แสงตอนกลางคืน"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ถูกปิดใช้งาน"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"เปิดใช้งาน NFC แล้ว"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"ไม่มีรายการล่าสุด"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"คุณได้ล้างทุกอย่างแล้ว"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ข้อมูลแอปพลิเคชัน"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"แยกในแนวนอน"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"แยกในแนวตั้ง"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"แยกแบบกำหนดเอง"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"ปิด"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"เปิด"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"แยกหน้าจอไปด้านบน"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"แยกหน้าจอไปทางซ้าย"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"แยกหน้าจอไปทางขวา"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ชาร์จแล้ว"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"ยกเลิกการเชื่อมต่อ VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"อุปกรณ์ของคุณได้รับการจัดการโดย <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ใช้ <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> เพื่อจัดการอุปกรณ์"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"ผู้ดูแลระบบของคุณสามารถตรวจสอบและจัดการการตั้งค่า การเข้าถึงของบริษัท แอป และข้อมูลที่เชื่อมโยงกับอุปกรณ์ และข้อมูลตำแหน่งของอุปกรณ์ด้วย"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"ผู้ดูแลระบบสามารถตรวจสอบและจัดการการตั้งค่า การเข้าถึงของบริษัท แอป ข้อมูลที่เชื่อมโยงกับอุปกรณ์ของคุณ และข้อมูลตำแหน่งของอุปกรณ์"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"เรียนรู้เพิ่มเติม"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"คุณเชื่อมต่อกับ <xliff:g id="VPN_APP">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์ได้"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"เปิดการตั้งค่า VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"ผู้ดูแลระบบของคุณเปิดการทำบันทึกเครือข่าย ซึ่งจะติดตามดูการรับส่งข้อมูลบนอุปกรณ์ \n\nหากต้องการข้อมูลเพิ่มเติม ให้ติดต่อผู้ดูแลระบบของคุณ"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"ผู้ดูแลระบบได้เปิดการทำบันทึกเครือข่าย ซึ่งจะติดตามดูการรับส่งข้อมูลบนอุปกรณ์ของคุณ\n\nโปรดติดต่อผู้ดูแลระบบสำหรับข้อมูลเพิ่มเติม"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"คุณได้ให้สิทธิ์แอปในการตั้งค่าการเชื่อมต่อ VPN\n\nแอปนี้จะสามารถตรวจสอบอุปกรณ์และกิจกรรมในเครือข่าย รวมถึงอีเมล แอป และเว็บไซต์ได้"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"โปรไฟล์งานได้รับการจัดการโดย <xliff:g id="ORGANIZATION">%1$s</xliff:g>\n\nผู้ดูแลระบบของคุณสามารถตรวจสอบกิจกรรมในเครือข่ายรวมถึงอีเมล แอป และเว็บไซต์ได้\n\nสำหรับข้อมูลเพิ่มเติม โปรดติดต่อผู้ดูแลระบบ\n\nนอกจากนี้คุณยังมีการเชื่อมต่อ VPN ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"โปรไฟล์งานของคุณได้รับการจัดการโดย <xliff:g id="ORGANIZATION">%1$s</xliff:g>\n\nผู้ดูแลระบบสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ ซึ่งรวมถึงอีเมล แอป และเว็บไซต์ต่างๆ\n\nโปรดติดต่อผู้ดูแลระบบสำหรับข้อมูลเพิ่มเติม\n\nนอกจากนี้คุณยังเชื่อมต่อกับ VPN ซึ่งตรวจสอบกิจกรรมในเครือข่ายของคุณได้"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"คุณเชื่อมต่อกับ <xliff:g id="APPLICATION">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์ได้"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"คุณเชื่อมต่อกับ <xliff:g id="APPLICATION">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายส่วนตัวของคุณ รวมถึงอีเมล แอป และเว็บไซต์ได้"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"คุณเชื่อมต่อกับ <xliff:g id="APPLICATION">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายส่วนตัวของคุณ รวมถึงอีเมล แอป และเว็บไซต์ได้"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"โปรไฟล์งานได้รับการจัดการโดย <xliff:g id="ORGANIZATION">%1$s</xliff:g> โดยมีการเชื่อมต่อกับ <xliff:g id="APPLICATION">%2$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่าย รวมถึงอีเมล แอป และเว็บไซต์ได้\n\nสำหรับข้อมูลเพิ่มเติม โปรดติดต่อผู้ดูแลระบบ"</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"โปรไฟล์งานของคุณได้รับการจัดการโดย <xliff:g id="ORGANIZATION">%1$s</xliff:g> โปรไฟล์ดังกล่าวเชื่อมโยงกับ <xliff:g id="APPLICATION">%2$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายงานของคุณ ซึ่งรวมถึงอีเมล แอป และเว็บไซต์ต่างๆ\n\nโปรดติดต่อผู้ดูแลระบบสำหรับข้อมูลเพิ่มเติม"</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"โปรไฟล์งานได้รับการจัดการโดย <xliff:g id="ORGANIZATION">%1$s</xliff:g> โดยมีการเชื่อมต่อกับ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่าย รวมถึงอีเมล แอป และเว็บไซต์ได้\n\nนอกจากนี้ คุณยังเชื่อมต่อกับ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายส่วนตัวได้"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"อุปกรณ์จะล็อกจนกว่าคุณจะปลดล็อกด้วยตนเอง"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"รับการแจ้งเตือนเร็วขึ้น"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index c156a8e..7857811 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Lock screen."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Mga Setting"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Overview"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Lock screen sa trabaho"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Isara"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Na-off ang wifi."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Babala sa <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Work mode"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Night Light"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Walang mga kamakailang item"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Na-clear mo ang lahat"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Custom"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"I-dismiss"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Bukas"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"I-split ang screen pataas"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"I-split ang screen pakaliwa"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"I-split ang screen pakanan"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nasingil na"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Idiskonekta ang VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Pinamamahalaan ng <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ang iyong device."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"Ginagamit ng <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ang <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> upang pamahalaan ang iyong device."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Masusubaybayan at mapapamahalaan ng admin mo ang mga setting, pangkorporasyong access, app, data sa device at impormasyon ng lokasyon ng device mo."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Matuto pa"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Kumonekta ka sa <xliff:g id="VPN_APP">%1$s</xliff:g>, na maaaring sumubaybay sa iyong aktibidad sa network, kasama ang mga email, app at website."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Buksan ang Mga Setting ng VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Na-on ng iyong admin ang pag-log sa network, na sumusubaybay sa trapiko sa device mo.\n\nPara sa higit pang impormasyon, makipag-ugnayan sa iyong admin."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Nagbigay ka ng pahintulot sa app upang mag-set up ng VPN na koneksyon.\n\nMaaaring subaybayan ng app na ito ang iyong aktibidad sa device at network, kabilang ang mga email, app at website."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Pinapamahalaan ang iyong profile ng <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nMay kakayahan ang iyong administrator sa pagsubaybay ng iyong aktibidad sa network kabilang ang mga email, app at website.\n\nPara sa higit pang impormasyon, makipag-ugnayan sa iyong administrator.\n\nNakakonekta ka rin sa isang VPN, na maaaring sumubaybay sa iyong aktibidad sa network."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Nakakonekta ka sa <xliff:g id="APPLICATION">%1$s</xliff:g>, na maaaring sumubaybay sa iyong aktibidad sa network kabilang ang mga email, app at website."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Nakakonekta ka sa <xliff:g id="APPLICATION">%1$s</xliff:g>, na maaaring sumubaybay sa iyong personal na aktibidad sa network, kabilang ang mga email, app at website."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Nakakonekta ka sa <xliff:g id="APPLICATION">%1$s</xliff:g>, na maaaring sumubaybay sa aktibidad sa iyong personal na network, kabilang ang mga email, app at website."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Ang iyong profile sa trabaho ay pinapamahalaan ng <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Nakakonekta ito sa <xliff:g id="APPLICATION">%2$s</xliff:g>, na maaaring sumubaybay sa iyong aktibidad sa network, kabilang ang mga email, app at website.\n\nPara sa higit pang impormasyon, makipag-ugnayan sa iyong administrator."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Ang iyong profile sa trabaho ay pinapamahalaan ng <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Nakakonekta ito sa <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, na maaaring sumubaybay sa iyong aktibidad sa network, kabilang ang mga email, app at website.\n\nNakakonekta ka rin sa <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, na maaaring sumubaybay sa iyong personal na aktibidad sa network."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Mananatiling naka-lock ang device hanggang sa manu-mano mong i-unlock"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Kunin ang notification nang mas mabilis"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index e5dddbe..d4ef51d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Kilit ekranı"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Ayarlar"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Genel Bakış."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"İş profili kilit ekranı"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Kapat"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Kablosuz kapatıldı."</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> uyarısı"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Çalışma modu"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Gece Işığı"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Yeni öğe yok"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Her şeyi sildiniz"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Uygulama Bilgileri"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Yatay Ayırma"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dikey Ayırma"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Özel Ayırma"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Kapat"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Aç"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ekranı yukarıya doğru böl"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ekranı sola doğru böl"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ekranı sağa doğru böl"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ödeme alındı"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN bağlantısını kes"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Cihazınız <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> tarafından yönetiliyor."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, cihazınızı yönetmek için <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> kullanıyor."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Yöneticiniz ayarları, kurumsal erişimi, uygulamaları, cihazınızla ilişkilendirilen verileri ve cihazınızın konum bilgilerini takip edip yönetebilir."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Daha fazla bilgi"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"E-postalarınız, uygulamalarınız ve web siteleriniz de dahil olmak üzere ağ etkinliğinizi takip edebilen <xliff:g id="VPN_APP">%1$s</xliff:g> ağına bağlısınız."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN Ayarlarını aç"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Yöneticiniz,cihazınızdaki trafiği izleyen ağ günlük kaydını açtı.\n\nDaha fazla bilgi için yöneticinizle iletişim kurun."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN bağlantısı kurması için bir uygulamaya izin verdiniz.\n\nBu uygulama, cihazınızın yanı sıra e-postalarınız, uygulamalarınız ve web siteleriniz dahil olmak üzere ağ etkinliğinizi izleyebilir."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Cihazınız <xliff:g id="ORGANIZATION">%1$s</xliff:g> tarafından yönetiliyor.\n\nYöneticiniz; e-postalarınız, uygulamalarınız ve web siteleriniz dahil olmak üzere ağ etkinliğinizi izleyebilir.\n\nDaha fazla bilgi edinmek için yöneticinizle iletişim kurun.\n\nAyrıca ağ etkinliğinizi izleyebilen bir VPN\'ye bağlısınız."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"E-postalarınız, uygulamalarınız ve web siteleriniz dahil olmak üzere ağ etkinliğinizi izleyebilen <xliff:g id="APPLICATION">%1$s</xliff:g> uygulamasına bağlısınız."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"E-postalarınız, uygulamalarınız ve web siteleriniz dahil olmak üzere kişisel ağ etkinliğinizi izleyebilen <xliff:g id="APPLICATION">%1$s</xliff:g> uygulamasına bağlısınız."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"E-postalarınız, uygulamalarınız ve web siteleriniz dahil olmak üzere kişisel ağ etkinliğinizi izleyebilen <xliff:g id="APPLICATION">%1$s</xliff:g> uygulamasına bağlısınız."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"İş profiliniz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tarafından yönetiliyor. E-postalarınız, uygulamalarınız ve web siteleriniz dahil olmak üzere ağ etkinliğinizi izleyebilen <xliff:g id="APPLICATION">%2$s</xliff:g> uygulamasına bağlı.\n\nDaha fazla bilgi için yöneticinizle iletişim kurun."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"İş profiliniz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tarafından yönetiliyor. E-postalarınız, uygulamalarınız ve web siteleriniz dahil olmak üzere ağ etkinliğinizi izleyebilen <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> uygulamasına bağlı.\n\n Ayrıca kişisel ağ etkinliğinizi izleyebilen <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> uygulamasına bağlısınız."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Cihazınızın kilidini manuel olarak açmadıkça cihaz kilitli kalacak"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Bildirimleri daha hızlı alın"</string>
@@ -440,10 +454,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Genişlet"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Daralt"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekran sabitlendi"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Bu işlem, siz sabitlemeyi kaldırana kadar ekranı görünür durumda tutar. Sabitlemeyi kaldırmak için Geri\'ye ve Genel Bakış\'a dokunup basılı tutun."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Bu işlem, siz sabitlemeyi kaldırana kadar ekranı görünür durumda tutar. Sabitlemeyi kaldırmak için Genel bakış\'a dokunup basılı tutun."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Anladım"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Hayır, teşekkürler"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlensin mi?"</string>
@@ -510,28 +522,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Açık"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Kapalı"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Güç bildirim kontrolleriyle, bir uygulamanın bildirimleri için 0 ile 5 arasında bir önem düzeyi ayarlayabilirsiniz. \n\n"<b>"5. Düzey"</b>" \n- Bildirim listesinin en üstünde gösterilsin \n- Tam ekran kesintisine izin verilsin \n- Ekranda her zaman kısaca belirsin \n\n"<b>"4. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda her zaman kısaca belirsin \n\n"<b>"3. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman kısaca belirmesin \n\n"<b>"2. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman belirmesin \n- Hiçbir zaman ses çıkarmasın ve titreştirmesin \n\n"<b>"1. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman kısaca belirmesin \n- Hiçbir zaman ses çıkarmasın veya titreştirmesin \n- Kilit ekranından ve durum çubuğundan gizlensin \n- Bildirim listesinin en altında gösterilsin \n\n"<b>"0. Düzey"</b>" \n- Uygulamadan gelen tüm bildirimler engellensin"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirimler"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Bu bildirimleri artık almayacaksınız."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Şunun için <xliff:g id="APP">%s</xliff:g> bildirimleri:"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Düşük"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Orta"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Yüksek"</string>
+    <string name="high_importance" msgid="730741630855788381">"Acil"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Ses veya görsel kesme yok"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Sessiz bir şekilde göster"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Ses çıkar"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Ses çıkar ve ekranda göster"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Diğer ayarlar"</string>
     <string name="notification_done" msgid="5279426047273930175">"Bitti"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildirim denetimleri"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 50887c6..11f88c5 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -189,8 +189,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Заблокований екран."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Налаштування"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Огляд."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Екран блокування завдання"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Закрити"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi вимкнено."</string>
@@ -328,6 +327,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Застереження: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Робочий режим"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Нічний режим"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"Немає нещодавніх завдань"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Ви очистили все"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Інформація про додаток"</string>
@@ -341,6 +346,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Розділити горизонтально"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Розділити вертикально"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Розділити (власний варіант)"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Закрити"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Відкрити"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Розділити екран угорі"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Розділити екран ліворуч"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Розділити екран праворуч"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заряджено"</string>
@@ -421,20 +431,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Від’єднатися від мережі VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Вашим пристроєм керує додаток <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"Компанія <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> керує вашим пристроєм за допомогою додатка <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Адміністратор може відстежувати й контролювати налаштування, корпоративний доступ, додатки, дані на пристрої та дані про місцезнаходження пристрою."</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Докладніше"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Під’єднано додаток <xliff:g id="VPN_APP">%1$s</xliff:g>, який може відстежувати вашу активність у мережі, як-от доступ до електронної пошти, додатків і веб-сайтів."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Відкрити налаштування мережі VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Ваш адміністратор увімкнув реєстрацію в мережі, під час якої на вашому пристрої відстежується трафік.\n\nЩоб дізнатися більше, зв’яжіться зі своїм адміністратором."</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Ви дозволили додатку під’єднуватися до мережі VPN.\n\nЦей додаток може відстежувати вашу активність на пристрої та в мережі, зокрема в електронній пошті, додатках і на веб-сайтах."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Вашим пристроєм керує організація <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nАдміністратор може відстежувати вашу активність у мережі, зокрема в електронній пошті, додатках і на веб-сайтах.\n\nЗв’яжіться з адміністратором, щоб дізнатися більше.\n\nПристрій під’єднано до мережі VPN, у якій ваша активність може відстежуватись."</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Ваш профіль під’єднано до додатка <xliff:g id="APPLICATION">%1$s</xliff:g>, який може відстежувати вашу активність у мережі, зокрема в електронній пошті, додатках і на веб-сайтах."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Ваш профіль під’єднано до додатка <xliff:g id="APPLICATION">%1$s</xliff:g>, який може відстежувати вашу особисту активність у мережі, зокрема в електронній пошті, додатках і на веб-сайтах."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Ваш профіль під’єднано до додатка <xliff:g id="APPLICATION">%1$s</xliff:g>, який може відстежувати вашу особисту активність у мережі, зокрема доступ до електронної пошти, додатків і веб-сайтів."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Вашим робочим профілем керує <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Профіль під’єднано до додатка <xliff:g id="APPLICATION">%2$s</xliff:g>, який може відстежувати вашу робочу активність у мережі, зокрема в електронній пошті, додатках і на веб-сайтах.\n\nЗв’яжіться з адміністратором, щоб дізнатися більше."</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Вашим робочим профілем керує <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Профіль під’єднано до додатка <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, який може відстежувати вашу робочу активність у мережі, зокрема в електронній пошті, додатках і на веб-сайтах.\n\nВаш профіль також під’єднано до додатка <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, який може відстежувати вашу особисту активність у мережі."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Пристрій залишатиметься заблокованим, доки ви не розблокуєте його вручну"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Швидше отримуйте сповіщення"</string>
@@ -446,10 +460,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Розгорнути"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Згорнути"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Екран закріплено"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Ви постійно бачитимете екран, доки не відкріпите його. Щоб відкріпити екран, натисніть і втримуйте кнопки \"Назад\" та \"Огляд\"."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ви постійно бачитимете екран, доки не відкріпите його. Щоб відкріпити екран, натисніть і втримуйте кнопку \"Огляд\"."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Зрозуміло"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Ні, дякую"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Сховати <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -516,28 +528,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Увімк."</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Вимк."</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"За допомогою елементів керування сповіщеннями ви можете налаштувати пріоритет сповіщень додатка – від 0 до 5 рівня. \n\n"<b>"Рівень 5"</b>\n"- Показувати сповіщення вгорі списку \n- Виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 4"</b>\n"- Не виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 3"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n\n"<b>"Рівень 2"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n\n"<b>"Рівень 1"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n- Не показувати на заблокованому екрані та в рядку стану \n- Показувати сповіщення внизу списку \n\n"<b>"Рівень 0"</b>\n"- Блокувати всі сповіщення з додатка"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Сповіщення"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Ви більше не отримуватимете ці сповіщення."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Сповіщення з додатка <xliff:g id="APP">%s</xliff:g>:"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Низький"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Середній"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Високий"</string>
+    <string name="high_importance" msgid="730741630855788381">"Терміново"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Без звуку та візуальних сповіщень"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Показувати без звукового сигналу"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Зі звуком"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Зі звуком і спливаючими вікнами"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Більше налаштувань"</string>
     <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Елементи керування сповіщеннями додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index f9ca3b2..f67ce8e 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"مقفل اسکرین۔"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ترتیبات"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"مجموعی جائزہ۔"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"دفتری مقفل اسکرین"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"بند کریں"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>۔"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"‏Wifi کو آف کر دیا گیا۔"</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> وارننگ"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"کام موڈ"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"نائٹ لائٹ"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"‏NFC غیر فعال ہے"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"‏NFC فعال ہے"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"کوئی حالیہ آئٹم نہیں"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"آپ نے سب کچھ صاف کر دیا ہے"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ایپلیکیشن کی معلومات"</string>
@@ -335,6 +337,16 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"بلحاظ افقی الگ کریں"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"بلحاظ عمودی الگ کریں"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"بلحاظ حسب ضرورت الگ کریں"</string>
+    <!-- no translation found for recents_accessibility_dismissed (2354459747918667050) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_open (1651449827614876864) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_top (9056056469282256287) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_left (8987144699630620019) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_right (275069779299592867) -->
+    <skip />
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"چارج ہوگئی"</string>
@@ -415,20 +427,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"‏VPN کو غیر منسلک کریں"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"آپ کا آلہ <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> کے زیر انتظام ہے۔"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> آپ کے آلہ کے نظم کیلئے <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> استعمال کرتا ہے۔"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"آپ کا منتظم ترتیبات، کارپوریٹ رسائی، ایپس، آپ کے آلہ سے وابستہ ڈیٹا اور آپ کے آلہ کے مقام کی معلومات کو مانیٹر اور ان کا نظم کر سکتا ہے۔"</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"آپ کا ایڈمن ترتیبات، کارپوریٹ رسائی، ایپس، آپ کے آلہ سے وابستہ ڈیٹا اور آپ کے آلہ کے مقام کی معلومات کو مانیٹر اور ان کا نظم کر سکتا ہے۔"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"مزید جانیں"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"آپ <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہیں جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"‏VPN کی ترتیبات کھولیں"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"آپ کے ایڈمن نے نیٹ ورک لاگنگ آن کر دی ہے، جو آپ کے آلہ پر ٹریفک کو مانیٹر کرتی ہے۔\n\nمزید معلومات کیلئے اپنے ایڈمن سے رابطہ کریں۔"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"آپ کے ایڈمن نے نیٹ ورک لاگنگ آن کر دی ہے، جو آپ کے آلہ پر ٹریفک کو مانیٹر کرتی ہے۔\n\nمزید معلومات کیلئے اپنے ایڈمن سے رابطہ کریں۔"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"‏آپ نے ایک ایپ کو VPN کنکشن ترتیب دینے کی اجازت دی ہے۔\n\nیہ ایپ ای میلز، ایپس اور ویب سائٹس سمیت آپ کے آلہ اور نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"‏آپ کا دفتری پروفائل <xliff:g id="ORGANIZATION">%1$s</xliff:g> کے زیر انتظام ہے۔\n\nآپ کا منتظم ای میلز، ایپس اور ویب سائٹس سیمت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتا ہے۔\n\nمزید معلومات کیلئے اپنے منتظم سے رابطہ کریں۔\n\nآپ ایک VPN سے بھی منسلک ہیں، جو آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتا ہے۔"</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"‏آپ کی دفتری پروفائل <xliff:g id="ORGANIZATION">%1$s</xliff:g> کے زیر نظم ہے۔\n\nآپ کا ایڈمن بشمول ای میلز، ایپس، اور ویب سائٹس، آپ کے نیٹ ورک کی سرگرمی کو مانیٹر کرنے کا اہل ہے۔\n\nمزید معلومات کے لیے اپنے ایڈمن سے رابطہ کریں۔\n\nآپ ایک VPN سے بھی منسلک ہیں، جو آپ کے نیٹ ورک کی سرگرمی کو مانیٹر کر سکتا ہے۔"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"آپ <xliff:g id="APPLICATION">%1$s</xliff:g> سے منسلک ہیں، جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"آپ <xliff:g id="APPLICATION">%1$s</xliff:g> سے منسلک ہیں، جو آپ کے نجی نیٹ ورک کی سرگرمی سمیت ای میلز، ایپس اور ویب سائٹس مانیٹر کر سکتی ہے۔"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"آپ <xliff:g id="APPLICATION">%1$s</xliff:g> سے منسلک ہیں، جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نجی نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"آپ کا دفتری پروفائل <xliff:g id="ORGANIZATION">%1$s</xliff:g> کے زیر انتظام ہے۔ یہ <xliff:g id="APPLICATION">%2$s</xliff:g> سے منسلک ہے، جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔\n\nمزید معلومات کیلئے اپنے منتظم سے رابطہ کریں۔"</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"آپ کی دفتری پروفائل <xliff:g id="ORGANIZATION">%1$s</xliff:g> کے زیر نظم ہے۔ یہ <xliff:g id="APPLICATION">%2$s</xliff:g> سے منسلک ہے، جو بشمول ای میلز، ایپس اور ویب سائٹس، آپ کے دفتری نیٹ ورک کی سرگرمی کو مانیٹر کر سکتی ہے۔ \n\nمزید معلومات کے لیے، اپنے ایڈمن سے رابطہ کریں۔"</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"آپ کا دفتری پروفائل <xliff:g id="ORGANIZATION">%1$s</xliff:g> کے زیر انتظام ہے۔ یہ <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> سے منسلک ہے، جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔\n\nآپ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> سے بھی منسلک ہیں، جو آپ کے نجی نیٹ ورک کی سرگرمی کو مانیٹر کر سکتی ہے۔"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"آلہ اس وقت تک مقفل رہے گا جب تک آپ دستی طور پر اسے غیر مقفل نہ کریں"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"تیزی سے اطلاعات حاصل کریں"</string>
@@ -440,10 +452,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"پھیلائیں"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"سکیڑیں"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"اسکرین پن کردہ ہے"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"یہ اسے اس وقت تک نظر میں رکھتا ہے جب تک آپ اس سے پن ہٹا نہیں دیتے۔ پن ہٹانے کیلئے پیچھے اور مجموعی جائزہ بٹنز کو ٹچ کریں اور دبائے رکھیں۔"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"یہ اسے اس وقت تک نظر میں رکھتا ہے جب تک آپ اس سے پن ہٹا نہیں دیتے۔ پن ہٹانے کیلئے مجموعی جائزہ بٹن کو ٹچ کریں اور دبائے رکھیں۔"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"سمجھ آ گئی"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"نہیں شکریہ"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> کو چھپائیں؟"</string>
@@ -510,28 +520,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"آن"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"آف"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"پاور اطلاع کنٹرولز کے ساتھ آپ کسی ایپ کی اطلاعات کیلئے 0 سے 5 تک اہمیت کی سطح سیٹ کر سکتے ہیں۔ \n\n"<b>"سطح 5"</b>\n"- اطلاعات کی فہرست کے اوپر دکھائیں \n- پوری اسکرین کی مداخلت کی اجازت دیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 4"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 3"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n\n"<b>"سطح 2"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n- کبھی آواز اور ارتعاش پیدا نہ کرنا \n\n"<b>" سطح 1"</b>\n"- پوری اسکرین کی مداخلت کو روکنا \n- کبھی نہ جھانکنا \n- کبھی بھی آواز یا ارتعاش پیدا نہ کرنا\n- مقفل اسکرین اور اسٹیٹس بار سے چھپانا \n - اطلاع کی فہرست کی نیچے دکھانا \n\n"<b>"سطح 0"</b>\n"- ایپ سے تمام اطلاعات مسدود کریں"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"اطلاعات"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"آپ کو یہ اطلاعات مزید نہیں ملیں گی۔"</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> کیلئے اطلاعات"</string>
+    <string name="min_importance" msgid="7559703098688382595">"کم"</string>
+    <string name="low_importance" msgid="6891335321576225228">"متوسط"</string>
+    <string name="default_importance" msgid="6400766013567512061">"زیادہ"</string>
+    <string name="high_importance" msgid="730741630855788381">"ارجنٹ"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"آواز یا بصری مداخلت نہیں ہے"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"خاموشی سے دکھائیں"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"آواز نکالیں"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"آواز نکالیں اور اسکرین پر پاپ کریں"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"مزید ترتیبات"</string>
     <string name="notification_done" msgid="5279426047273930175">"ہوگیا"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے نوٹیفکیشن کنٹرولز"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 413cd7a3..cf4435f 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Qulflash ekrani."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Sozlamalar"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Umumiy nazar."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Ishchi ekran qulfi"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Yopish"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi o‘chirildi."</string>
@@ -324,6 +323,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Ogohlantirish: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Ish rejimi"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Tungi rejim"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC o‘chiq"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC yoniq"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Hozircha hech narsa yo‘q"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Hammasi o‘chirildi"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ilova haqida ma’lumot"</string>
@@ -337,6 +339,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gorizontal yo‘nalishda bo‘lish"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikal yo‘nalishda bo‘lish"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Boshqa usulda bo‘lish"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Yopish"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Ochish"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Ekranni tepaga qadash"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ekranni chap tomonga qadash"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ekranni o‘ng tomonga qadash"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Batareya quvvati to‘ldi"</string>
@@ -417,20 +424,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN ulanishini uzish"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Qurilmangiz <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> tomonidan boshqariladi."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> qurilmangizni boshqarish uchun <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ilovasidan foydalanadi."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Administratoringiz qurilmangiz bilan bog‘liq sozlamalar, korporativ kirish huquqi, ilova va ma’lumotlarni hamda qurilmangizning joylashuv axborotini kuzatishi va boshqarishi mumkin."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Administratoringiz qurilmangiz bilan bog‘liq sozlamalar, korporativ kirish huquqi, ilova va ma’lumotlarni hamda qurilmangizning joylashuv axborotini kuzatishi va boshqarishi mumkin."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Batafsil"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g> ilovasi ishga tushirilgan. U internetdagi harakatlaringiz, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"VPN sozlamalarini ochish"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Administrator qurilmangizdagi trafikni nazorat qiluvchi tarmoq jurnalini yoqdi.\n\nBatafsil ma’lumot olish uchun administratoringizga murojaat qiling."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administrator qurilmangizdagi trafikni nazorat qiluvchi tarmoq jurnalini yoqdi.\n\nBatafsil ma’lumot olish uchun administratoringizga murojaat qiling."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Siz ilovaga VPN tarmog‘iga ulanishga ruxsat bergansiz.\n\nUshbu ilova qurilmangiz va internetdagi harakatlaringizni, jumladan, e-pochta, ilovalar va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Sizning ishchi profilingiz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tomonidan boshqariladi.\n\nAdministrator internetdagi harakatlaringizni, jumladan, e-pochta, ilova va xavfsiz veb-saytlar bilan ishlashingizni kuzatishi mumkin.\n\nBatafsil ma’lumot olish uchun administrator bilan bog‘laning.\n\nShuningdek, siz VPN tarmog‘iga ham ulangansiz. U internetdagi harakatlaringizni kuzatishi mumkin."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Sizning ishchi profilingiz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tomonidan boshqariladi.\n\nAdministrator internetdagi harakatlaringizni, jumladan, e-pochta, ilova va xavfsiz veb-saytlar bilan ishlashingizni kuzatishi mumkin.\n\nBatafsil ma’lumot olish uchun administrator bilan bog‘laning.\n\nShuningdek, siz VPN tarmog‘iga ham ulangansiz. U internetdagi harakatlaringizni kuzatishi mumkin."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasi ishga tushirilgan. U internetdagi harakatlaringiz, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasi ishga tushirilgan. U internetdagi harakatlaringiz, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasi ishga tushirilgan. U internetdagi harakatlaringiz, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Sizning ishchi profilingiz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tomonidan boshqariladi. <xliff:g id="APPLICATION">%2$s</xliff:g> ilovasi ish tarmog‘idagi harakatlaringizni, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin.\n\nBatafsil ma’lumot olish uchun administrator bilan bog‘laning."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Sizning ishchi profilingiz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tomonidan boshqariladi. <xliff:g id="APPLICATION">%2$s</xliff:g> ilovasi ish tarmog‘idagi harakatlaringizni, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin.\n\nBatafsil ma’lumot olish uchun administrator bilan bog‘laning."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Sizning ishchi profilingiz <xliff:g id="ORGANIZATION">%1$s</xliff:g> tomonidan boshqariladi. <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> ilovasi ish tarmog‘idagi harakatlaringizni, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin.\n\nShuningdek, <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> ilovasi ham shaxsiy tarmoqdagi harakatlaringizni kuzatishi mumkin."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Qurilma qo‘lda qulfdan chiqarilmaguncha qulflangan holatda qoladi"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Bildirishnomalarni tezroq oling"</string>
@@ -442,10 +449,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Yoyish"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Yig‘ish"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Ekran qadaldi"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Ekran yechilmaguncha u o‘zgarmas holatda qoladi. Uni yechish uchun “Orqaga” va “Umumiy ma’lumot” tugmalarini bosib turing."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Ekran yechilmaguncha u o‘zgarmas holatda qoladi. Uni yechish uchun “Umumiy ma’lumot” tugmasini bosib turing."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Yo‘q, kerakmas"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> berkitilsinmi?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Yoniq"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"O‘chiq"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Bildirishnomalar uchun kengaytirilgan boshqaruv yordamida ilova bildirishnomalarining muhimlik darajasini (0-5) sozlash mumkin. \n\n"<b>"5-daraja"</b>" \n- Bildirishnomani ro‘yxatning boshida ko‘rsatish \n- To‘liq ekranli bildirishnomalarni ko‘rsatish \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"4-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"3-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n\n"<b>"2-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n\n"<b>"1-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n- Ekran qulfi va holat qatorida ko‘rsatmaslik \n- Bildirishnomani ro‘yxatning oxirida ko‘rsatish \n\n"<b>"0-daraja"</b>" \n- Ilovadan keladigan barcha bildirishnomalarni bloklash"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirishnomalar"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Ushbu bildirishnomalar endi ko‘rsatilmaydi."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> bildirishnomalari"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Muhim emas"</string>
+    <string name="low_importance" msgid="6891335321576225228">"O‘rtacha"</string>
+    <string name="default_importance" msgid="6400766013567512061">"O‘ta muhim"</string>
+    <string name="high_importance" msgid="730741630855788381">"Shoshilinch"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Bildirishnomalarsiz"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Ovozsiz"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Ovozli"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Ovoz va qalqib chiquvchi oyna"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Boshqa sozlamalar"</string>
     <string name="notification_done" msgid="5279426047273930175">"Tayyor"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildirishnomalarini boshqarish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index dde279b..d101ec1 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Màn hình khóa."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Cài đặt"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Tổng quan."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Màn hình khóa công việc"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Đóng"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Đã tắt Wifi."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Cảnh báo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Chế độ làm việc"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Đèn đọc sách"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC đã được tắt"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC đã được bật"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Không có mục gần đây nào"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Bạn đã xóa mọi nội dung"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Thông tin ứng dụng"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Phân tách ngang"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Phân tách dọc"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tùy chỉnh phân tách"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Loại bỏ"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Mở"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Chia đôi màn hình lên trên"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Chia đôi màn hình sang trái"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Chia đôi màn hình sang phải"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Đã sạc đầy"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Ngắt kết nối VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Thiết bị của bạn do <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> quản lý."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> sử dụng <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> để quản lý thiết bị của bạn."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Quản trị viên có thể giám sát &amp; q.lý cài đặt, quyền truy cập d.liệu công ty, ứng dụng, d.liệu được liên kết với thiết bị &amp; thông tin vị trí thiết bị của bạn."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Quản trị viên của bạn có thể giám sát và quản lý cài đặt, quyền truy cập dữ liệu công ty, ứng dụng, dữ liệu được liên kết với thiết bị và thông tin vị trí thiết bị của bạn."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Tìm hiểu thêm"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Bạn đang kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>. Ứng dụng này có thể giám sát hoạt động mạng của bạn, bao gồm email, ứng dụng và trang web."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Mở cài đặt VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Quản trị viên đã bật tính năng ghi nhật ký mạng. Tính năng này giám sát lưu lượng truy cập trên thiết bị của bạn.\n\nĐể biết thêm thông tin, hãy liên hệ với quản trị viên."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Quản trị viên đã bật tính năng ghi nhật ký mạng. Tính năng này giám sát lưu lượng truy cập trên thiết bị của bạn.\n\nĐể biết thêm thông tin, hãy liên hệ với quản trị viên của bạn."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Bạn đã cấp cho ứng dụng quyền thiết lập kết nối VPN.\n\nỨng dụng này có thể giám sát hoạt động mạng và thiết bị của bạn, bao gồm email, ứng dụng và trang web."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Hồ sơ Android Work của bạn được quản lý bởi <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nQuản trị viên có thể giám sát hoạt động mạng của bạn bao gồm email, ứng dụng và trang web.\n\nĐể biết thêm thông tin, hãy liên hệ với quản trị viên của bạn.\n\nBạn cũng được kết nối với VPN, có thể giám sát hoạt động mạng của bạn."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Hồ sơ công việc của bạn do <xliff:g id="ORGANIZATION">%1$s</xliff:g> quản lý.\n\nQuản trị viên có thể giám sát hoạt động mạng của bạn bao gồm email, ứng dụng và trang web.\n\nĐể biết thêm thông tin, hãy liên hệ với quản trị viên của bạn.\n\nBạn cũng được kết nối với VPN. Dịch vụ này có thể giám sát hoạt động mạng của bạn."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Bạn đang kết nối với <xliff:g id="APPLICATION">%1$s</xliff:g>. Ứng dụng này có thể giám sát hoạt động mạng của bạn bao gồm email, ứng dụng và trang web."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Bạn đang kết nối với <xliff:g id="APPLICATION">%1$s</xliff:g>. Ứng dụng này có thể giám sát hoạt động mạng cá nhân của bạn bao gồm email, ứng dụng và trang web."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Bạn đang kết nối với <xliff:g id="APPLICATION">%1$s</xliff:g>. Ứng dụng này có thể giám sát hoạt động mạng cá nhân của bạn bao gồm email, ứng dụng và trang web."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Hồ sơ công việc của bạn được quản lý bởi <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Hồ sơ được kết nối với <xliff:g id="APPLICATION">%2$s</xliff:g>, ứng dụng này có thể giám sát hoạt động mạng cơ quan của bạn, bao gồm email, ứng dụng và trang web.\n\nĐể biết thêm thông tin, hãy liên hệ với quản trị viên của bạn."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Hồ sơ công việc của bạn do <xliff:g id="ORGANIZATION">%1$s</xliff:g> quản lý. Hồ sơ này được kết nối với <xliff:g id="APPLICATION">%2$s</xliff:g>, ứng dụng này có thể giám sát hoạt động mạng của bạn, bao gồm email, ứng dụng và trang web.\n\nĐể biết thêm thông tin, hãy liên hệ với quản trị viên của bạn."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Hồ sơ công việc của bạn được quản lý bởi <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Hồ sơ được kết nối với <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, ứng dụng này có thể giám sát hoạt động mạng cơ quan của bạn, bao gồm email, ứng dụng và trang web.\n\nBạn cũng được kết nối với <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, có thể giám sát hoạt động mạng cá nhân của bạn."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Thiết bị sẽ vẫn bị khóa cho tới khi bạn mở khóa theo cách thủ công"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Nhận thông báo nhanh hơn"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Mở rộng"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Thu gọn"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Màn hình được ghim"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Thao tác này sẽ duy trì hiển thị màn hình cho đến khi bạn bỏ ghim. Hãy chạm và giữ Quay lại và Tổng quan để bỏ ghim."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Thao tác này sẽ duy trì hiển thị màn hình cho đến khi bạn bỏ ghim. Hãy chạm và giữ Tổng quan để bỏ ghim."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Ok"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Không, cảm ơn"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ẩn <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -512,28 +517,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Bật"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Tắt"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Với các kiểm soát thông báo nguồn, bạn có thể đặt cấp độ quan trọng từ 0 đến 5 cho các thông báo của ứng dụng. \n\n"<b>"Cấp 5"</b>" \n- Hiển thị ở đầu danh sách thông báo \n- Cho phép gián đoạn ở chế độ toàn màn hình \n- Luôn xem nhanh \n\n"<b>"Cấp 4"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Luôn xem nhanh \n\n"<b>"Cấp 3"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n\n"<b>"Cấp 2"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n- Không bao giờ có âm báo và rung \n\n"<b>"Cấp 1"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n- Không bao giờ có âm báo và rung \n- Ẩn khỏi màn hình khóa và thanh trạng thái \n- Hiển thị ở cuối danh sách thông báo \n\n"<b>"Cấp 0"</b>" \n- Chặn tất cả các thông báo từ ứng dụng"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Thông báo"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Bạn sẽ không nhận được những thông báo này nữa."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"Thông báo của <xliff:g id="APP">%s</xliff:g> cho"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Thấp"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Trung bình"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Cao"</string>
+    <string name="high_importance" msgid="730741630855788381">"Khẩn cấp"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Không làm gián đoạn bằng âm báo hoặc hình ảnh"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Hiển thị mà không phát âm báo"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Phát âm báo"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Phát âm báo và hiển thị trên màn hình"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Cài đặt khác"</string>
     <string name="notification_done" msgid="5279426047273930175">"Xong"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"Điều khiển thông báo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 4f5518b..f22fc3d 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"锁定屏幕。"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"设置"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"概览。"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"工作锁定屏幕"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"关闭"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"WLAN已关闭。"</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g>警告"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"工作模式"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"夜间模式"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"近期没有任何内容"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"您已清除所有内容"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"应用信息"</string>
@@ -335,6 +340,16 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自定义分割"</string>
+    <!-- no translation found for recents_accessibility_dismissed (2354459747918667050) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_open (1651449827614876864) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_top (9056056469282256287) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_left (8987144699630620019) -->
+    <skip />
+    <!-- no translation found for recents_accessibility_split_screen_right (275069779299592867) -->
+    <skip />
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"已充满"</string>
@@ -415,20 +430,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"断开VPN连接"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"您的设备由<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>管理。"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>会使用<xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>管理您的设备。"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"您的管理员能够监控和管理与您的设备相关的设置、企业权限、应用、数据以及您设备的位置信息。"</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"了解详情"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"您已连接到<xliff:g id="VPN_APP">%1$s</xliff:g>,该应用可以监控您的网络活动,包括收发电子邮件、使用应用和浏览网站。"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"打开 VPN 设置"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"您的管理员已开启网络日志功能,该功能会监控您设备上的流量。\n\n要了解详情,请与您的管理员联系。"</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"您已授权应用设置 VPN 连接。\n\n该应用可以监控您的设备和网络活动,包括收发电子邮件、使用应用和浏览网站。"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"您的工作资料由以下单位管理:<xliff:g id="ORGANIZATION">%1$s</xliff:g>。\n\n您单位的管理员可以监控您的网络活动,包括收发电子邮件、使用应用和浏览网站。\n\n若要了解详情,请与您单位的管理员联系。\n\n此外,您还连接到了 VPN,此 VPN 也可以监控您的网络活动。"</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"您已连接到<xliff:g id="APPLICATION">%1$s</xliff:g>,该应用可以监控您的网络活动,包括收发电子邮件、使用应用和浏览网站。"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"您已连接到<xliff:g id="APPLICATION">%1$s</xliff:g>,该应用可以监控您的个人网络活动,包括收发电子邮件、使用应用和浏览网站。"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"您已连接到<xliff:g id="APPLICATION">%1$s</xliff:g>,该应用可以监控您的个人网络活动,包括收发电子邮件、使用应用和浏览网站。"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"您的工作资料由以下单位管理:<xliff:g id="ORGANIZATION">%1$s</xliff:g>。您已连接到<xliff:g id="APPLICATION">%2$s</xliff:g>,该应用可以监控您的工作网络活动,包括收发电子邮件、使用应用和浏览网站。\n\n若要了解详情,请与您单位的管理员联系。"</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"您的工作资料由以下单位管理:<xliff:g id="ORGANIZATION">%1$s</xliff:g>。您已连接到<xliff:g id="APPLICATION_WORK">%2$s</xliff:g>,该应用可以监控您的工作网络活动,包括收发电子邮件、使用应用和浏览网站。\n\n此外,您还连接到了<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>,该应用可以监控您的个人网络活动。"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"在您手动解锁之前,设备会保持锁定状态"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"更快捷地查看通知"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 947848e..0392078 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -187,8 +187,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"上鎖畫面。"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"設定"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"概覽"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"工作螢幕鎖定"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"關閉"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"WiFi 已關閉。"</string>
@@ -324,6 +323,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"工作模式"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"夜燈模式"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"沒有最近項目"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"您已清除所有項目"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資料"</string>
@@ -337,6 +342,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"關閉"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"開啟"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"將分割畫面顯示喺頂部"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"將分割畫面顯示喺左邊"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"將分割畫面顯示喺右邊"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"已完成充電"</string>
@@ -417,20 +427,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"中斷 VPN 連線"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"您的裝置由「<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>」管理。"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>使用「<xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>」管理您的裝置。"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"您的管理員可以監控及管理您裝置的設定、企業存取、應用程式、資料及位置資訊。"</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"瞭解詳情"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"您已連接至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,此應用程式可以監控您的網絡活動,包括電郵、應用程式及網站。"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"開啟 VPN 設定"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"您的管理員已開啟網絡記錄功能,以監控您裝置上的流量。\n\n如需瞭解詳情,請聯絡您的管理員。"</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"您已授權應用程式設定 VPN 連線。\n\n這個應用程式能夠監控您的裝置和網絡活動,包括電郵、應用程式和網站。"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"您的裝置由 <xliff:g id="ORGANIZATION">%1$s</xliff:g> 管理。\n\n您的管理員可以監控您的網絡活動,包括電郵、應用程式及網站。\n\n如需瞭解更多資訊,請聯絡管理員。\n\n由於您的裝置連至 VPN,因此 VPN 服務供應商也可監控您的網絡活動。"</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"您已連結至<xliff:g id="APPLICATION">%1$s</xliff:g> ,它能夠監控您的網絡活動,包括電郵、應用程式和網站。"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"您已連結至<xliff:g id="APPLICATION">%1$s</xliff:g>,它能夠監控您的個人網絡活動,包括電郵、應用程式和網站。"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"您已連接至「<xliff:g id="APPLICATION">%1$s</xliff:g>」,此應用程式可以監控您的個人網絡活動,包括電郵、應用程式及網站。"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"您的工作設定檔由<xliff:g id="ORGANIZATION">%1$s</xliff:g>管理。它已連結至<xliff:g id="APPLICATION">%2$s</xliff:g>,能夠監控您的工作網絡活動,包括電郵、應用程式和網站。\n\n如需進一步資訊,請聯絡您的管理員。"</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"您的工作設定檔由<xliff:g id="ORGANIZATION">%1$s</xliff:g>管理。它已連結至<xliff:g id="APPLICATION_WORK">%2$s</xliff:g>,能夠監控您的工作網絡活動,包括電郵、應用程式和網站。\n\n此外,您亦連結至<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>,因此它亦能夠監控您的個人網絡活動。"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"裝置將保持上鎖,直到您手動解鎖"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"更快取得通知"</string>
@@ -442,8 +456,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"展開"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"收合"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"螢幕已固定"</string>
-    <string name="screen_pinning_description" msgid="8909878447196419623">"這會讓目前的螢幕畫面保持顯示狀態,直到取消固定為止。按住 [返回] 按鈕和 [總覽] 按鈕即可取消固定。"</string>
-    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"這會讓目前的螢幕畫面保持顯示狀態,直到取消固定為止。按住 [總覽] 按鈕即可取消固定。"</string>
+    <string name="screen_pinning_description" msgid="8909878447196419623">"畫面將會繼續顯示,直至您取消固定。按住 [返回] 和 [概覽] 即可取消固定。"</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"畫面將會繼續顯示,直至您取消固定。按住 [概覽] 即可取消固定。"</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"知道了"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"不用了,謝謝"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"隱藏 <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -511,7 +525,7 @@
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"關閉"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"通知控制項讓您設定應用程式通知的重要性 (0 至 5 級)。\n\n"<b>"第 5 級"</b>" \n- 在通知清單頂部顯示 \n- 允許全螢幕騷擾 \n- 一律顯示通知 \n\n"<b>"第 4 級"</b>" \n- 阻止全螢幕騷擾 \n- 一律顯示通知 \n\n"<b>"第 3 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n\n"<b>"第 2 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n- 永不發出聲響和震動 \n\n"<b>"第 1 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n- 永不發出聲響和震動 \n- 從上鎖畫面和狀態列中隱藏 \n- 在通知清單底部顯示 \n\n"<b>"第 0 級"</b>" \n- 封鎖所有應用程式通知"</string>
     <string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string>
-    <string name="notification_channel_disabled" msgid="5805874247999578073">"你不會再收到這類通知。"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"您不會再收到這些通知。"</string>
     <string name="notification_importance_header_app" msgid="3572576545406258751">"以下頻道的「<xliff:g id="APP">%s</xliff:g>」通知:"</string>
     <string name="min_importance" msgid="7559703098688382595">"低"</string>
     <string name="low_importance" msgid="6891335321576225228">"中"</string>
@@ -520,7 +534,7 @@
     <string name="notification_importance_min" msgid="3237794091374404537">"不發出音效或顯示通知"</string>
     <string name="notification_importance_low" msgid="8929105501798019743">"顯示通知但不發出音效"</string>
     <string name="notification_importance_default" msgid="9025125660733917469">"發出音效"</string>
-    <string name="notification_importance_high" msgid="3316555356062640222">"發出音效並在畫面上彈出通知"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"發出音效並在螢幕上彈出通知"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string>
     <string name="notification_done" msgid="5279426047273930175">"完成"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」通知控制項"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index b0aa942..1d7159a 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"螢幕鎖定。"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"設定"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"總覽。"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Work 螢幕鎖定"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"關閉"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>。"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"WiFi 已關閉。"</string>
@@ -322,6 +321,12 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> 警告"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"工作模式"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"夜燈"</string>
+    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
+    <skip />
+    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
+    <skip />
     <string name="recents_empty_message" msgid="808480104164008572">"最近沒有任何項目"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"您已清除所有工作"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資訊"</string>
@@ -335,6 +340,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"關閉"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"開啟"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"將分割畫面顯示在頂端"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"將分割畫面顯示在左邊"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"將分割畫面顯示在右邊"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"已充飽"</string>
@@ -415,20 +425,24 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"中斷 VPN 連線"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"你的裝置是由「<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>」所管理。"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> 使用「<xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>」管理你的裝置。"</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"你的管理員可以監控及管理與你的裝置相關聯的設定、公司系統權限、應用程式和資料,以及裝置的位置資訊。"</string>
+    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
+    <skip />
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"瞭解詳情"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"由於你已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,你的網路活動 (包括收發電子郵件、使用應用程式及瀏覽網站) 可能會受到這個應用程式監控。"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"開啟 VPN 設定"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"你的管理員已啟用網路紀錄功能,可監控你裝置上的流量。\n\n如需詳細資訊,請與你的管理員聯絡。"</string>
+    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
+    <skip />
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"您已授權一個應用程式設定 VPN 連線。\n\n這個應用程式可以監控您的裝置和網路活動,包括收發電子郵件、使用應用程式和瀏覽網站。"</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"您的 Work 設定檔由下列機構管理:<xliff:g id="ORGANIZATION">%1$s</xliff:g>。\n\n您的管理員可以監控您的網路活動,包括收發電子郵件、使用應用程式及瀏覽網站。\n\n如需詳細資訊,請洽您的管理員。\n\n同時,由於您的裝置已連線至 VPN,您的網路活動也會受到 VPN 監控。"</string>
+    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
+    <skip />
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"由於您已連線至 <xliff:g id="APPLICATION">%1$s</xliff:g>,您的網路活動也會受到這個應用程式監控,包括收發電子郵件、使用應用程式和瀏覽網站。"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"由於您已連線至 <xliff:g id="APPLICATION">%1$s</xliff:g>,您的個人網路活動也會受到這個應用程式監控,包括收發電子郵件、使用應用程式和瀏覽網站。"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"由於你已連結至「<xliff:g id="APPLICATION">%1$s</xliff:g>」,你的個人網路活動 (包括收發電子郵件、使用應用程式及瀏覽網站) 可能會受到這個應用程式監控。"</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"您的 Work 設定檔是由下列機構管理:<xliff:g id="ORGANIZATION">%1$s</xliff:g>。由於設定檔已連線至 <xliff:g id="APPLICATION">%2$s</xliff:g>,您的工作網路活動也會受到這個應用程式監控,包括收發電子郵件、使用應用程式和瀏覽網站。\n\n詳情請洽您的管理員。"</string>
+    <!-- no translation found for monitoring_description_app_work (7777228449969022305) -->
+    <skip />
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"您的 Work 設定檔是由下列機構管理:<xliff:g id="ORGANIZATION">%1$s</xliff:g>。由於設定檔已連線至 <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>,您的工作網路活動也會受到這個應用程式監控,包括收發電子郵件、使用應用程式和瀏覽網站。\n\n同時由於您也連線至<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>,您的個人網路活動也會受到這個應用程式監控。"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"在您手動解鎖前,裝置將保持鎖定狀態"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"更快取得通知"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index d5f98c2..7588eee 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -185,8 +185,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Khiya isikrini."</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"Izilungiselelo"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Buka konke."</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"Ukukhiya isikrini somsebenzi"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"Vala"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"I-Wifi ivaliwe."</string>
@@ -322,6 +321,9 @@
     <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"<xliff:g id="DATA_LIMIT">%s</xliff:g> isexwayiso"</string>
     <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Imodi yomsebenzi"</string>
     <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ukukhanya kwasebusuku"</string>
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"I-NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"I-NFC ikhutshaziwe"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"I-NFC inikwe amandla"</string>
     <string name="recents_empty_message" msgid="808480104164008572">"Azikho izinto zakamuva"</string>
     <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Usule yonke into"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ulwazi lohlelo lokusebenza"</string>
@@ -335,6 +337,11 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Hlukanisa okuvundlile"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Hlukanisa okumile"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Hlukanisa kwezifiso"</string>
+    <string name="recents_accessibility_dismissed" msgid="2354459747918667050">"Cashisa"</string>
+    <string name="recents_accessibility_open" msgid="1651449827614876864">"Kuvuliwe"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Hlukanisela isikrini phezulu"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Hlukanisela isikrini ngakwesokunxele"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Hlukanisela isikrini ngakwesokudla"</string>
   <string-array name="recents_blacklist_array">
   </string-array>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kushajiwe"</string>
@@ -415,20 +422,20 @@
     <string name="disconnect_vpn" msgid="1324915059568548655">"Nqamula i-VPN"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"Idivayisi yakho iphethwe yi-<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"I-<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> isebenzisa i-<xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ukuze iphathe idivayisi yakho."</string>
-    <string name="monitoring_description_do_body" msgid="6764108354701060766">"Umlawuli wakho angaqaphela aphinde aphathe izilungiselelo, ukufinyelela kwezinkampani, izinhlelo zokusebenza, idatha ehlotshaniswa nedivayisi yakho, nolwazi lwendawo yedivayisi yakho."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"Umlawuli wakho angaqaphela aphinde aphathe izilungiselelo, ukufinyelela kwezinkampani, izinhlelo zokusebenza, idatha ehlotshaniswa nedivayisi yakho, nolwazi lwendawo yedivayisi yakho."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"Funda kabanzi"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"Uxhumeke ku-<xliff:g id="VPN_APP">%1$s</xliff:g>, engaqapha umsebenzi wenethiwekhi yakho, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="8869300202410505143">"Vula izilungiselelo ze-VPN"</string>
-    <string name="monitoring_description_network_logging" msgid="3901006351911787915">"Umlawuli wakho uvule ukungena kwenethiwekhi, okuhlola ithrafikhi kudivayisi yakho.\n\nNgolwazi olubanzi xhumana nomlawuli wakho."</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Umlawuli wakho uvule ukungena kwenethiwekhi, okuhlola ithrafikhi kudivayisi yakho.\n\nNgolwazi olubanzi xhumana nomlawuli wakho."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"Unikeze uhlelo lokusebenza imvume yokusetha ukuxhumana kwe-VPN.\n\nLolu hlelo lokusebenza lungahlola idivayisi yakho nomsebenzi wenethiwekhi, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Iphrofayela yakho yomsebenzi iphethwe yi-<xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nUmqondisi wakho unamandla wokuqaphela umsebenzi wenethiwekhi yakho ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi.\n\nUkuze uthole olunye ulwazi, xhumana nomqondisi wakho.\n\nFuthi uxhumeke ku-VPN, engaqaphela umsebenzi wenethiwekhi yakho."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Iphrofayela yakho yomsebenzi iphethwe ngu-<xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nUmlawuli wakho uyakwazi ukwengamela umsebenzi wakho wenethiwekhi kufaka phakathi ama-imeyili, izinhlelo zokusebenza, namawebhusayithi.\n\nNgolwazi olubanzi, xhumana nomlawuli wakho.\n\nFuthi uxhumekile ku-VPN, engangamela umsebenzi wakho wenethiwekhi."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"I-VPN"</string>
     <string name="monitoring_description_app" msgid="6259179342284742878">"Uxhumeke ku-<xliff:g id="APPLICATION">%1$s</xliff:g>, engahlola umsebenzi wakho wenethiwekhi ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"Uxhumeke ku-<xliff:g id="APPLICATION">%1$s</xliff:g>, engahlola umsebenzi wenethiwekhi yakho yomuntu siqu, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Uxhumeke ku-<xliff:g id="APPLICATION">%1$s</xliff:g>, engaqapha umsebenzi wakho womuntu siqu wenethiwekhi, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string>
-    <string name="monitoring_description_app_work" msgid="1754325860918060897">"Iphrofayela yakho yomsebenzi iphethwe yi-<xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ixhumeke ku-<xliff:g id="APPLICATION">%2$s</xliff:g>, engahlola umsebenzi wenethiwekhi yakho yokusebenza, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi.\n\nUkuze uthole olunye ulwazi, xhumana nomqondisi wakho."</string>
+    <string name="monitoring_description_app_work" msgid="7777228449969022305">"Iphrofayela yakho yomsebenzi iphethwe ngu-<xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ixhumeke ku-<xliff:g id="APPLICATION">%2$s</xliff:g>, engangamela umsebenzi wakho wenethiwekhi, kufaka phakathi ama-imeyili, izinhlelo zokusebenza, namawebhusayithi.\n\nNgolwazi olubanzi, xhumana nomlawuli wakho."</string>
     <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Iphrofayela yakho yomsebenzi iphethwe yi-<xliff:g id="ORGANIZATION">%1$s</xliff:g>. Ixhumeke ku-<xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, engahlola umsebenzi wenethiwekhi yakho yomsebenzi, ofaka ama-imeyili, izinhlelo zokusebenza namawebhusayithi.\n\nFuthi uxhumeke ku-<xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, engahlola umsebenzi wenethiwekhi yakho yomuntu siqu."</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Idivayisi izohlala ikhiyekile uze uyivule ngokwenza"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Thola izaziso ngokushesha"</string>
@@ -440,10 +447,8 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Nweba"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Goqa"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Isikrini siphiniwe"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"Lokhu kuyigcina ibukeka uze ususe ukuphina. Thinta uphinde ubambe okuthi Emuva Nokubuka konke ukuze ususe ukuphina."</string>
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"Lokhu kuyigcina ibukeka uze ususe ukuphina. Thinta uphinde ubambe Ukubuka konke ukuze ususe ukuphina."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Ngiyitholile"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Cha ngiyabonga"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fihla i-<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -510,28 +515,17 @@
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Vuliwe"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Valiwe"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"Ngezilawuli zesaziso zamandla, ungasetha ileveli ebalulekile kusuka ku-0 kuya ku-5 kusuka kuzaziso zohlelo lokusebenza. \n\n"<b>"Ileveli 5"</b>" \n- Ibonisa phezulu kuhlu lwesaziso \n- Vumela ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 4"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 3"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n\n"<b>"Ileveli 2"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo nokudlidliza \n\n"<b>"Ileveli 1"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo noma ukudlidliza \n- Fihla kusuka kusikrini sokukhiya nebha yesimo \n- Bonisa phansi kohlu lwesaziso \n\n"<b>"Ileveli 0"</b>" \n- Vimbela zonke izaziso kusuka kuhlelo lokusebenza"</string>
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
-    <!-- no translation found for notification_channel_disabled (5805874247999578073) -->
-    <skip />
-    <!-- no translation found for notification_importance_header_app (3572576545406258751) -->
-    <skip />
-    <!-- no translation found for min_importance (7559703098688382595) -->
-    <skip />
-    <!-- no translation found for low_importance (6891335321576225228) -->
-    <skip />
-    <!-- no translation found for default_importance (6400766013567512061) -->
-    <skip />
-    <!-- no translation found for high_importance (730741630855788381) -->
-    <skip />
-    <!-- no translation found for notification_importance_min (3237794091374404537) -->
-    <skip />
-    <!-- no translation found for notification_importance_low (8929105501798019743) -->
-    <skip />
-    <!-- no translation found for notification_importance_default (9025125660733917469) -->
-    <skip />
-    <!-- no translation found for notification_importance_high (3316555356062640222) -->
-    <skip />
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"Izaziso"</string>
+    <string name="notification_channel_disabled" msgid="5805874247999578073">"Ngeke usathola lezi zaziso."</string>
+    <string name="notification_importance_header_app" msgid="3572576545406258751">"<xliff:g id="APP">%s</xliff:g> izaziso ze-"</string>
+    <string name="min_importance" msgid="7559703098688382595">"Phansi"</string>
+    <string name="low_importance" msgid="6891335321576225228">"Okulingene"</string>
+    <string name="default_importance" msgid="6400766013567512061">"Okuphezulu"</string>
+    <string name="high_importance" msgid="730741630855788381">"Okuphuthumayo"</string>
+    <string name="notification_importance_min" msgid="3237794091374404537">"Awukho umsindo noma ukuphazamiseka okubukwayo"</string>
+    <string name="notification_importance_low" msgid="8929105501798019743">"Bonisa ngokuthulile"</string>
+    <string name="notification_importance_default" msgid="9025125660733917469">"Yenza umsindo"</string>
+    <string name="notification_importance_high" msgid="3316555356062640222">"Yenza umsindo ne-pop kusikrini"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Izilungiselelo eziningi"</string>
     <string name="notification_done" msgid="5279426047273930175">"Kwenziwe"</string>
     <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> izilawuli zasaziso"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index a229866..7fb513c 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -40,8 +40,9 @@
         <attr name="decayTime" format="integer" />
         <attr name="orientation" />
     </declare-styleable>
+    <attr name="frameColor" format="color" />
     <declare-styleable name="BatteryMeterView">
-        <attr name="frameColor" format="color" />
+        <attr name="frameColor" />
     </declare-styleable>
     <declare-styleable name="Clock">
         <attr name="amPmStyle" format="enum">
@@ -60,7 +61,7 @@
         <attr name="framePadding" format="dimension" />
         <!-- {@deprecated Use a statelist in frameColor instead.} -->
         <attr name="activeFrameColor" format="color" />
-        <attr name="frameColor" format="color" />
+        <attr name="frameColor" />
         <attr name="badgeDiameter" format="dimension" />
         <attr name="badgeMargin" format="dimension" />
     </declare-styleable>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b18b6ac..1ec611a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -75,6 +75,9 @@
     <!-- The color of the material notification background when dimmed -->
     <color name="notification_material_background_dimmed_color">#ccffffff</color>
 
+    <!-- The color of the material notification background when dark -->
+    <color name="notification_material_background_dark_color">#ff333333</color>
+
     <!-- The color of the material notification background when low priority -->
     <color name="notification_material_background_low_priority_color">#fff5f5f5</color>
 
@@ -100,6 +103,10 @@
     <color name="notification_guts_icon_tint">#8a000000</color>
     <color name="notification_guts_disabled_icon_tint">#4d000000</color>
 
+    <!-- Colors of the snooze menu reached via snooze icon behind a notification -->
+    <color name="snooze_snackbar_bg">#FF4A4A4A</color>
+	<color name="snooze_snackbar_text">#FFA6BAFF</color>
+
     <color name="assist_orb_color">#ffffff</color>
 
     <color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 50ef392..b7647cf 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -286,15 +286,22 @@
     <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
 
     <!-- Nav bar button default ordering/layout -->
-    <string name="config_navBarLayout" translatable="false">space,back;home;recent,menu_ime</string>
+    <string name="config_navBarLayout" translatable="false">left,back;home;recent,right</string>
 
     <bool name="quick_settings_show_full_alarm">false</bool>
 
     <!-- Whether to show a warning notification when the device reaches a certain temperature. -->
-    <bool name="config_showTemperatureWarning">false</bool>
+    <integer name="config_showTemperatureWarning">0</integer>
 
     <!-- Temp at which to show a warning notification if config_showTemperatureWarning is true.
          If < 0, uses the value from HardwarePropertiesManager#getDeviceTemperatures. -->
     <integer name="config_warningTemperature">-1</integer>
 
+    <!-- Accessibility actions -->
+    <item type="id" name="action_split_task_to_left" />
+    <item type="id" name="action_split_task_to_right" />
+    <item type="id" name="action_split_task_to_top" />
+    <item type="id" name="action_open" />
+    <item type="id" name="action_dimiss" />
+
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 78d211f..ddcc4ba 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -75,6 +75,9 @@
     <!-- Height of a large notification in the status bar -->
     <dimen name="notification_max_height">284dp</dimen>
 
+    <!-- Height of an ambient notification on ambient display -->
+    <dimen name="notification_ambient_height">400dp</dimen>
+
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
 
@@ -103,11 +106,20 @@
     <!-- Minimum layouted height of a notification in the statusbar-->
     <dimen name="min_notification_layout_height">48dp</dimen>
 
-    <!-- Width of the space containing the gear icon behind a notification -->
-    <dimen name="notification_gear_width">64dp</dimen>
+    <!-- Size of the space to place a notification menu item -->
+    <dimen name="notification_menu_icon_size">64dp</dimen>
 
-    <!-- The space around the gear icon displayed behind a notification  -->
-    <dimen name="notification_gear_padding">20dp</dimen>
+    <!-- The space around a notification menu item  -->
+    <dimen name="notification_menu_icon_padding">20dp</dimen>
+
+    <!-- The minimum height for the snackbar shown after the snooze option has been chosen. -->
+    <dimen name="snooze_snackbar_min_height">48dp</dimen>
+
+    <!-- The text size of options in the snooze menu. -->
+    <dimen name="snooze_option_text_size">14sp</dimen>
+
+    <!-- The padding around options int the snooze menu. -->
+    <dimen name="snooze_option_padding">8dp</dimen>
 
     <!-- size at which Notification icons will be drawn in the status bar -->
     <dimen name="status_bar_icon_drawing_size">17dp</dimen>
@@ -161,7 +173,7 @@
     <dimen name="close_handle_underlap">32dp</dimen>
 
     <!-- Height of the status bar header bar -->
-    <dimen name="status_bar_header_height">104dp</dimen>
+    <dimen name="status_bar_header_height">126dp</dimen>
 
     <!-- Height of the status bar header bar when expanded -->
     <dimen name="status_bar_header_height_expanded">116dp</dimen>
@@ -201,8 +213,8 @@
     <!-- The size of the gesture span needed to activate the "pull" notification expansion -->
     <dimen name="pull_span_min">25dp</dimen>
 
-    <dimen name="qs_tile_height">88dp</dimen>
-    <dimen name="qs_tile_margin">16dp</dimen>
+    <dimen name="qs_tile_height">80dp</dimen>
+    <dimen name="qs_tile_margin">36dp</dimen>
     <dimen name="qs_tile_margin_top">16dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
@@ -418,7 +430,7 @@
     <dimen name="qs_time_collapsed_size">14sp</dimen>
 
     <!-- The font size of the time when expanded in QS -->
-    <dimen name="qs_time_expanded_size">20sp</dimen>
+    <dimen name="qs_time_expanded_size">14sp</dimen>
 
     <!-- The font size of the "emergency calls only" label in QS -->
     <dimen name="qs_emergency_calls_only_text_size">12sp</dimen>
diff --git a/packages/SystemUI/res/values/dimens_grid.xml b/packages/SystemUI/res/values/dimens_grid.xml
index 94ffdb1..5858443 100644
--- a/packages/SystemUI/res/values/dimens_grid.xml
+++ b/packages/SystemUI/res/values/dimens_grid.xml
@@ -21,5 +21,7 @@
   <dimen name="recents_grid_padding_task_view">20dp</dimen>
   <dimen name="recents_grid_task_view_header_height">44dp</dimen>
   <dimen name="recents_grid_task_view_header_button_padding">8dp</dimen>
+  <dimen name="recents_grid_task_view_focused_frame_thickness">8dp</dimen>
+  <dimen name="recents_grid_task_view_rounded_corners_radius">8dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 4a19dde..fc0c82c 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -58,7 +58,6 @@
     <item type="id" name="transformation_start_y_tag"/>
     <item type="id" name="transformation_start_scale_x_tag"/>
     <item type="id" name="transformation_start_scale_y_tag"/>
-    <item type="id" name="custom_background_color"/>
 
     <!-- Whether the icon is from a notification for which targetSdk < L -->
     <item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 69515fa..7f4baa5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -760,6 +760,12 @@
     <string name="quick_settings_work_mode_label">Work mode</string>
     <!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] -->
     <string name="quick_settings_night_display_label">Night Light</string>
+    <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_nfc_label">NFC</string>
+    <!-- QuickSettings: NFC (off) [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_nfc_off">NFC is disabled</string>
+    <!-- QuickSettings: NFC (on) [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_nfc_on">NFC is enabled</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
     <string name="recents_empty_message">No recent items</string>
@@ -788,6 +794,16 @@
     <string name="recents_multistack_add_stack_dialog_split_vertical">Split Vertical</string>
     <!-- Recents: MultiStack add stack split custom radio button. [CHAR LIMIT=NONE] -->
     <string name="recents_multistack_add_stack_dialog_split_custom">Split Custom</string>
+    <!-- Recents: Accessibility dismiss label -->
+    <string name="recents_accessibility_dismissed">Dismiss</string>
+    <!-- Recents: Accessibility open label -->
+    <string name="recents_accessibility_open">Open</string>
+    <!-- Recents: Accessibility split to the top -->
+    <string name="recents_accessibility_split_screen_top">Split screen to the top</string>
+    <!-- Recents: Accessibility split to the left -->
+    <string name="recents_accessibility_split_screen_left">Split screen to the left</string>
+    <!-- Recents: Accessibility split to the right -->
+    <string name="recents_accessibility_split_screen_right">Split screen to the right</string>
 
     <!-- Fully qualified activity class names to be blacklisted in Recents, add package names into overlay as needed -->
     <string-array name="recents_blacklist_array">
@@ -1016,10 +1032,10 @@
     <string name="monitoring_title">Network monitoring</string>
 
     <!-- STOPSHIP Monitoring strings still need to be finalized and approved -->
-    <!-- Monitoring dialog subtitle for the section describing VPN [CHAR LIMIT=TODO]-->
+    <!-- Monitoring dialog subtitle for the section describing VPN [CHAR LIMIT=35]-->
     <string name="monitoring_subtitle_vpn">VPN</string>
 
-    <!-- Monitoring dialog subtitle for the section describing network logging [CHAR LIMIT=TODO]-->
+    <!-- Monitoring dialog subtitle for the section describing network logging [CHAR LIMIT=35]-->
     <string name="monitoring_subtitle_network_logging">Network Logging</string>
 
     <!-- Monitoring dialog disable vpn button [CHAR LIMIT=30] -->
@@ -1035,7 +1051,7 @@
     <string name="monitoring_description_do_header_with_name"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> uses <xliff:g id="device_owner_app" example="Google Mobile Management">%2$s</xliff:g> to manage your device.</string>
 
     <!-- Monitoring dialog: Part of text body explaining what a Device Owner app can do [CHAR LIMIT=130] -->
-    <string name="monitoring_description_do_body">Your administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.</string>
+    <string name="monitoring_description_do_body">Your admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.</string>
 
     <!-- Monitoring dialog: Space that separates the body text and the "learn more" link that follows it. [CHAR LIMIT=5] -->
     <string name="monitoring_description_do_learn_more_separator">" "</string>
@@ -1049,18 +1065,18 @@
     <!-- Monitoring dialog: Space that separates the VPN body text and the "Open VPN Settings" link that follows it. [CHAR LIMIT=5] -->
     <string name="monitoring_description_vpn_settings_separator">" "</string>
 
-    <!-- Monitoring dialog: Link to open the VPN settings page [CHAR LIMIT=TODO] -->
+    <!-- Monitoring dialog: Link to open the VPN settings page [CHAR LIMIT=60] -->
     <string name="monitoring_description_vpn_settings">Open VPN Settings</string>
 
-    <!-- Monitoring dialog: Network logging text [CHAR LIMIT=TODO] -->
-    <string name="monitoring_description_network_logging">Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information contact your admin.</string>
+    <!-- Monitoring dialog: Network logging text [CHAR LIMIT=400] -->
+    <string name="monitoring_description_network_logging">Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information, contact your admin.</string>
 
 
     <!-- Monitoring dialog VPN text [CHAR LIMIT=400] -->
     <string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps, and websites.</string>
 
     <!-- Monitoring dialog VPN with profile owner text [CHAR LIMIT=400] -->
-    <string name="monitoring_description_vpn_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your administrator.\n\nYou\'re also connected to a VPN, which can monitor your network activity.</string>
+    <string name="monitoring_description_vpn_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour admin is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your admin.\n\nYou\'re also connected to a VPN, which can monitor your network activity.</string>
 
     <!-- Name for a generic legacy VPN connection [CHAR LIMIT=20] -->
     <string name="legacy_vpn_name">VPN</string>
@@ -1075,7 +1091,7 @@
     <string name="branded_monitoring_description_app_personal">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps, and websites.</string>
 
     <!-- Monitoring dialog text for single app (inside work profile) [CHAR LIMIT=400] -->
-    <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
+    <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nFor more information, contact your admin.</string>
 
     <!-- Monitoring dialog text for multiple apps (in personal and work profiles) [CHAR LIMIT=400] -->
     <string name="monitoring_description_app_personal_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application_work">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nYou\'re also connected to <xliff:g id="application_personal">%3$s</xliff:g>, which can monitor your personal network activity.</string>
@@ -1359,8 +1375,26 @@
     <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
     <string name="notification_done">Done</string>
 
-    <!-- Notification: Gear: Content description for the gear. [CHAR LIMIT=NONE] -->
-    <string name="notification_gear_accessibility"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> notification controls</string>
+    <!-- Notification: Menu row: Content description for menu items. [CHAR LIMIT=NONE] -->
+    <string name="notification_menu_accessibility"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> <xliff:g id="menu_description" example="notification controls">%2$s</xliff:g></string>
+
+    <!-- Notification: Menu row: Content description for the gear menu item. [CHAR LIMIT=NONE] -->
+    <string name="notification_menu_gear_description">notification controls</string>
+
+    <!-- Notification: Menu row: Content description for the snooze icon. [CHAR LIMIT=NONE] -->
+    <string name="notification_menu_snooze_description">notification snooze options</string>
+
+    <!-- Notification: Menu row: Snooze options: 15 minute option. [CHAR LIMIT=50]-->
+    <string name="snooze_option_15_min">15 minutes</string>
+    <!-- Notification: Menu row: Snooze options: 30 minute option. [CHAR LIMIT=50]-->
+    <string name="snooze_option_30_min">30 minutes</string>
+    <!-- Notification: Menu row: Snooze options: 1 hour option. [CHAR LIMIT=50]-->
+    <string name="snooze_option_1_hour">1 hour</string>
+    <!-- Notification: Menu row: Snooze undo button label. [CHAR LIMIT=50]-->
+    <string name="snooze_undo">UNDO</string>
+
+    <!-- Notification: Menu row: Snooze: message indicating how long the notification was snoozed for. [CHAR LIMIT=100]-->
+    <string name="snoozed_for_time">Snoozed for <xliff:g id="time_amount" example="15 minutes">%1$s</xliff:g></string>
 
     <!-- Title of the battery settings detail panel [CHAR LIMIT=20] -->
     <string name="battery_panel_title">Battery usage</string>
@@ -1507,56 +1541,71 @@
     <!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] -->
     <string name="nav_bar">Navigation bar</string>
 
-    <!-- SysUI Tuner: Group of buttons that show on the start of the screen [CHAR LIMIT=30] -->
-    <string name="start">Start</string>
-    <!-- SysUI Tuner: Group of buttons that show on the center of the screen [CHAR LIMIT=30] -->
-    <string name="center">Center</string>
-    <!-- SysUI Tuner: Group of buttons that show on the end of the screen [CHAR LIMIT=30] -->
-    <string name="end">End</string>
-    <!-- SysUI Tuner: Name of space used in custom navigation bar layouts [CHAR LIMIT=30] -->
-    <string name="space">Spacer</string>
+    <!-- SysUI Tuner: Button that controls layout of navigation bar [CHAR LIMIT=60] -->
+    <string name="nav_bar_layout">Layout</string>
+
+    <!-- SysUI Tuner: Label for section of settings about the left nav button [CHAR LIMIT=60] -->
+    <string name="nav_bar_left">Left</string>
+
+    <!-- SysUI Tuner: Label for section of settings about the right nav button [CHAR LIMIT=60] -->
+    <string name="nav_bar_right">Right</string>
+
+    <!-- SysUI Tuner: Setting for button type in nav bar [CHAR LIMIT=60] -->
+    <string name="nav_bar_button_type">Button type</string>
+
+    <!-- SysUI Tuner: Added to nav bar option to indicate it is the default [CHAR LIMIT=60] -->
+    <string name="nav_bar_default"> (default)</string>
+
+    <!-- SysUI Tuner: Labels for different types of navigation bar buttons [CHAR LIMIT=60] -->
+    <string-array name="nav_bar_buttons">
+        <item>Clipboard</item>
+        <item>Keycode</item>
+        <item>Menu / Keyboard Switcher</item>
+        <item>None</item>
+    </string-array>
+    <string-array name="nav_bar_button_values" translatable="false">
+        <item>clipboard</item>
+        <item>key</item>
+        <item>menu_ime</item>
+        <item>space</item>
+    </string-array>
+
+    <!-- SysUI Tuner: Labels for different types of navigation bar layouts [CHAR LIMIT=60] -->
+    <string-array name="nav_bar_layouts">
+        <item>Divided (default)</item>
+        <item>Centered</item>
+        <item>Left-aligned</item>
+        <item>Right-aligned</item>
+    </string-array>
+
+    <string-array name="nav_bar_layouts_values" translatable="false">
+        <item>default</item>
+        <item>left;back,home,recent;right</item>
+        <item>left,back,home,recent,right;space;space</item>
+        <item>space;space;left,back,home,recent,right</item>
+    </string-array>
+
     <!-- SysUI Tuner: Name of Combination Menu / Keyboard Switcher button [CHAR LIMIT=30] -->
     <string name="menu_ime">Menu / Keyboard Switcher</string>
-    <!-- SysUI Tuner: Title for dialog to add a button [CHAR LIMIT=30] -->
-    <string name="select_button">Select button to add</string>
-    <!-- SysUI Tuner: Button to add a button [CHAR LIMIT=30] -->
-    <string name="add_button">Add button</string>
     <!-- SysUI Tuner: Save the current settings [CHAR LIMIT=30] -->
     <string name="save">Save</string>
     <!-- SysUI Tuner: Reset to default settings [CHAR LIMIT=30] -->
     <string name="reset">Reset</string>
 
-    <!-- SysUI Tuner: Title of no home warning dialog [CHAR LIMIT=30] -->
-    <string name="no_home_title">No home button found</string>
-    <!-- SysUI Tuner: Message of no home warning dialog [CHAR LIMIT=NONE] -->
-    <string name="no_home_message">A home button is required to be able to navigate this device. Please add a home button before saving.</string>
-
     <!-- SysUI Tuner: Adjust button width dialog title [CHAR LIMIT=60] -->
     <string name="adjust_button_width">Adjust button width</string>
 
     <!-- SysUI Tuner: Nav bar button that holds the clipboard [CHAR LIMIT=30] -->
     <string name="clipboard">Clipboard</string>
 
-    <!-- SysUI Tuner: Description of nav bar button that holds the clipboard [CHAR LIMIT=NONE] -->
-    <string name="clipboard_description">The Clipboard allows items to be dragged directly to the clipboard. Items can also be dragged directly out of the clipboard when present.</string>
-
     <!-- SysUI Tuner: Accessibility description for custom nav key [CHAR LIMIT=NONE] -->
     <string name="accessibility_key">Custom navigation button</string>
 
     <!-- SysUI Tuner: Nav bar button that emulates a keycode [CHAR LIMIT=30] -->
     <string name="keycode">Keycode</string>
 
-    <!-- SysUI Tuner: Description of nav bar button that emulates a keycode [CHAR LIMIT=NONE] -->
-    <string name="keycode_description">Keycode buttons allow keyboard keys to
-        be added to the Navigation Bar. When pressed they emulate the selected
-        keyboard key. First the key must be selected for the button, followed
-        by an image to be shown on the button.</string>
-
-    <!-- SysUI Tuner: Title of dialog to select which key to emulate [CHAR LIMIT=60] -->
-    <string name="select_keycode">Select Keyboard Button</string>
-
-    <!-- SysUI Tuner: Label for preview area in navigation bar tuner [CHAR LIMIT=NONE] -->
-    <string name="preview">Preview</string>
+    <!-- SysUI Tuner: Settings to change nav bar icon [CHAR LIMIT=30] -->
+    <string name="icon">Icon</string>
 
     <!-- Label for area where tiles can be dragged out of [CHAR LIMIT=60] -->
     <string name="drag_to_add_tiles">Drag to add tiles</string>
@@ -1736,5 +1785,30 @@
     <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] -->
     <string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string>
 
+    <!-- SysUI Tuner: Group of settings for left lock screen affordance [CHAR LIMIT=60] -->
+    <string name="lockscreen_left">Left</string>
+
+    <!-- SysUI Tuner: Group of settings for right lock screen affordance [CHAR LIMIT=60] -->
+    <string name="lockscreen_right">Right</string>
+
+    <!-- SysUI Tuner: Switch controlling whether to customize lock screen button [CHAR LIMIT=60] -->
+    <string name="lockscreen_customize">Customize shortcut</string>
+
+    <!-- SysUI Tuner: Button to select lock screen shortcut [CHAR LIMIT=60] -->
+    <string name="lockscreen_shortcut">Shortcut</string>
+
+    <!-- SysUI Tuner: Switch to control if device gets unlocked [CHAR LIMIT=60] -->
+    <string name="lockscreen_unlock">Prompt for password</string>
+
+    <!-- Title for the notification channel containing important alerts like low battery. [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_alerts">Alerts</string>
+    <!-- Title for the notification channel dedicated to screenshot progress. [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_screenshot">Screenshots</string>
+    <!-- Title for the notification channel for urgent security issues. [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_security">Security</string>
+    <!-- Title for the notification channel containing multi-user status information. [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_user_status">User status</string>
+    <!-- Title for the notification channel for problems with storage (i.e. low disk). [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_storage">Storage</string>
 
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a9c858c..c5a5518 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -93,37 +93,34 @@
         <item name="android:textColor">#FF808080</item>
     </style>
 
-    <style name="TextAppearance.StatusBar.TextButton"
-        parent="@*android:style/TextAppearance.StatusBar">
-        <item name="android:textAppearance">?android:attr/textAppearance</item>
-        <item name="android:textStyle">normal</item>
-        <item name="android:textColor">#FFFFFFFF</item>
-    </style>
-
     <style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
         <item name="android:textSize">@dimen/status_bar_clock_size</item>
         <item name="android:fontFamily">sans-serif-medium</item>
         <item name="android:textColor">@color/status_bar_clock_color</item>
     </style>
 
-    <style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar" />
+    <style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar">
+        <item name="android:textColor">?android:attr/textColorTertiary</item>
+    </style>
 
     <style name="TextAppearance.StatusBar.Expanded.Clock">
         <item name="android:textSize">@dimen/qs_time_expanded_size</item>
-        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:fontFamily">sans-serif-condensed</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textStyle">normal</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Expanded.Date">
         <item name="android:textSize">@dimen/qs_date_collapsed_size</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:fontFamily">sans-serif-condensed</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Expanded.AboveDateTime">
         <item name="android:textSize">@dimen/qs_emergency_calls_only_text_size</item>
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:textColor">?android:attr/textColorTertiary</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
@@ -135,7 +132,7 @@
     <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
         <item name="android:textSize">16sp</item>
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">@color/qs_user_detail_name</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
     <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" />
 
@@ -172,7 +169,7 @@
 
     <style name="TextAppearance.QS.DetailButton">
         <item name="android:textSize">@dimen/qs_detail_button_text_size</item>
-        <item name="android:textColor">@color/qs_detail_button</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textAllCaps">true</item>
         <item name="android:fontFamily">sans-serif-medium</item>
         <item name="android:gravity">center</item>
@@ -184,7 +181,7 @@
 
     <style name="TextAppearance.QS.DetailEmpty">
         <item name="android:textSize">@dimen/qs_detail_empty_text_size</item>
-        <item name="android:textColor">@color/qs_subhead</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.QS.Subhead">
@@ -386,6 +383,20 @@
         <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
 
+    <style name="TextAppearance.SnoozeSnackBar">
+        <item name="android:textSize">14sp</item>
+        <item name="android:fontFamily">roboto-regular</item>
+        <item name="android:textColor">@android:color/white</item>
+    </style>
+
+    <style name="TextAppearance.SnoozeSnackBar.Button">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textAllCaps">true</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:gravity">center</item>
+        <item name="android:textColor">@color/snooze_snackbar_text</item>
+    </style>
+
     <style name="edit_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
         <item name="android:colorBackground">?android:attr/colorSecondary</item>
     </style>
diff --git a/packages/SystemUI/res/xml/lockscreen_settings.xml b/packages/SystemUI/res/xml/lockscreen_settings.xml
new file mode 100644
index 0000000..73e29af
--- /dev/null
+++ b/packages/SystemUI/res/xml/lockscreen_settings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+    android:title="@string/other">
+
+    <PreferenceCategory
+        android:key="left"
+        android:title="@string/lockscreen_left">
+
+        <SwitchPreference
+            android:key="customize"
+            android:title="@string/lockscreen_customize" />
+
+        <Preference
+            android:key="shortcut"
+            android:title="@string/lockscreen_shortcut" />
+
+        <com.android.systemui.tuner.TunerSwitch
+            android:key="sysui_keyguard_left_unlock"
+            android:title="@string/lockscreen_unlock"
+            sysui:defValue="true" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="right"
+        android:title="@string/lockscreen_right">
+
+        <SwitchPreference
+            android:key="customize"
+            android:title="@string/lockscreen_customize" />
+
+        <Preference
+            android:key="shortcut"
+            android:title="@string/lockscreen_shortcut" />
+
+        <com.android.systemui.tuner.TunerSwitch
+            android:key="sysui_keyguard_right_unlock"
+            android:title="@string/lockscreen_unlock"
+            sysui:defValue="true" />
+
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/packages/SystemUI/res/xml/nav_bar_tuner.xml b/packages/SystemUI/res/xml/nav_bar_tuner.xml
new file mode 100644
index 0000000..6fa8bec
--- /dev/null
+++ b/packages/SystemUI/res/xml/nav_bar_tuner.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+    android:title="@string/nav_bar">
+
+    <ListPreference
+        android:key="layout"
+        android:title="@string/nav_bar_layout"
+        android:summary="%s"
+        android:persistent="false"
+        android:entries="@array/nav_bar_layouts"
+        android:entryValues="@array/nav_bar_layouts_values" />
+
+    <PreferenceCategory
+        android:key="left"
+        android:title="@string/nav_bar_left">
+
+        <DropDownPreference
+            android:key="type_left"
+            android:title="@string/nav_bar_button_type"
+            android:persistent="false"
+            android:summary="%s"
+            android:entries="@array/nav_bar_buttons"
+            android:entryValues="@array/nav_bar_button_values" />
+
+        <Preference
+            android:key="keycode_left"
+            android:persistent="false"
+            android:title="@string/keycode" />
+
+        <com.android.systemui.tuner.BetterListPreference
+            android:key="icon_left"
+            android:persistent="false"
+            android:summary="%s"
+            android:title="@string/icon" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="right"
+        android:title="@string/nav_bar_right">
+
+        <DropDownPreference
+            android:key="type_right"
+            android:title="@string/nav_bar_button_type"
+            android:summary="%s"
+            android:persistent="false"
+            android:entries="@array/nav_bar_buttons"
+            android:entryValues="@array/nav_bar_button_values" />
+
+        <Preference
+            android:key="keycode_right"
+            android:persistent="false"
+            android:title="@string/keycode" />
+
+        <com.android.systemui.tuner.BetterListPreference
+            android:key="icon_right"
+            android:persistent="false"
+            android:summary="%s"
+            android:title="@string/icon" />
+
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/packages/SystemUI/res/xml/other_settings.xml b/packages/SystemUI/res/xml/other_settings.xml
index 18cb930..7719d5e 100644
--- a/packages/SystemUI/res/xml/other_settings.xml
+++ b/packages/SystemUI/res/xml/other_settings.xml
@@ -23,10 +23,5 @@
             android:key="power_notification_controls"
             android:title="@string/tuner_full_importance_settings"
             android:fragment="com.android.systemui.tuner.PowerNotificationControlsFragment"/>
-e
-    <com.android.systemui.tuner.ThemePreference
-        android:key="theme"
-        android:title="@string/theme"
-        android:summary="%s" />
 
 </PreferenceScreen>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 59a10da..94a7c07 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -150,12 +150,15 @@
 
     </PreferenceScreen>
 
-    <!--
     <Preference
         android:key="nav_bar"
         android:title="@string/nav_bar"
         android:fragment="com.android.systemui.tuner.NavBarTuner" />
-    -->
+
+    <Preference
+            android:key="lockscreen"
+            android:title="@string/accessibility_desc_lock_screen"
+            android:fragment="com.android.systemui.tuner.LockscreenFragment" />
 
     <Preference
             android:key="other"
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarter.java b/packages/SystemUI/src/com/android/systemui/ActivityStarter.java
new file mode 100644
index 0000000..a4d8a10
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+
+/**
+ * An interface to start activities. This is used as a callback from the views to
+ * {@link PhoneStatusBar} to allow custom handling for starting the activity, i.e. dismissing the
+ * Keyguard.
+ */
+public interface ActivityStarter {
+
+    void startPendingIntentDismissingKeyguard(PendingIntent intent);
+    void startActivity(Intent intent, boolean dismissShade);
+    void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
+    void startActivity(Intent intent, boolean dismissShade, Callback callback);
+    void postStartActivityDismissingKeyguard(Intent intent, int delay);
+    void postStartActivityDismissingKeyguard(PendingIntent intent);
+    void postQSRunnableDismissingKeyguard(Runnable runnable);
+
+    interface Callback {
+        void onActivityStarted(int resultCode);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
new file mode 100644
index 0000000..4ae81a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+
+/**
+ * Single common instance of ActivityStarter that can be gotten and referenced from anywhere, but
+ * delegates to an actual implementation such as PhoneStatusBar, assuming it exists.
+ */
+public class ActivityStarterDelegate implements ActivityStarter {
+
+    private ActivityStarter mActualStarter;
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
+        if (mActualStarter == null) return;
+        mActualStarter.startPendingIntentDismissingKeyguard(intent);
+    }
+
+    @Override
+    public void startActivity(Intent intent, boolean dismissShade) {
+        if (mActualStarter == null) return;
+        mActualStarter.startActivity(intent, dismissShade);
+    }
+
+    @Override
+    public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
+        if (mActualStarter == null) return;
+        mActualStarter.startActivity(intent, onlyProvisioned, dismissShade);
+    }
+
+    @Override
+    public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
+        if (mActualStarter == null) return;
+        mActualStarter.startActivity(intent, dismissShade, callback);
+    }
+
+    @Override
+    public void postStartActivityDismissingKeyguard(Intent intent, int delay) {
+        if (mActualStarter == null) return;
+        mActualStarter.postStartActivityDismissingKeyguard(intent, delay);
+    }
+
+    @Override
+    public void postStartActivityDismissingKeyguard(PendingIntent intent) {
+        if (mActualStarter == null) return;
+        mActualStarter.postStartActivityDismissingKeyguard(intent);
+    }
+
+    @Override
+    public void postQSRunnableDismissingKeyguard(Runnable runnable) {
+        if (mActualStarter == null) return;
+        mActualStarter.postQSRunnableDismissingKeyguard(runnable);
+    }
+
+    public void setActivityStarterImpl(ActivityStarter starter) {
+        mActualStarter = starter;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 07ef5e0..5a6afca 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -292,12 +292,16 @@
         }
         int backgroundColor = getBackgroundColor(darkIntensity);
         int fillColor = getFillColor(darkIntensity);
+        setColors(fillColor, backgroundColor);
+        mOldDarkIntensity = darkIntensity;
+    }
+
+    public void setColors(int fillColor, int backgroundColor) {
         mIconTint = fillColor;
         mFramePaint.setColor(backgroundColor);
         mBoltPaint.setColor(fillColor);
         mChargeColor = fillColor;
         invalidateSelf();
-        mOldDarkIntensity = darkIntensity;
     }
 
     private int getBackgroundColor(float darkIntensity) {
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index ef1c25d..b30b596 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -72,6 +72,8 @@
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
+        mBatteryController = Dependency.get(BatteryController.class);
+        mDrawable.setBatteryController(mBatteryController);
         mBatteryController.addCallback(this);
         mDrawable.startListening();
         TunerService.get(getContext()).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
@@ -97,12 +99,11 @@
 
     }
 
-    public void setBatteryController(BatteryController mBatteryController) {
-        this.mBatteryController = mBatteryController;
-        mDrawable.setBatteryController(mBatteryController);
-    }
-
     public void setDarkIntensity(float f) {
         mDrawable.setDarkIntensity(f);
     }
+
+    public void setRawColors(int fgColor, int bgColor) {
+        mDrawable.setColors(fgColor, bgColor);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ConfigurationChangedReceiver.java b/packages/SystemUI/src/com/android/systemui/ConfigurationChangedReceiver.java
new file mode 100644
index 0000000..4fba640
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ConfigurationChangedReceiver.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.res.Configuration;
+
+public interface ConfigurationChangedReceiver {
+    void onConfigurationChanged(Configuration newConfiguration);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
new file mode 100644
index 0000000..135b129
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Class to handle ugly dependencies throughout sysui until we determine the
+ * long-term dependency injection solution.
+ *
+ * Classes added here should be things that are expected to live the lifetime of sysui,
+ * and are generally applicable to many parts of sysui. They will be lazily
+ * initialized to ensure they aren't created on form factors that don't need them
+ * (e.g. HotspotController on TV). Despite being lazily initialized, it is expected
+ * that all dependencies will be gotten during sysui startup, and not during runtime
+ * to avoid jank.
+ *
+ * All classes used here are expected to manage their own lifecycle, meaning if
+ * they have no clients they should not have any registered resources like bound
+ * services, registered receivers, etc.
+ */
+public class Dependency extends SystemUI {
+
+    /**
+     * Key for getting a background Looper for background work.
+     */
+    public static final String BG_LOOPER = "background_loooper";
+    /**
+     * Key for getting a Handler for receiving time tick broadcasts on.
+     */
+    public static final String TIME_TICK_HANDLER = "time_tick_handler";
+    /**
+     * Generic handler on the main thread.
+     */
+    public static final String MAIN_HANDLER = "main_handler";
+
+    private final ArrayMap<String, Object> mDependencies = new ArrayMap<>();
+    private final ArrayMap<String, DependencyProvider> mProviders = new ArrayMap<>();
+
+    @Override
+    public void start() {
+        sDependency = this;
+        // TODO: Think about ways to push these creation rules out of Dependency to cut down
+        // on imports.
+        mProviders.put(TIME_TICK_HANDLER, () -> {
+            HandlerThread thread = new HandlerThread("TimeTick");
+            thread.start();
+            return new Handler(thread.getLooper());
+        });
+        mProviders.put(BG_LOOPER, () -> {
+            HandlerThread thread = new HandlerThread("SysUiBg",
+                    Process.THREAD_PRIORITY_BACKGROUND);
+            thread.start();
+            return thread.getLooper();
+        });
+        mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
+        mProviders.put(ActivityStarter.class.getName(), () -> new ActivityStarterDelegate());
+        mProviders.put(ActivityStarterDelegate.class.getName(), () ->
+                getDependency(ActivityStarter.class));
+
+        mProviders.put(BluetoothController.class.getName(), () ->
+                new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));
+
+        mProviders.put(LocationController.class.getName(), () ->
+                new LocationControllerImpl(mContext, getDependency(BG_LOOPER)));
+
+        mProviders.put(RotationLockController.class.getName(), () ->
+                new RotationLockControllerImpl(mContext));
+
+        mProviders.put(NetworkController.class.getName(), () ->
+                new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),
+                        getDependency(DeviceProvisionedController.class)));
+
+        mProviders.put(ZenModeController.class.getName(), () ->
+                new ZenModeControllerImpl(mContext, getDependency(MAIN_HANDLER)));
+
+        mProviders.put(HotspotController.class.getName(), () ->
+                new HotspotControllerImpl(mContext));
+
+        mProviders.put(CastController.class.getName(), () ->
+                new CastControllerImpl(mContext));
+
+        mProviders.put(FlashlightController.class.getName(), () ->
+                new FlashlightControllerImpl(mContext));
+
+        mProviders.put(KeyguardMonitor.class.getName(), () ->
+                new KeyguardMonitorImpl(mContext));
+
+        mProviders.put(UserSwitcherController.class.getName(), () ->
+                new UserSwitcherController(mContext, getDependency(KeyguardMonitor.class),
+                        getDependency(MAIN_HANDLER), getDependency(ActivityStarter.class)));
+
+        mProviders.put(UserInfoController.class.getName(), () ->
+                new UserInfoControllerImpl(mContext));
+
+        mProviders.put(BatteryController.class.getName(), () ->
+                new BatteryControllerImpl(mContext));
+
+        mProviders.put(ManagedProfileController.class.getName(), () ->
+                new ManagedProfileControllerImpl(mContext));
+
+        mProviders.put(NextAlarmController.class.getName(), () ->
+                new NextAlarmControllerImpl(mContext));
+
+        mProviders.put(DataSaverController.class.getName(), () ->
+                get(NetworkController.class).getDataSaverController());
+
+        mProviders.put(AccessibilityController.class.getName(), () ->
+                new AccessibilityController(mContext));
+
+        mProviders.put(DeviceProvisionedController.class.getName(), () ->
+                new DeviceProvisionedControllerImpl(mContext));
+
+        mProviders.put(AssistManager.class.getName(), () ->
+                new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
+
+        mProviders.put(SecurityController.class.getName(), () ->
+                new SecurityControllerImpl(mContext));
+
+        // Put all dependencies above here so the factory can override them if it wants.
+        SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        super.dump(fd, pw, args);
+        pw.println("Dumping existing controllers:");
+        mDependencies.values().stream().filter(obj -> obj instanceof Dumpable)
+                .forEach(o -> ((Dumpable) o).dump(fd, pw, args));
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mDependencies.values().stream().filter(obj -> obj instanceof ConfigurationChangedReceiver)
+                .forEach(o -> ((ConfigurationChangedReceiver) o).onConfigurationChanged(newConfig));
+    }
+
+    protected final <T> T getDependency(Class<T> cls) {
+        return getDependency(cls.getName());
+    }
+
+    protected final <T> T getDependency(String cls) {
+        T obj = (T) mDependencies.get(cls);
+        if (obj == null) {
+            obj = createDependency(cls);
+            mDependencies.put(cls, obj);
+        }
+        return obj;
+    }
+
+    @VisibleForTesting
+    protected <T> T createDependency(String cls) {
+        DependencyProvider<T> provider = mProviders.get(cls);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unsupported dependency " + cls);
+        }
+        return provider.createDependency();
+    }
+
+    private static Dependency sDependency;
+
+    public interface DependencyProvider<T> {
+        T createDependency();
+    }
+
+    public static <T> T get(Class<T> cls) {
+        return sDependency.getDependency(cls.getName());
+    }
+
+    public static <T> T get(String cls) {
+        return sDependency.getDependency(cls);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dumpable.java b/packages/SystemUI/src/com/android/systemui/Dumpable.java
new file mode 100644
index 0000000..65a6844
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/Dumpable.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public interface Dumpable {
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index d98bb23..8141b28 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -326,7 +326,7 @@
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 if (DEBUG) Log.d(TAG, "up/cancel");
-                finishExpanding(ev.getActionMasked() == MotionEvent.ACTION_CANCEL,
+                finishExpanding(ev.getActionMasked() == MotionEvent.ACTION_CANCEL /* forceAbort */,
                         getCurrentVelocity());
                 clearView();
                 break;
@@ -587,6 +587,9 @@
             mFlingAnimationUtils.apply(mScaleAnimation, currentHeight, targetHeight, velocity);
             mScaleAnimation.start();
         } else {
+            if (targetHeight != currentHeight) {
+                mScaler.setHeight(targetHeight);
+            }
             mCallback.setUserExpandedChild(mResizedView, nowExpanded);
             mCallback.setUserLockedChild(mResizedView, false);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
index efa7cae..efddf20 100644
--- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
@@ -98,7 +98,7 @@
     }
 
     @Override
-    public void onPluginConnected(ViewProvider plugin) {
+    public void onPluginConnected(ViewProvider plugin, Context context) {
         mPluginView = plugin.getView();
         inflateLayout();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 5c50b5c..a95713f 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -32,7 +32,9 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
 import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.NotificationMenuRow;
 
 import java.util.HashMap;
 
@@ -85,10 +87,12 @@
     private int mFalsingThreshold;
     private boolean mTouchAboveFalsingThreshold;
     private boolean mDisableHwLayers;
+    private Context mContext;
 
     private HashMap<View, Animator> mDismissPendingMap = new HashMap<>();
 
     public SwipeHelper(int swipeDirection, Callback callback, Context context) {
+        mContext = context;
         mCallback = callback;
         mHandler = new Handler();
         mSwipeDirection = swipeDirection;
@@ -249,6 +253,7 @@
         }
     }
 
+    @Override
     public boolean onInterceptTouchEvent(final MotionEvent ev) {
         final int action = ev.getAction();
 
@@ -280,7 +285,8 @@
                                         mCurrView.getLocationOnScreen(mTmpPos);
                                         final int x = (int) ev.getRawX() - mTmpPos[0];
                                         final int y = (int) ev.getRawY() - mTmpPos[1];
-                                        mLongPressListener.onLongPress(mCurrView, x, y);
+                                        mLongPressListener.onLongPress(mCurrView, x, y,
+                                                NotificationMenuRow.getLongpressMenuItem(mContext));
                                     }
                                 }
                             };
@@ -379,6 +385,7 @@
             animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
         }
         AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
+            @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
             }
@@ -401,10 +408,12 @@
         anim.addListener(new AnimatorListenerAdapter() {
             private boolean mCancelled;
 
+            @Override
             public void onAnimationCancel(Animator animation) {
                 mCancelled = true;
             }
 
+            @Override
             public void onAnimationEnd(Animator animation) {
                 updateSwipeProgressFromOffset(animView, canBeDismissed);
                 mDismissPendingMap.remove(animView);
@@ -435,6 +444,7 @@
     public void snapChild(final View animView, final float targetLeft, float velocity) {
         final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
         AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
+            @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
             }
@@ -447,6 +457,7 @@
         int duration = SNAP_ANIM_LEN;
         anim.setDuration(duration);
         anim.addListener(new AnimatorListenerAdapter() {
+            @Override
             public void onAnimationEnd(Animator animator) {
                 mSnappingChild = false;
                 updateSwipeProgressFromOffset(animView, canBeDismissed);
@@ -522,6 +533,7 @@
         }
     }
 
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (mLongPressSent) {
             return true;
@@ -690,6 +702,6 @@
          * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
          * @return whether the longpress was handled
          */
-        boolean onLongPress(View v, int x, int y);
+        boolean onLongPress(View v, int x, int y, MenuItem item);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SysUiServiceProvider.java b/packages/SystemUI/src/com/android/systemui/SysUiServiceProvider.java
index c4470cd..ff4b7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUiServiceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUiServiceProvider.java
@@ -14,10 +14,16 @@
 
 package com.android.systemui;
 
+import android.content.Context;
+
 /**
  * The interface for getting core components of SysUI. Exists for Testability
  * since tests don't have SystemUIApplication as their ApplicationContext.
  */
 public interface SysUiServiceProvider {
     <T> T getComponent(Class<T> interfaceType);
+
+    public static <T> T getComponent(Context context, Class<T> interfaceType) {
+        return ((SysUiServiceProvider) context.getApplicationContext()).getComponent(interfaceType);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index bd4e3dc..afe88c1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -29,7 +29,6 @@
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.systemui.doze.DozeFactory;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyboard.KeyboardUI;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -47,6 +46,7 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.usb.StorageNotification;
+import com.android.systemui.util.NotificationChannels;
 import com.android.systemui.volume.VolumeUI;
 
 import java.util.HashMap;
@@ -64,8 +64,10 @@
      * The classes of the stuff to start.
      */
     private final Class<?>[] SERVICES = new Class[] {
+            Dependency.class,
             FragmentService.class,
             TunerService.class,
+            NotificationChannels.class,
             CommandQueue.CommandQueueStart.class,
             KeyguardViewMediator.class,
             Recents.class,
@@ -207,7 +209,7 @@
         PluginManager.getInstance(this).addPluginListener(OverlayPlugin.ACTION,
                 new PluginListener<OverlayPlugin>() {
             @Override
-            public void onPluginConnected(OverlayPlugin plugin) {
+            public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
                 PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class);
                 if (phoneStatusBar != null) {
                     plugin.setup(phoneStatusBar.getStatusBarWindow(),
@@ -239,8 +241,4 @@
     public SystemUI[] getServices() {
         return mServices;
     }
-
-    public static <T> T getComponent(Context context, Class<T> interfaceType) {
-        return ((SysUiServiceProvider) context.getApplicationContext()).getComponent(interfaceType);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 10328a4..ec11812 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -18,19 +18,23 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.Dependency.DependencyProvider;
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -113,25 +117,20 @@
         return new NotificationIconAreaController(context, phoneStatusBar);
     }
 
+    public KeyguardIndicationController createKeyguardIndicationController(Context context,
+            ViewGroup indicationArea, LockIcon lockIcon) {
+        return new KeyguardIndicationController(context, indicationArea, lockIcon);
+    }
+
     public QSTileHost createQSTileHost(Context context, PhoneStatusBar statusBar,
-            BluetoothController bluetooth, LocationController location,
-            RotationLockController rotation, NetworkController network,
-            ZenModeController zen, HotspotController hotspot,
-            CastController cast, FlashlightController flashlight,
-            UserSwitcherController userSwitcher, UserInfoController userInfo,
-            KeyguardMonitor keyguard, SecurityController security,
-            BatteryController battery, StatusBarIconController iconController,
-            NextAlarmController nextAlarmController) {
-        return new QSTileHost(context, statusBar, bluetooth, location, rotation, network, zen,
-                hotspot, cast, flashlight, userSwitcher, userInfo, keyguard, security, battery,
-                iconController, nextAlarmController);
+            StatusBarIconController iconController) {
+        return new QSTileHost(context, statusBar, iconController);
     }
 
     public <T> T createInstance(Class<T> classType) {
         return null;
     }
 
-    public AssistManager createAssistManager(BaseStatusBar bar, Context context) {
-        return new AssistManager(bar, context);
-    }
+    public void injectDependencies(ArrayMap<String, DependencyProvider> providers,
+            Context context) { }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 05e5f6b..11e0f28 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -18,7 +18,9 @@
 
 import android.app.Service;
 import android.content.Intent;
+import android.os.Build;
 import android.os.IBinder;
+import android.os.SystemProperties;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -29,6 +31,11 @@
     public void onCreate() {
         super.onCreate();
         ((SystemUIApplication) getApplication()).startServicesIfNeeded();
+
+        // For debugging RescueParty
+        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
+            throw new RuntimeException();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 2576bb7..09fdf5a 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -9,14 +9,15 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -33,14 +34,17 @@
 import com.android.internal.app.IVoiceInteractionSessionListener;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.systemui.ConfigurationChangedReceiver;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 /**
  * Class to manage everything related to assist in SystemUI.
  */
-public class AssistManager {
+public class AssistManager implements ConfigurationChangedReceiver {
 
     private static final String TAG = "AssistManager";
     private static final String ASSIST_ICON_METADATA_NAME =
@@ -52,9 +56,10 @@
     protected final Context mContext;
     private final WindowManager mWindowManager;
     private final AssistDisclosure mAssistDisclosure;
+    private final InterestingConfigChanges mInterestingConfigChanges;
 
     private AssistOrbContainer mView;
-    private final BaseStatusBar mBar;
+    private final DeviceProvisionedController mDeviceProvisionedController;
     protected final AssistUtils mAssistUtils;
 
     private IVoiceInteractionSessionShowCallback mShowCallback =
@@ -79,14 +84,16 @@
         }
     };
 
-    public AssistManager(BaseStatusBar bar, Context context) {
+    public AssistManager(DeviceProvisionedController controller, Context context) {
         mContext = context;
-        mBar = bar;
+        mDeviceProvisionedController = controller;
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mAssistUtils = new AssistUtils(context);
         mAssistDisclosure = new AssistDisclosure(context, new Handler());
 
         registerVoiceInteractionSessionListener();
+        mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION);
+        onConfigurationChanged(context.getResources().getConfiguration());
     }
 
     protected void registerVoiceInteractionSessionListener() {
@@ -104,7 +111,10 @@
         });
     }
 
-    public void onConfigurationChanged() {
+    public void onConfigurationChanged(Configuration newConfiguration) {
+        if (!mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
+            return;
+        }
         boolean visible = false;
         if (mView != null) {
             visible = mView.isShowing();
@@ -183,13 +193,13 @@
     }
 
     private void startAssistActivity(Bundle args, @NonNull ComponentName assistComponent) {
-        if (!mBar.isDeviceProvisioned()) {
+        if (!mDeviceProvisionedController.isDeviceProvisioned()) {
             return;
         }
 
         // Close Recent Apps if needed
-        mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL |
-                CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL);
+        SysUiServiceProvider.getComponent(mContext, CommandQueue.class).animateCollapsePanels(
+                CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL | CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL);
 
         boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index e081b53..00d2298 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -37,7 +37,7 @@
 
     interface Callback {
         default void onNewNotifications() {}
-        default void onBuzzBeepBlinked() {}
+        default void onNotificationHeadsUp() {}
         default void onNotificationLight(boolean on) {}
         default void onPowerSaveChanged(boolean active) {}
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 84b22ea..db5a392 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -309,7 +309,7 @@
 
     private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
         @Override
-        public void onBuzzBeepBlinked() {
+        public void onNotificationHeadsUp() {
             onNotification();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 57857cc..50506a9 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -27,11 +27,13 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcelable;
+import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import com.android.settingslib.applications.InterestingConfigChanges;
 import com.android.systemui.SystemUIApplication;
+import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginManager;
 
 import java.io.FileDescriptor;
@@ -47,6 +49,7 @@
     private final View mRootView;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges();
     private final FragmentService mManager;
+    private final PluginFragmentManager mPlugins = new PluginFragmentManager();
 
     private FragmentController mFragments;
     private FragmentLifecycleCallbacks mLifecycleCallbacks;
@@ -163,6 +166,10 @@
         return mFragments.getFragmentManager();
     }
 
+    PluginFragmentManager getPluginManager() {
+        return mPlugins;
+    }
+
     public interface FragmentListener {
         void onFragmentViewCreated(String tag, Fragment fragment);
 
@@ -198,6 +205,11 @@
         }
 
         @Override
+        public Fragment instantiate(Context context, String className, Bundle arguments) {
+            return mPlugins.instantiate(context, className, arguments);
+        }
+
+        @Override
         public boolean onShouldSaveFragmentState(Fragment fragment) {
             return true; // True for now.
         }
@@ -237,4 +249,57 @@
             return true;
         }
     }
+
+    class PluginFragmentManager {
+        private final ArrayMap<String, Context> mPluginLookup = new ArrayMap<>();
+
+        public void removePlugin(String tag, String currentClass, String defaultClass) {
+            Fragment fragment = getFragmentManager().findFragmentByTag(tag);
+            mPluginLookup.remove(currentClass);
+            getFragmentManager().beginTransaction()
+                    .replace(((View) fragment.getView().getParent()).getId(),
+                            instantiate(mContext, defaultClass, null), tag)
+                    .commit();
+            reloadFragments();
+        }
+
+        public void setCurrentPlugin(String tag, String currentClass, Context context) {
+            Fragment fragment = getFragmentManager().findFragmentByTag(tag);
+            mPluginLookup.put(currentClass, context);
+            getFragmentManager().beginTransaction()
+                    .replace(((View) fragment.getView().getParent()).getId(),
+                            instantiate(context, currentClass, null), tag)
+                    .commit();
+            reloadFragments();
+        }
+
+        private void reloadFragments() {
+            // Save the old state.
+            Parcelable p = destroyFragmentHost();
+            // Generate a new fragment host and restore its state.
+            createFragmentHost(p);
+        }
+
+        Fragment instantiate(Context context, String className, Bundle arguments) {
+            Context pluginContext = mPluginLookup.get(className);
+            if (pluginContext != null) {
+                Fragment f = Fragment.instantiate(pluginContext, className, arguments);
+                if (f instanceof Plugin) {
+                    ((Plugin) f).onCreate(mContext, pluginContext);
+                }
+                return f;
+            }
+            return Fragment.instantiate(context, className, arguments);
+        }
+    }
+
+    private static class PluginState {
+        Context mContext;
+        String mCls;
+
+        private PluginState(String cls, Context context) {
+            mCls = cls;
+            mContext = context;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java
index e107fcd..2e6de4a 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/PluginFragmentListener.java
@@ -15,6 +15,7 @@
 package com.android.systemui.fragments;
 
 import android.app.Fragment;
+import android.content.Context;
 import android.util.Log;
 import android.view.View;
 
@@ -30,27 +31,19 @@
     private final FragmentHostManager mFragmentHostManager;
     private final PluginManager mPluginManager;
     private final Class<? extends Fragment> mDefaultClass;
-    private final int mId;
-    private final String mTag;
     private final Class<? extends FragmentBase> mExpectedInterface;
+    private final String mTag;
 
-    public PluginFragmentListener(View view, String tag, int id,
-            Class<? extends Fragment> defaultFragment,
+    public PluginFragmentListener(View view, String tag, Class<? extends Fragment> defaultFragment,
             Class<? extends FragmentBase> expectedInterface) {
+        mTag = tag;
         mFragmentHostManager = FragmentHostManager.get(view);
         mPluginManager = PluginManager.getInstance(view.getContext());
         mExpectedInterface = expectedInterface;
-        mTag = tag;
         mDefaultClass = defaultFragment;
-        mId = id;
     }
 
     public void startListening(String action, int version) {
-        try {
-            setFragment(mDefaultClass.newInstance());
-        } catch (InstantiationException | IllegalAccessException e) {
-            Log.e(TAG, "Couldn't instantiate " + mDefaultClass.getName(), e);
-        }
         mPluginManager.addPluginListener(action, this, version, false /* Only allow one */);
     }
 
@@ -58,17 +51,13 @@
         mPluginManager.removePluginListener(this);
     }
 
-    private void setFragment(Fragment fragment) {
-        mFragmentHostManager.getFragmentManager().beginTransaction()
-                .replace(mId, fragment, mTag)
-                .commit();
-    }
-
     @Override
-    public void onPluginConnected(Plugin plugin) {
+    public void onPluginConnected(Plugin plugin, Context pluginContext) {
         try {
             mExpectedInterface.cast(plugin);
-            setFragment((Fragment) plugin);
+            Fragment.class.cast(plugin);
+            mFragmentHostManager.getPluginManager().setCurrentPlugin(mTag,
+                    plugin.getClass().getName(), pluginContext);
         } catch (ClassCastException e) {
             Log.e(TAG, plugin.getClass().getName() + " must be a Fragment and implement "
                     + mExpectedInterface.getName(), e);
@@ -77,10 +66,7 @@
 
     @Override
     public void onPluginDisconnected(Plugin plugin) {
-        try {
-            setFragment(mDefaultClass.newInstance());
-        } catch (InstantiationException | IllegalAccessException e) {
-            Log.e(TAG, "Couldn't instantiate " + mDefaultClass.getName(), e);
-        }
+        mFragmentHostManager.getPluginManager().removePlugin(mTag,
+                plugin.getClass().getName(), mDefaultClass.getName());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index 63d1cc2..23eaed9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard;
 
 import static android.app.ActivityManager.TaskDescription;
+import static android.app.ActivityManager.StackId;
 
 import android.annotation.ColorInt;
 import android.annotation.UserIdInt;
@@ -31,6 +32,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -91,10 +93,6 @@
         // Draw captions overlaid on the content view, so the whole window is one solid color.
         setOverlayWithDecorCaptionEnabled(true);
 
-        // Match task description to the task stack we are replacing so it's still recognizably the
-        // original task stack with the same icon and title text.
-        setTaskDescription(new TaskDescription(null, null, color));
-
         // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with
         // redaction switched on.
         final View blankView = new View(this);
@@ -127,6 +125,11 @@
         return;
     }
 
+    @Override
+    public void setTaskDescription(TaskDescription taskDescription) {
+        // Use the previous activity's task description.
+    }
+
     private final BroadcastReceiver mLockEventReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -159,9 +162,23 @@
 
         credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
         try {
-            ActivityManager.getService().startConfirmDeviceCredentialIntent(credential);
+            ActivityManager.getService().startConfirmDeviceCredentialIntent(credential,
+                    getChallengeOptions().toBundle());
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to start confirm credential intent", e);
         }
     }
+
+    private ActivityOptions getChallengeOptions() {
+        // If we are taking up the whole screen, just use the default animation of clipping the
+        // credentials activity into the entire foreground.
+        if (!isInMultiWindowMode()) {
+            return ActivityOptions.makeBasic();
+        }
+
+        // Otherwise, animate the transition from this part of the screen to fullscreen
+        // using our own decor as the starting position.
+        final View view = getWindow().getDecorView();
+        return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index 2c41a08..e6483f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -42,9 +42,8 @@
         Intent intent = new Intent(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)
                 .setComponent(new ComponentName(mContext, WorkLockActivity.class))
                 .putExtra(Intent.EXTRA_USER_ID, userId)
-                .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                        | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
-                        | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+                .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
 
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchTaskId(taskId);
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 728d558..9788903 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -22,6 +22,7 @@
 import android.media.MediaPlayer;
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
+import android.media.PlayerBase;
 import android.net.Uri;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -81,6 +82,12 @@
                     (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
                 try {
                     MediaPlayer player = new MediaPlayer();
+                    if (mCmd.attributes == null) {
+                        mCmd.attributes = new AudioAttributes.Builder()
+                                .setUsage(AudioAttributes.USAGE_NOTIFICATION)
+                                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                                .build();
+                    }
                     player.setAudioAttributes(mCmd.attributes);
                     player.setDataSource(mCmd.context, mCmd.uri);
                     player.setLooping(mCmd.looping);
@@ -92,13 +99,11 @@
                                 if (mAudioManagerWithAudioFocus == null) {
                                     if (mDebug) Log.d(mTag, "requesting AudioFocus");
                                     if (mCmd.looping) {
-                                        audioManager.requestAudioFocus(null,
-                                                AudioAttributes.toLegacyStreamType(mCmd.attributes),
-                                                AudioManager.AUDIOFOCUS_GAIN);
+                                        audioManager.requestAudioFocus(null, mCmd.attributes,
+                                                AudioManager.AUDIOFOCUS_GAIN, 0);
                                     } else {
-                                        audioManager.requestAudioFocus(null,
-                                                AudioAttributes.toLegacyStreamType(mCmd.attributes),
-                                                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
+                                        audioManager.requestAudioFocus(null, mCmd.attributes,
+                                                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
                                     }
                                     mAudioManagerWithAudioFocus = audioManager;
                                 } else {
@@ -296,6 +301,7 @@
      */
     @Deprecated
     public void play(Context context, Uri uri, boolean looping, int stream) {
+        PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play");
         Command cmd = new Command();
         cmd.requestTime = SystemClock.uptimeMillis();
         cmd.code = PLAY;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index a2d7d6b..3df557d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -16,14 +16,20 @@
 
 package com.android.systemui.pip.phone;
 
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityOptions;
 import android.app.IActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.IPinnedStackController;
 import android.view.IPinnedStackListener;
@@ -49,6 +55,7 @@
     private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
 
     private PipMenuActivityController mMenuController;
+    private PipMediaController mMediaController;
     private PipTouchHandler mTouchHandler;
 
     /**
@@ -57,7 +64,11 @@
     TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onActivityPinned() {
+            if (!checkCurrentUserId(false /* debug */)) {
+                return;
+            }
             mTouchHandler.onActivityPinned();
+            mMediaController.onActivityPinned();
         }
 
         @Override
@@ -66,8 +77,27 @@
         }
 
         @Override
-        public void onPinnedActivityRestartAttempt() {
-            // TODO(winsonc): Hide the menu and expand the PiP
+        public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) {
+            if (!checkCurrentUserId(false /* debug */)) {
+                return;
+            }
+
+            // Expand the activity back to fullscreen only if it was attempted to be restarted from
+            // another package than the top activity in the stack
+            boolean expandPipToFullscreen = true;
+            if (sourceComponent != null) {
+                ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext,
+                        mActivityManager);
+                if (topActivity != null && topActivity.getPackageName().equals(
+                        sourceComponent.getPackageName())) {
+                    expandPipToFullscreen = false;
+                }
+            }
+            if (expandPipToFullscreen) {
+                mTouchHandler.expandPinnedStackToFullscreen();
+            } else {
+                Log.w(TAG, "Can not expand PiP to fullscreen via intent from the same package.");
+            }
         }
     };
 
@@ -91,7 +121,7 @@
         @Override
         public void onActionsChanged(ParceledListSlice actions) {
             mHandler.post(() -> {
-                mMenuController.setActions(actions);
+                mMenuController.setAppActions(actions);
             });
         }
 
@@ -127,7 +157,9 @@
         }
         SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
 
-        mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager);
+        mMediaController = new PipMediaController(context, mActivityManager);
+        mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager,
+                mMediaController);
         mTouchHandler = new PipTouchHandler(context, mMenuController, mActivityManager,
                 mWindowManager);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
new file mode 100644
index 0000000..d96baa6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip.phone;
+
+import android.app.IActivityManager;
+import android.app.RemoteAction;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaController;
+import android.media.session.MediaController.TransportControls;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Interfaces with the {@link MediaSessionManager} to compose the right set of actions to show (only
+ * if there are no actions from the PiP activity itself). The active media controller is only set
+ * when there is a media session from the top PiP activity.
+ */
+public class PipMediaController {
+
+    /**
+     * A listener interface to receive notification on changes to the media actions.
+     */
+    public interface ActionListener {
+        /**
+         * Called when the media actions changes.
+         */
+        void onMediaActionsChanged(List<RemoteAction> actions);
+    }
+
+    private final Context mContext;
+    private final IActivityManager mActivityManager;
+
+    private final MediaSessionManager mMediaSessionManager;
+    private MediaController mMediaController;
+
+    private RemoteAction mPauseAction;
+    private RemoteAction mPlayAction;
+
+    private MediaController.Callback mPlaybackChangedListener = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            if (!mListeners.isEmpty()) {
+                notifyActionsChanged(getMediaActions());
+            }
+        }
+    };
+
+    private ArrayList<ActionListener> mListeners = new ArrayList<>();
+
+    public PipMediaController(Context context, IActivityManager activityManager) {
+        mContext = context;
+        mActivityManager = activityManager;
+
+        createMediaActions();
+        mMediaSessionManager =
+                (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        mMediaSessionManager.addOnActiveSessionsChangedListener(controllers -> {
+            resolveActiveMediaController(controllers);
+        }, null);
+    }
+
+    /**
+     * Handles when an activity is pinned.
+     */
+    public void onActivityPinned() {
+        // Once we enter PiP, try to find the active media controller for the top most activity
+        resolveActiveMediaController(mMediaSessionManager.getActiveSessions(null));
+    }
+
+    /**
+     * Adds a new media action listener.
+     */
+    public void addListener(ActionListener listener) {
+        if (!mListeners.contains(listener)) {
+            mListeners.add(listener);
+            listener.onMediaActionsChanged(getMediaActions());
+        }
+    }
+
+    /**
+     * Removes a media action listener.
+     */
+    public void removeListener(ActionListener listener) {
+        listener.onMediaActionsChanged(Collections.EMPTY_LIST);
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Gets the set of media actions currently available.
+     */
+    private List<RemoteAction> getMediaActions() {
+        if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+            return Collections.EMPTY_LIST;
+        }
+
+        ArrayList<RemoteAction> mediaActions = new ArrayList<>();
+        int state = mMediaController.getPlaybackState().getState();
+        boolean isPlaying = MediaSession.isActiveState(state);
+        long actions = mMediaController.getPlaybackState().getActions();
+        if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
+            mediaActions.add(mPauseAction);
+        } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
+            mediaActions.add(mPlayAction);
+        }
+        return mediaActions;
+    }
+
+    /**
+     * Creates the standard media buttons that we may show.
+     */
+    private void createMediaActions() {
+        String pauseDescription = mContext.getString(R.string.pip_pause);
+        mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
+                R.drawable.ic_pause_white_24dp), pauseDescription, pauseDescription,
+                action -> mMediaController.getTransportControls().pause());
+
+        String playDescription = mContext.getString(R.string.pip_play);
+        mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
+                R.drawable.ic_play_arrow_white_24dp), playDescription, playDescription,
+                action -> mMediaController.getTransportControls().play());
+    }
+
+    /**
+     * Tries to find and set the active media controller for the top PiP activity.
+     */
+    private void resolveActiveMediaController(List<MediaController> controllers) {
+        if (controllers != null) {
+            final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext,
+                    mActivityManager);
+            if (topActivity != null) {
+                for (int i = 0; i < controllers.size(); i++) {
+                    final MediaController controller = controllers.get(i);
+                    if (controller.getPackageName().equals(topActivity.getPackageName())) {
+                        setActiveMediaController(controller);
+                        return;
+                    }
+                }
+            }
+        }
+        setActiveMediaController(null);
+    }
+
+    /**
+     * Sets the active media controller for the top PiP activity.
+     */
+    private void setActiveMediaController(MediaController controller) {
+        if (controller != mMediaController) {
+            if (mMediaController != null) {
+                mMediaController.unregisterCallback(mPlaybackChangedListener);
+            }
+            mMediaController = controller;
+            if (controller != null) {
+                controller.registerCallback(mPlaybackChangedListener);
+            }
+            if (!mListeners.isEmpty()) {
+                notifyActionsChanged(getMediaActions());
+            }
+
+            // TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
+        }
+    }
+
+    /**
+     * Notifies all listeners that the actions have changed.
+     */
+    private void notifyActionsChanged(List<RemoteAction> actions) {
+        if (!mListeners.isEmpty()) {
+            mListeners.forEach(l -> l.onMediaActionsChanged(actions));
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 0f7647d..6ef30c0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.systemui.pip.phone;
 
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
@@ -5,6 +21,7 @@
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
 import android.app.IActivityManager;
+import android.app.RemoteAction;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
@@ -16,11 +33,20 @@
 import android.util.Log;
 import android.view.IWindowManager;
 
-import java.util.ArrayList;
+import com.android.systemui.pip.phone.PipMediaController.ActionListener;
 
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the PiP menu activity.
+ *
+ * The current media session provides actions whenever there are no valid actions provided by the
+ * current PiP activity. Otherwise, those actions always take precedence.
+ */
 public class PipMenuActivityController {
 
-    private static final String TAG = "PipMenuActivityController";
+    private static final String TAG = "PipMenuActController";
 
     public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
     public static final String EXTRA_ACTIONS = "actions";
@@ -59,9 +85,12 @@
     private Context mContext;
     private IActivityManager mActivityManager;
     private IWindowManager mWindowManager;
+    private PipMediaController mMediaController;
 
     private ArrayList<Listener> mListeners = new ArrayList<>();
-    private ParceledListSlice mActions;
+    private ParceledListSlice mAppActions;
+    private ParceledListSlice mMediaActions;
+    private boolean mVisible;
 
     private Messenger mToActivityMessenger;
     private Messenger mMessenger = new Messenger(new Handler() {
@@ -70,13 +99,13 @@
             switch (msg.what) {
                 case MESSAGE_MENU_VISIBILITY_CHANGED: {
                     boolean visible = msg.arg1 > 0;
-                    mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
+                    onMenuVisibilityChanged(visible);
                     break;
                 }
                 case MESSAGE_EXPAND_PIP: {
                     mListeners.forEach(l -> l.onPipExpand());
                     // Preemptively mark the menu as invisible once we expand the PiP
-                    mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false));
+                    onMenuVisibilityChanged(false);
                     break;
                 }
                 case MESSAGE_MINIMIZE_PIP: {
@@ -86,14 +115,14 @@
                 case MESSAGE_DISMISS_PIP: {
                     mListeners.forEach(l -> l.onPipDismiss());
                     // Preemptively mark the menu as invisible once we dismiss the PiP
-                    mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false));
+                    onMenuVisibilityChanged(false);
                     break;
                 }
                 case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
                     mToActivityMessenger = msg.replyTo;
                     // Mark the menu as invisible once the activity finishes as well
                     if (mToActivityMessenger == null) {
-                        mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false));
+                        onMenuVisibilityChanged(false);
                     }
                     break;
                 }
@@ -101,11 +130,20 @@
         }
     });
 
+    private ActionListener mMediaActionListener = new ActionListener() {
+        @Override
+        public void onMediaActionsChanged(List<RemoteAction> mediaActions) {
+            mMediaActions = new ParceledListSlice<>(mediaActions);
+            updateMenuActions();
+        }
+    };
+
     public PipMenuActivityController(Context context, IActivityManager activityManager,
-            IWindowManager windowManager) {
+            IWindowManager windowManager, PipMediaController mediaController) {
         mContext = context;
         mActivityManager = activityManager;
         mWindowManager = windowManager;
+        mMediaController = mediaController;
     }
 
     /**
@@ -137,7 +175,7 @@
                         pinnedStackInfo.taskIds.length > 0) {
                     Intent intent = new Intent(mContext, PipMenuActivity.class);
                     intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
-                    intent.putExtra(EXTRA_ACTIONS, mActions);
+                    intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
                     ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
                     options.setLaunchTaskId(
                             pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
@@ -168,15 +206,31 @@
     }
 
     /**
-     * Sets the {@param actions} associated with the PiP.
+     * Sets the menu actions to the actions provided by the current PiP activity.
      */
-    public void setActions(ParceledListSlice actions) {
-        mActions = actions;
+    public void setAppActions(ParceledListSlice appActions) {
+        mAppActions = appActions;
+        updateMenuActions();
+    }
 
+    /**
+     * @return the best set of actions to show in the PiP menu.
+     */
+    private ParceledListSlice resolveMenuActions() {
+        if (isValidActions(mAppActions)) {
+            return mAppActions;
+        }
+        return mMediaActions;
+    }
+
+    /**
+     * Updates the PiP menu activity with the best set of actions provided.
+     */
+    private void updateMenuActions() {
         if (mToActivityMessenger != null) {
             Message m = Message.obtain();
             m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS;
-            m.obj = actions;
+            m.obj = resolveMenuActions();
             try {
                 mToActivityMessenger.send(m);
             } catch (RemoteException e) {
@@ -184,4 +238,30 @@
             }
         }
     }
+
+    /**
+     * Returns whether the set of actions are valid.
+     */
+    private boolean isValidActions(ParceledListSlice actions) {
+        return actions != null && actions.getList().size() > 0;
+    }
+
+    /**
+     * Handles changes in menu visibility.
+     */
+    private void onMenuVisibilityChanged(boolean visible) {
+        mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
+        if (visible != mVisible) {
+            if (visible) {
+                // Once visible, start listening for media action changes. This call will trigger
+                // the menu actions to be updated again.
+                mMediaController.addListener(mMediaActionListener);
+            } else {
+                // Once hidden, stop listening for media action changes. This call will trigger
+                // the menu actions to be updated again.
+                mMediaController.removeListener(mMediaActionListener);
+            }
+        }
+        mVisible = visible;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 18ae3cf..12fda14 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -87,6 +87,7 @@
     // Allow the PIP to be "docked" slightly offscreen
     private boolean mEnableMinimizing = true;
 
+    private final Rect mStableInsets = new Rect();
     private final Rect mPinnedStackBounds = new Rect();
     private final Rect mBoundedPinnedStackBounds = new Rect();
     private ValueAnimator mPinnedStackBoundsAnimator = null;
@@ -421,7 +422,8 @@
         mContext.getDisplay().getRealSize(displaySize);
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
                 mPinnedStackBounds);
-        mSnapAlgorithm.applyMinimizedOffset(toBounds, mBoundedPinnedStackBounds, displaySize);
+        mSnapAlgorithm.applyMinimizedOffset(toBounds, mBoundedPinnedStackBounds, displaySize,
+                mStableInsets);
         mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
                 toBounds, MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN,
                 mUpdatePinnedStackBoundsListener);
@@ -485,7 +487,7 @@
     /**
      * Resizes the pinned stack back to fullscreen.
      */
-    private void expandPinnedStackToFullscreen() {
+    void expandPinnedStackToFullscreen() {
         BackgroundThread.getHandler().post(() -> {
             try {
                 mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
@@ -528,6 +530,7 @@
                 if (updatePinnedStackBounds) {
                     mPinnedStackBounds.set(info.bounds);
                 }
+                mWindowManager.getStableInsets(info.displayId, mStableInsets);
                 mBoundedPinnedStackBounds.set(mWindowManager.getPictureInPictureMovementBounds(
                         info.displayId));
             }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
new file mode 100644
index 0000000..a8cdd1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip.phone;
+
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+
+import android.app.ActivityManager.StackInfo;
+import android.app.IActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class PipUtils {
+
+    private static final String TAG = "PipUtils";
+
+    /**
+     * @return the ComponentName of the top non-SystemUI activity in the pinned stack, or null if
+     *         none exists.
+     */
+    public static ComponentName getTopPinnedActivity(Context context,
+            IActivityManager activityManager) {
+        try {
+            final String sysUiPackageName = context.getPackageName();
+            final StackInfo pinnedStackInfo = activityManager.getStackInfo(PINNED_STACK_ID);
+            if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
+                    pinnedStackInfo.taskIds.length > 0) {
+                for (int i = pinnedStackInfo.taskNames.length - 1; i >= 0; i--) {
+                    ComponentName cn = ComponentName.unflattenFromString(
+                            pinnedStackInfo.taskNames[i]);
+                    if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
+                        return cn;
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Unable to get pinned stack.");
+        }
+        return null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index a622656..964fefa 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -377,16 +377,18 @@
                 mCurrentPipBounds = mPipBounds;
                 break;
         }
-        try {
-            int animationDurationMs = -1;
-            if (wasRecentsShown
-                    && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) {
-                animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
+        if (mCurrentPipBounds != null) {
+            try {
+                int animationDurationMs = -1;
+                if (wasRecentsShown
+                        && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) {
+                    animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
+                }
+                mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
+                        true, true, true, animationDurationMs);
+            } catch (RemoteException e) {
+                Log.e(TAG, "resizeStack failed", e);
             }
-            mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
-                    true, true, true, animationDurationMs);
-        } catch (RemoteException e) {
-            Log.e(TAG, "resizeStack failed", e);
         }
     }
 
@@ -590,7 +592,7 @@
         @Override
         public void onTaskStackChanged() {
             if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
-            if (!checkCurrentUserId()) {
+            if (!checkCurrentUserId(DEBUG)) {
                 return;
             }
             if (mState != STATE_NO_PIP) {
@@ -627,7 +629,7 @@
         @Override
         public void onActivityPinned() {
             if (DEBUG) Log.d(TAG, "onActivityPinned()");
-            if (!checkCurrentUserId()) {
+            if (!checkCurrentUserId(DEBUG)) {
                 return;
             }
             StackInfo stackInfo = getPinnedStackInfo();
@@ -658,9 +660,9 @@
         }
 
         @Override
-        public void onPinnedActivityRestartAttempt() {
+        public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) {
             if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
-            if (!checkCurrentUserId()) {
+            if (!checkCurrentUserId(DEBUG)) {
                 return;
             }
             // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
@@ -670,7 +672,7 @@
         @Override
         public void onPinnedStackAnimationEnded() {
             if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
-            if (!checkCurrentUserId()) {
+            if (!checkCurrentUserId(DEBUG)) {
                 return;
             }
             switch (mState) {
@@ -693,26 +695,6 @@
                     break;
             }
         }
-
-        // {@link android.app.ITaskStackListener} isn't multi-user aware.
-        // Check the current uid and current SystemUI's running uid
-        // so we can handle the PIP status change only once.
-        private boolean checkCurrentUserId() {
-            try {
-                int processUserId = UserHandle.myUserId();
-                int currentUserId = mActivityManager.getCurrentUser().id;
-                if (processUserId != currentUserId) {
-                    if (DEBUG) {
-                        Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId
-                            + " and the current user is uid=" + currentUserId);
-                    }
-                    return false;
-                }
-            } catch (RemoteException e) {
-                Log.w(TAG, "Unable to get current user.");
-            }
-            return true;
-        }
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 71dda2d..94ea4dc 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -43,6 +43,7 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.NotificationChannels;
 
 import java.io.PrintWriter;
 import java.text.NumberFormat;
@@ -151,8 +152,7 @@
                 .setOngoing(true)
                 .setContentTitle(mContext.getString(R.string.invalid_charger_title))
                 .setContentText(mContext.getString(R.string.invalid_charger_text))
-                .setPriority(Notification.PRIORITY_MAX)
-                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                .setChannel(NotificationChannels.ALERTS)
                 .setColor(mContext.getColor(
                         com.android.internal.R.color.system_notification_accent_color));
         SystemUI.overrideNotificationAppName(mContext, nb);
@@ -173,7 +173,7 @@
                 .setContentText(mContext.getString(textRes, percentage))
                 .setOnlyAlertOnce(true)
                 .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_WARNING))
-                .setPriority(Notification.PRIORITY_MAX)
+                .setChannel(NotificationChannels.ALERTS)
                 .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setColor(mContext.getColor(
                         com.android.internal.R.color.battery_saver_mode_color));
@@ -217,8 +217,16 @@
             return;
         }
         mTempWarning = false;
-        mNoMan.cancelAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP,
-                UserHandle.ALL);
+        dismissTemperatureWarningInternal();
+    }
+
+    /**
+     * Internal only version of {@link #dismissTemperatureWarning()} that simply dismisses
+     * the notification. As such, the notification will not show again until
+     * {@link #dismissTemperatureWarning()} is called.
+     */
+    private void dismissTemperatureWarningInternal() {
+        mNoMan.cancelAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, UserHandle.ALL);
     }
 
     @Override
@@ -233,7 +241,7 @@
                 .setShowWhen(false)
                 .setContentTitle(mContext.getString(R.string.high_temp_title))
                 .setContentText(mContext.getString(R.string.high_temp_notif_message))
-                .setPriority(Notification.PRIORITY_HIGH)
+                .setChannel(NotificationChannels.ALERTS)
                 .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING))
                 .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
@@ -390,10 +398,10 @@
             } else if (action.equals(ACTION_DISMISSED_WARNING)) {
                 dismissLowBatteryWarning();
             } else if (ACTION_CLICKED_TEMP_WARNING.equals(action)) {
-                dismissTemperatureWarning();
+                dismissTemperatureWarningInternal();
                 showTemperatureDialog();
             } else if (ACTION_DISMISSED_TEMP_WARNING.equals(action)) {
-                dismissTemperatureWarning();
+                dismissTemperatureWarningInternal();
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 28ca6a3..1d4a5c7 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.BatteryManager;
 import android.os.Handler;
@@ -221,11 +222,15 @@
     };
 
     private void initTemperatureWarning() {
-        if (!mContext.getResources().getBoolean(R.bool.config_showTemperatureWarning)) {
+        ContentResolver resolver = mContext.getContentResolver();
+        Resources resources = mContext.getResources();
+        if (Settings.Global.getInt(resolver, Settings.Global.SHOW_TEMPERATURE_WARNING,
+                resources.getInteger(R.integer.config_showTemperatureWarning)) == 0) {
             return;
         }
 
-        mThrottlingTemp = mContext.getResources().getInteger(R.integer.config_warningTemperature);
+        mThrottlingTemp = Settings.Global.getFloat(resolver, Settings.Global.WARNING_TEMPERATURE,
+                resources.getInteger(R.integer.config_warningTemperature));
 
         if (mThrottlingTemp < 0f) {
             // Get the throttling temperature. No need to check if we're not throttling.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 26da551..b22ea4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -1,9 +1,12 @@
 package com.android.systemui.qs;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
@@ -49,9 +52,14 @@
         while (numPages < getChildCount()) {
             removeViewAt(getChildCount() - 1);
         }
+        TypedArray array = getContext().obtainStyledAttributes(
+                new int[]{android.R.attr.colorForeground});
+        int color = array.getColor(0, 0);
+        array.recycle();
         while (numPages > getChildCount()) {
             ImageView v = new ImageView(mContext);
             v.setImageResource(R.drawable.minor_a_b);
+            v.setImageTintList(ColorStateList.valueOf(color));
             addView(v, new LayoutParams(mPageIndicatorWidth, mPageIndicatorHeight));
         }
         // Refresh state.
@@ -196,7 +204,7 @@
         for (int i = 0; i < N; i++) {
             getChildAt(i).measure(widthChildSpec, heightChildSpec);
         }
-        int width = (mPageIndicatorWidth - mPageDotWidth) * N + mPageDotWidth;
+        int width = (mPageIndicatorWidth - mPageDotWidth) * (N - 1) + mPageDotWidth;
         setMeasuredDimension(width, mPageIndicatorHeight);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 1c242e9..a231e79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -29,7 +29,6 @@
     private PageIndicator mPageIndicator;
 
     private int mNumPages;
-    private View mDecorGroup;
     private PageListener mPageListener;
 
     private int mPosition;
@@ -145,14 +144,14 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mPageIndicator = (PageIndicator) findViewById(R.id.page_indicator);
-        mDecorGroup = findViewById(R.id.page_decor);
-        ((LayoutParams) mDecorGroup.getLayoutParams()).isDecor = true;
-
-        mPages.add((TilePage) LayoutInflater.from(mContext)
+        mPages.add((TilePage) LayoutInflater.from(getContext())
                 .inflate(R.layout.qs_paged_page, this, false));
     }
 
+    public void setPageIndicator(PageIndicator indicator) {
+        mPageIndicator = indicator;
+    }
+
     @Override
     public int getOffsetTop(TileRecord tile) {
         final ViewGroup parent = (ViewGroup) tile.tileView.getParent();
@@ -196,7 +195,7 @@
                 if (++index == mPages.size()) {
                     if (DEBUG) Log.d(TAG, "Adding page for "
                             + tile.tile.getClass().getSimpleName());
-                    mPages.add((TilePage) LayoutInflater.from(mContext)
+                    mPages.add((TilePage) LayoutInflater.from(getContext())
                             .inflate(R.layout.qs_paged_page, this, false));
                 }
             }
@@ -211,7 +210,7 @@
             }
             if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
             mPageIndicator.setNumPages(mNumPages);
-            mDecorGroup.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE);
+            mPageIndicator.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE);
             setAdapter(mAdapter);
             mAdapter.notifyDataSetChanged();
             setCurrentItem(0, false);
@@ -243,8 +242,7 @@
                 maxHeight = height;
             }
         }
-        setMeasuredDimension(getMeasuredWidth(), maxHeight
-                + (mDecorGroup.getVisibility() != View.GONE ? mDecorGroup.getMeasuredHeight() : 0));
+        setMeasuredDimension(getMeasuredWidth(), maxHeight);
     }
 
     private final Runnable mDistribute = new Runnable() {
@@ -265,7 +263,7 @@
         public TilePage(Context context, AttributeSet attrs) {
             super(context, attrs);
             updateResources();
-            setContentDescription(mContext.getString(R.string.accessibility_desc_quick_settings));
+            setContentDescription(getContext().getString(R.string.accessibility_desc_quick_settings));
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index fdefcf9..602f9bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -193,8 +193,9 @@
                 translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
                 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
 
-                mTopFiveQs.add(tileIcon);
-                mAllViews.add(tileIcon);
+                mTopFiveQs.add(tileView.getIcon());
+                mTopFiveQs.add(tileView.getBgCicle());
+                mAllViews.add(tileView.getIcon());
                 mAllViews.add(quickTileView);
             } else if (mFullRows && isIconInAnimatedRow(count)) {
                 // TODO: Refactor some of this, it shares a lot with the above block.
@@ -223,9 +224,10 @@
             // Make brightness appear static position and alpha in through second half.
             View brightness = mQsPanel.getBrightnessView();
             if (brightness != null) {
-                firstPageBuilder.addFloat(brightness, "translationY", mQsPanel.getHeight(), 0);
+//                firstPageBuilder.addFloat(brightness, "translationY", mQsPanel.getHeight(), 0);
                 mBrightnessAnimator = new TouchAnimator.Builder()
                         .addFloat(brightness, "alpha", 0, 1)
+                        .addFloat(mQsPanel.getPageIndicator(), "alpha", 0, 1)
                         .setStartDelay(.5f)
                         .build();
                 mAllViews.add(brightness);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index f4da5ec..91b4d0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,18 +18,12 @@
 
 import android.content.Context;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QS.HeightListener;
 import com.android.systemui.qs.customize.QSCustomizer;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
-import com.android.systemui.statusbar.phone.QSTileHost;
-import com.android.systemui.statusbar.phone.QuickStatusBarHeader;
 
 /**
  * Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
@@ -95,7 +89,7 @@
         updateBottom();
     }
 
-    void updateBottom() {
+    public void updateBottom() {
         int height = calculateContainerHeight();
         setBottom(getTop() + height);
         mQSDetail.setBottom(getTop() + height);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 5027144..a20b7ba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -33,11 +33,15 @@
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Dependency;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader;
 import com.android.systemui.plugins.qs.QS.Callback;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.QSTileHost;
 
 public class QSDetail extends LinearLayout {
@@ -160,7 +164,8 @@
             setupDetailHeader(adapter);
             if (toggleQs && !mFullyExpanded) {
                 mTriggeredExpand = true;
-                mHost.animateToggleQSExpansion();
+                SysUiServiceProvider.getComponent(mContext, CommandQueue.class)
+                        .animateExpandSettingsPanel(null);
             } else {
                 mTriggeredExpand = false;
             }
@@ -171,7 +176,8 @@
             x = mOpenX;
             y = mOpenY;
             if (toggleQs && mTriggeredExpand) {
-                mHost.animateToggleQSExpansion();
+                SysUiServiceProvider.getComponent(mContext, CommandQueue.class)
+                        .animateCollapsePanels();
                 mTriggeredExpand = false;
             }
         }
@@ -231,12 +237,8 @@
     protected void setupDetailFooter(DetailAdapter adapter) {
         final Intent settingsIntent = adapter.getSettingsIntent();
         mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
-        mDetailSettingsButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mHost.startActivityDismissingKeyguard(settingsIntent);
-            }
-        });
+        mDetailSettingsButton.setOnClickListener(v -> Dependency.get(ActivityStarter.class)
+                .postStartActivityDismissingKeyguard(settingsIntent, 0));
     }
 
     protected void setupDetailHeader(final DetailAdapter adapter) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index fb3b1d9..1835afd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -19,11 +19,9 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.SpannableStringBuilder;
 import android.text.method.LinkMovementMethod;
@@ -33,12 +31,13 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.View.OnClickListener;
-import android.view.Window;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.statusbar.phone.QSTileHost;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.SecurityController;
@@ -55,12 +54,13 @@
     private final ImageView mFooterIcon2;
     private final Context mContext;
     private final Callback mCallback = new Callback();
+    private final SecurityController mSecurityController;
+    private final ActivityStarter mActivityStarter;
+    private final Handler mMainHandler;
 
-    private SecurityController mSecurityController;
     private AlertDialog mDialog;
     private QSTileHost mHost;
-    protected Handler mHandler;
-    private final Handler mMainHandler;
+    protected H mHandler;
 
     private boolean mIsVisible;
     private boolean mIsIconVisible;
@@ -81,13 +81,13 @@
         mFooterIcon2Id = R.drawable.ic_qs_network_logging;
         mContext = context;
         mMainHandler = new Handler(Looper.getMainLooper());
+        mActivityStarter = Dependency.get(ActivityStarter.class);
+        mSecurityController = Dependency.get(SecurityController.class);
+        mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
     }
 
-    public void setHostEnvironment(QSTileHost host, SecurityController securityController,
-            Looper looper) {
+    public void setHostEnvironment(QSTileHost host) {
         mHost = host;
-        mSecurityController = securityController;
-        mHandler = new H(looper);
     }
 
     public void setListening(boolean listening) {
@@ -173,7 +173,7 @@
     public void onClick(DialogInterface dialog, int which) {
         if (which == DialogInterface.BUTTON_NEGATIVE) {
             final Intent settingsIntent = new Intent(ACTION_VPN_SETTINGS);
-            mHost.startActivityDismissingKeyguard(settingsIntent);
+            mActivityStarter.postStartActivityDismissingKeyguard(settingsIntent, 0);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
index 9c4a149..f28a0e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -16,14 +16,20 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.qs.QSTile.getColorForState;
+
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.Drawable;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
+
 import com.android.systemui.R;
 
 import java.util.Objects;
@@ -34,6 +40,8 @@
     protected final int mIconSizePx;
     protected final int mTilePaddingBelowIconPx;
     private boolean mAnimationEnabled = true;
+    private int mState = -1;
+    private int mTint;
 
     public QSIconView(Context context) {
         super(context);
@@ -65,7 +73,6 @@
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int w = getMeasuredWidth();
-        final int h = getMeasuredHeight();
         int top = 0;
         final int iconLeft = (w - mIcon.getMeasuredWidth()) / 2;
         layout(mIcon, iconLeft, top);
@@ -100,8 +107,43 @@
         } else {
             iv.clearColorFilter();
         }
+        if (state.state != mState) {
+            int color = getColorForState(getContext(), state.state);
+            mState = state.state;
+            if (iv.isShown()) {
+                animateGrayScale(mTint, color, iv);
+                mTint = color;
+            } else {
+                setTint(iv, color);
+                mTint = color;
+            }
+        }
     }
 
+    public static void animateGrayScale(int fromColor, int toColor, ImageView iv) {
+        final float fromAlpha = Color.alpha(fromColor);
+        final float toAlpha = Color.alpha(toColor);
+        final float fromChannel = Color.red(fromColor);
+        final float toChannel = Color.red(toColor);
+
+        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+        anim.setDuration(350);
+
+        anim.addUpdateListener(animation -> {
+            float fraction = animation.getAnimatedFraction();
+            int alpha = (int) (fromAlpha + (toAlpha - fromAlpha) * fraction);
+            int channel = (int) (fromChannel + (toChannel - fromChannel) * fraction);
+
+            setTint(iv, Color.argb(alpha, channel, channel, channel));
+        });
+        anim.start();
+    }
+
+    public static void setTint(ImageView iv, int color) {
+        iv.setImageTintList(ColorStateList.valueOf(color));
+    }
+
+
     protected int getIconMeasureMode() {
         return MeasureSpec.EXACTLY;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 9feaa0a..e004828 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -54,6 +54,7 @@
     protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
     protected final View mBrightnessView;
     private final H mHandler = new H();
+    private final View mPageIndicator;
 
     private int mPanelPaddingBottom;
     private int mBrightnessPaddingTop;
@@ -84,21 +85,31 @@
 
         setOrientation(VERTICAL);
 
-        mBrightnessView = LayoutInflater.from(context).inflate(
-                R.layout.quick_settings_brightness_dialog, this, false);
-        addView(mBrightnessView);
-
         setupTileLayout();
 
         mFooter = new QSFooter(this, context);
         addView(mFooter.getView());
 
+        mPageIndicator = LayoutInflater.from(context).inflate(
+                R.layout.qs_page_indicator, this, false);
+        addView(mPageIndicator);
+        if (mTileLayout instanceof PagedTileLayout) {
+            ((PagedTileLayout) mTileLayout).setPageIndicator((PageIndicator) mPageIndicator);
+        }
+
         updateResources();
 
+        mBrightnessView = LayoutInflater.from(context).inflate(
+                R.layout.quick_settings_brightness_dialog, this, false);
+        addView(mBrightnessView);
+
         mBrightnessController = new BrightnessController(getContext(),
                 (ImageView) findViewById(R.id.brightness_icon),
                 (ToggleSliderView) findViewById(R.id.brightness_slider));
+    }
 
+    public View getPageIndicator() {
+        return mPageIndicator;
     }
 
     protected void setupTileLayout() {
@@ -179,12 +190,11 @@
         mHost = host;
         mHost.addCallback(this);
         setTiles(mHost.getTiles());
-        mFooter.setHostEnvironment(host, host.getSecurityController(), host.getLooper());
+        mFooter.setHostEnvironment(host);
         mCustomizePanel = customizer;
         if (mCustomizePanel != null) {
             mCustomizePanel.setHost(mHost);
         }
-        mBrightnessController.setBackgroundLooper(host.getLooper());
     }
 
     public QSTileHost getHost() {
@@ -359,20 +369,10 @@
         };
         r.tile.addCallback(callback);
         r.callback = callback;
-        final View.OnClickListener click = new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onTileClick(r.tile);
-            }
-        };
-        final View.OnLongClickListener longClick = new View.OnLongClickListener() {
-            @Override
-            public boolean onLongClick(View v) {
-                r.tile.longClick();
-                return true;
-            }
-        };
-        r.tileView.init(click, longClick);
+        r.tileView.init(v -> r.tile.click(), v -> r.tile.secondaryClick(), v -> {
+            r.tile.longClick();
+            return true;
+        });
         r.tile.refreshState();
         mRecords.add(r);
 
@@ -402,10 +402,6 @@
         });
     }
 
-    protected void onTileClick(QSTile<?> tile) {
-        tile.click();
-    }
-
     public void closeDetail() {
         if (mCustomizePanel != null && mCustomizePanel.isShown()) {
             // Treat this as a detail panel for now, to make things easy.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index f73241c..e18654e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -17,13 +17,13 @@
 package com.android.systemui.qs;
 
 import android.app.ActivityManager;
-import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.service.quicksettings.Tile;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -31,29 +31,22 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSTile.State;
 import com.android.systemui.qs.external.TileServices;
-import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.ZenModeController;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Objects;
 
+import com.android.settingslib.Utils;
+
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
+import com.android.systemui.R;
+
 /**
  * Base quick-settings tile, extend this to create a new tile.
  *
@@ -79,7 +72,9 @@
     private String mTileSpec;
 
     public abstract TState newTileState();
+
     abstract protected void handleClick();
+
     abstract protected void handleUpdateState(TState state, Object arg);
 
     /**
@@ -93,7 +88,7 @@
     protected QSTile(Host host) {
         mHost = host;
         mContext = host.getContext();
-        mHandler = new H(host.getLooper());
+        mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
     }
 
     /**
@@ -134,7 +129,9 @@
         return null; // optional
     }
 
-    protected DetailAdapter createDetailAdapter() { throw new UnsupportedOperationException(); }
+    protected DetailAdapter createDetailAdapter() {
+        throw new UnsupportedOperationException();
+    }
 
     /**
      * Is a startup check whether this device currently supports this tile.
@@ -233,7 +230,8 @@
 
     protected void handleLongClick() {
         MetricsLogger.action(mContext, MetricsEvent.ACTION_QS_LONG_PRESS, getTileSpec());
-        mHost.startActivityDismissingKeyguard(getLongClickIntent());
+        Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
+                getLongClickIntent(), 0);
     }
 
     public abstract Intent getLongClickIntent();
@@ -319,6 +317,21 @@
 
     public abstract CharSequence getTileLabel();
 
+    public static int getColorForState(Context context, int state) {
+        switch (state) {
+            case Tile.STATE_UNAVAILABLE:
+                return Utils.getDisabled(context,
+                        Utils.getColorAttr(context, android.R.attr.textColorTertiary));
+            case Tile.STATE_INACTIVE:
+                return Utils.getColorAttr(context, android.R.attr.textColorSecondary);
+            case Tile.STATE_ACTIVE:
+                return Utils.getColorAttr(context, android.R.attr.colorPrimary);
+            default:
+                Log.e("QSTile", "Invalid state " + state);
+                return 0;
+        }
+    }
+
     protected final class H extends Handler {
         private static final int ADD_CALLBACK = 1;
         private static final int CLICK = 2;
@@ -357,7 +370,8 @@
                     if (mState.disabledByPolicy) {
                         Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
                                 mContext, mState.enforcedAdmin);
-                        mHost.startActivityDismissingKeyguard(intent);
+                        Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
+                                intent, 0);
                     } else {
                         mAnnounceNextStateChange = true;
                         handleClick();
@@ -412,36 +426,17 @@
     }
 
     public interface Host {
-        void startActivityDismissingKeyguard(Intent intent);
-        void startActivityDismissingKeyguard(PendingIntent intent);
-        void startRunnableDismissingKeyguard(Runnable runnable);
         void warn(String message, Throwable t);
         void collapsePanels();
-        void animateToggleQSExpansion();
         void openPanels();
-        Looper getLooper();
         Context getContext();
         Collection<QSTile<?>> getTiles();
         void addCallback(Callback callback);
         void removeCallback(Callback callback);
-        BluetoothController getBluetoothController();
-        LocationController getLocationController();
-        RotationLockController getRotationLockController();
-        NetworkController getNetworkController();
-        ZenModeController getZenModeController();
-        HotspotController getHotspotController();
-        CastController getCastController();
-        FlashlightController getFlashlightController();
-        KeyguardMonitor getKeyguardMonitor();
-        UserSwitcherController getUserSwitcherController();
-        UserInfoController getUserInfoController();
-        BatteryController getBatteryController();
         TileServices getTileServices();
         void removeTile(String tileSpec);
-        ManagedProfileController getManagedProfileController();
 
-
-        public interface Callback {
+        interface Callback {
             void onTilesChanged();
         }
     }
@@ -546,6 +541,7 @@
 
     public static class State {
         public Icon icon;
+        public int state = Tile.STATE_ACTIVE;
         public CharSequence label;
         public CharSequence contentDescription;
         public CharSequence dualLabelContentDescription;
@@ -572,6 +568,7 @@
                     || !Objects.equals(other.expandedAccessibilityClassName,
                     expandedAccessibilityClassName)
                     || !Objects.equals(other.disabledByPolicy, disabledByPolicy)
+                    || !Objects.equals(other.state, state)
                     || !Objects.equals(other.enforcedAdmin, enforcedAdmin);
             other.icon = icon;
             other.label = label;
@@ -582,6 +579,7 @@
             other.expandedAccessibilityClassName = expandedAccessibilityClassName;
             other.autoMirrorDrawable = autoMirrorDrawable;
             other.disabledByPolicy = disabledByPolicy;
+            other.state = state;
             if (enforcedAdmin == null) {
                 other.enforcedAdmin = null;
             } else if (other.enforcedAdmin == null) {
@@ -609,6 +607,7 @@
             sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable);
             sb.append(",disabledByPolicy=").append(disabledByPolicy);
             sb.append(",enforcedAdmin=").append(enforcedAdmin);
+            sb.append(",state=").append(state);
             return sb.append(']');
         }
     }
@@ -648,7 +647,6 @@
         public boolean connected;
         public boolean activityIn;
         public boolean activityOut;
-        public int overlayIconId;
         public boolean filter;
         public boolean isOverlayIconWide;
 
@@ -657,12 +655,10 @@
             final SignalState o = (SignalState) other;
             final boolean changed = o.connected != connected || o.activityIn != activityIn
                     || o.activityOut != activityOut
-                    || o.overlayIconId != overlayIconId
                     || o.isOverlayIconWide != isOverlayIconWide;
             o.connected = connected;
             o.activityIn = activityIn;
             o.activityOut = activityOut;
-            o.overlayIconId = overlayIconId;
             o.filter = filter;
             o.isOverlayIconWide = isOverlayIconWide;
             return super.copyTo(other) || changed;
@@ -674,7 +670,6 @@
             rt.insert(rt.length() - 1, ",connected=" + connected);
             rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
             rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
-            rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId);
             rt.insert(rt.length() - 1, ",filter=" + filter);
             rt.insert(rt.length() - 1, ",wideOverlayIcon=" + isOverlayIconWide);
             return rt;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
index 2b7fcc5..a177cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -15,31 +15,49 @@
  */
 package com.android.systemui.qs;
 
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.RippleDrawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.service.quicksettings.Tile;
 import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
 import android.widget.LinearLayout;
 import android.widget.Switch;
 
+import com.android.settingslib.Utils;
+
 import com.android.systemui.R;
 
 public class QSTileBaseView extends LinearLayout {
 
+    private static final String TAG = "QSTileBaseView";
     private final H mHandler = new H();
+    private final ImageView mBg;
     protected QSIconView mIcon;
     protected RippleDrawable mRipple;
     private Drawable mTileBackground;
     private String mAccessibilityClass;
     private boolean mTileState;
     private boolean mCollapsedView;
+    private final int mColorActive;
+    private final int mColorInactive;
+    private final int mColorDisabled;
+    private int mCircleColor;
 
     public QSTileBaseView(Context context, QSIconView icon) {
         this(context, icon, false);
@@ -47,8 +65,26 @@
 
     public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) {
         super(context);
+        // Default to Quick Tile padding, and QSTileView will specify its own padding.
+        int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
+
+        FrameLayout frame = new FrameLayout(context);
+        frame.setForegroundGravity(Gravity.CENTER);
+        int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
+        addView(frame, new LayoutParams(size, size));
+        mBg = new ImageView(getContext());
+        mBg.setScaleType(ScaleType.FIT_CENTER);
+        mBg.setImageResource(R.drawable.ic_qs_circle);
+        frame.addView(mBg);
         mIcon = icon;
-        addView(mIcon);
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        params.setMargins(0, padding, 0, padding);
+        frame.addView(mIcon, params);
+        mColorActive = Utils.getColorAttr(context, android.R.attr.textColorPrimary);
+        mColorDisabled = Utils.getDisabled(context,
+                Utils.getColorAttr(context, android.R.attr.textColorTertiary));
+        mColorInactive = Utils.getColorAttr(context, android.R.attr.textColorSecondary);
 
         mTileBackground = newTileBackground();
         if (mTileBackground instanceof RippleDrawable) {
@@ -57,18 +93,20 @@
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
         setBackground(mTileBackground);
 
-        // Default to Quick Tile padding, and QSTileView will specify its own padding.
-        int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
-        setPadding(0, padding, 0, padding);
+        setPadding(0, 0, 0, 0);
         setClipChildren(false);
         setClipToPadding(false);
         mCollapsedView = collapsedView;
         setFocusable(true);
     }
 
+    public View getBgCicle() {
+        return mBg;
+    }
+
     protected Drawable newTileBackground() {
-        final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
-        final TypedArray ta = mContext.obtainStyledAttributes(attrs);
+        final int[] attrs = new int[]{android.R.attr.selectableItemBackgroundBorderless};
+        final TypedArray ta = getContext().obtainStyledAttributes(attrs);
         final Drawable d = ta.getDrawable(0);
         ta.recycle();
         return d;
@@ -85,11 +123,12 @@
         // center the touch feedback on the center of the icon, and dial it down a bit
         final int cx = width / 2;
         final int cy = height / 2;
-        final int rad = (int)(mIcon.getHeight() * .85f);
+        final int rad = (int) (mIcon.getHeight() * .85f);
         mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
     }
 
-    public void init(OnClickListener click, OnLongClickListener longClick) {
+    public void init(OnClickListener click, OnClickListener secondaryClick,
+            OnLongClickListener longClick) {
         setClickable(true);
         setOnClickListener(click);
         setOnLongClickListener(longClick);
@@ -128,6 +167,16 @@
     }
 
     protected void handleStateChanged(QSTile.State state) {
+        int circleColor = getCircleColor(state.state);
+        if (circleColor != mCircleColor) {
+            if (mBg.isShown()) {
+                QSIconView.animateGrayScale(mCircleColor, circleColor, mBg);
+            } else {
+                QSIconView.setTint(mBg, circleColor);
+            }
+            mCircleColor = circleColor;
+        }
+
         mIcon.setIcon(state);
         if (mCollapsedView && !TextUtils.isEmpty(state.minimalContentDescription)) {
             setContentDescription(state.minimalContentDescription);
@@ -144,6 +193,19 @@
         }
     }
 
+    private int getCircleColor(int state) {
+        switch (state) {
+            case Tile.STATE_ACTIVE:
+                return mColorActive;
+            case Tile.STATE_INACTIVE:
+            case Tile.STATE_UNAVAILABLE:
+                return mColorDisabled;
+            default:
+                Log.e(TAG, "Invalid state " + state);
+                return 0;
+        }
+    }
+
     public QSIconView getIcon() {
         return mIcon;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index e59873a..7126f3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -16,27 +16,34 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.qs.QSTile.getColorForState;
+
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.util.MathUtils;
+import android.service.quicksettings.Tile;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
+
 import libcore.util.Objects;
 
 /** View that represents a standard quick settings tile. **/
 public class QSTileView extends QSTileBaseView {
-    private final int mTileSpacingPx;
-    private int mTilePaddingTopPx;
 
     protected TextView mLabel;
     private ImageView mPadLock;
+    private int mState;
+    private OnClickListener mClick;
+    private OnClickListener mSecondaryClick;
 
     public QSTileView(Context context, QSIconView icon) {
         this(context, icon, false);
@@ -45,13 +52,10 @@
     public QSTileView(Context context, QSIconView icon, boolean collapsedView) {
         super(context, icon, collapsedView);
 
-        final Resources res = context.getResources();
-        mTileSpacingPx = res.getDimensionPixelSize(R.dimen.qs_tile_spacing);
-
         setClipChildren(false);
+        setClipToPadding(false);
 
         setClickable(true);
-        updateTopPadding();
         setId(View.generateViewId());
         createLabel();
         setOrientation(VERTICAL);
@@ -62,27 +66,17 @@
         return mLabel;
     }
 
-    protected void updateTopPadding() {
-        Resources res = getResources();
-        int padding = res.getDimensionPixelSize(R.dimen.qs_tile_padding_top);
-        int largePadding = res.getDimensionPixelSize(R.dimen.qs_tile_padding_top_large_text);
-        float largeFactor = (MathUtils.constrain(getResources().getConfiguration().fontScale,
-                1.0f, FontSizeUtils.LARGE_TEXT_SCALE) - 1f) / (FontSizeUtils.LARGE_TEXT_SCALE - 1f);
-        mTilePaddingTopPx = Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
-        setPadding(mTileSpacingPx, mTilePaddingTopPx + mTileSpacingPx, mTileSpacingPx,
-                mTileSpacingPx);
-        requestLayout();
-    }
-
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        updateTopPadding();
         FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
     }
 
     protected void createLabel() {
-        View view = LayoutInflater.from(mContext).inflate(R.layout.qs_tile_label, null);
+        ViewGroup view = (ViewGroup) LayoutInflater.from(getContext())
+                .inflate(R.layout.qs_tile_label, null);
+        view.setClipChildren(false);
+        view.setClipToPadding(false);
         mLabel = (TextView) view.findViewById(R.id.tile_label);
         mPadLock = (ImageView) view.findViewById(R.id.restricted_padlock);
         addView(view);
@@ -91,10 +85,32 @@
     @Override
     protected void handleStateChanged(QSTile.State state) {
         super.handleStateChanged(state);
-        if (!Objects.equal(mLabel.getText(), state.label)) {
+        if (!Objects.equal(mLabel.getText(), state.label) || mState != state.state) {
+            if (state.state == Tile.STATE_UNAVAILABLE) {
+                int color = getColorForState(getContext(), state.state);
+                state.label = new SpannableStringBuilder().append(state.label,
+                        new ForegroundColorSpan(color),
+                        SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
+            }
+            mState = state.state;
             mLabel.setText(state.label);
         }
         mLabel.setEnabled(!state.disabledByPolicy);
         mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
     }
+
+    @Override
+    public void init(OnClickListener click, OnClickListener secondaryClick, OnLongClickListener longClick) {
+        mClick = click;
+        mSecondaryClick = secondaryClick;
+        super.init(click, secondaryClick, longClick);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_UP)  {
+            setOnClickListener(event.getY() < (getMeasuredHeight() / 2) ? mClick : mSecondaryClick);
+        }
+        return super.onTouchEvent(event);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index de3e19c..16b351e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -56,7 +56,7 @@
         }
         mTileLayout = new HeaderTileLayout(context);
         mTileLayout.setListening(mListening);
-        addView((View) mTileLayout, 1 /* Between brightness and footer */);
+        addView((View) mTileLayout, 0 /* Between brightness and footer */);
     }
 
     @Override
@@ -113,11 +113,6 @@
     }
 
     @Override
-    protected void onTileClick(QSTile<?> tile) {
-        tile.secondaryClick();
-    }
-
-    @Override
     public void onTuningChanged(String key, String newValue) {
         // No tunings for you.
         if (key.equals(QS_SHOW_BRIGHTNESS)) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index b2bfa06..8555e31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -33,7 +33,6 @@
 
     private FrameLayout mIconFrame;
     private ImageView mSignal;
-    private ImageView mOverlay;
     private ImageView mIn;
     private ImageView mOut;
 
@@ -62,8 +61,6 @@
         mIconFrame = new FrameLayout(mContext);
         mSignal = new ImageView(mContext);
         mIconFrame.addView(mSignal);
-        mOverlay = new ImageView(mContext);
-        mIconFrame.addView(mOverlay, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
         return mIconFrame;
     }
 
@@ -109,17 +106,6 @@
     public void setIcon(QSTile.State state) {
         final SignalState s = (SignalState) state;
         setIcon(mSignal, s);
-        if (s.overlayIconId > 0) {
-            mOverlay.setVisibility(VISIBLE);
-            mOverlay.setImageResource(s.overlayIconId);
-        } else {
-            mOverlay.setVisibility(GONE);
-        }
-        if (s.overlayIconId > 0 && s.isOverlayIconWide) {
-            mSignal.setPaddingRelative(mWideOverlayIconStartPadding, 0, 0, 0);
-        } else {
-            mSignal.setPaddingRelative(0, 0, 0, 0);
-        }
         Drawable drawable = mSignal.getDrawable();
         if (state.autoMirrorDrawable && drawable != null) {
             drawable.setAutoMirrored(true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 0be53b4..730b55d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -36,13 +36,14 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.QSDetailClipper;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
 
 import java.util.ArrayList;
@@ -140,7 +141,7 @@
             mNotifQsContainer.setCustomizerShowing(true);
             announceForAccessibility(mContext.getString(
                     R.string.accessibility_desc_quick_settings_edit));
-            mHost.getKeyguardMonitor().addCallback(mKeyguardCallback);
+            Dependency.get(KeyguardMonitor.class).addCallback(mKeyguardCallback);
         }
     }
 
@@ -156,7 +157,7 @@
             mNotifQsContainer.setCustomizerShowing(false);
             announceForAccessibility(mContext.getString(
                     R.string.accessibility_desc_quick_settings));
-            mHost.getKeyguardMonitor().removeCallback(mKeyguardCallback);
+            Dependency.get(KeyguardMonitor.class).removeCallback(mKeyguardCallback);
         }
     }
 
@@ -206,12 +207,9 @@
         mTileAdapter.saveSpecs(mHost);
     }
 
-    private final Callback mKeyguardCallback = new Callback() {
-        @Override
-        public void onKeyguardChanged() {
-            if (mHost.getKeyguardMonitor().isShowing()) {
-                hide(0, 0);
-            }
+    private final Callback mKeyguardCallback = () -> {
+        if (Dependency.get(KeyguardMonitor.class).isShowing()) {
+            hide(0, 0);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 0cd6490..72e6fcc0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -30,6 +30,7 @@
 import android.service.quicksettings.TileService;
 import android.widget.Button;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTile.DrawableIcon;
@@ -59,7 +60,7 @@
     private void addSystemTiles(final QSTileHost host) {
         String possible = mContext.getString(R.string.quick_settings_tiles_stock);
         String[] possibleTiles = possible.split(",");
-        final Handler qsHandler = new Handler(host.getLooper());
+        final Handler qsHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
         final Handler mainHandler = new Handler(Looper.getMainLooper());
         for (int i = 0; i < possibleTiles.length; i++) {
             final String spec = possibleTiles[i];
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 9e3889b..3afbc35 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -15,15 +15,10 @@
  */
 package com.android.systemui.qs.external;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-
 import android.app.ActivityManager;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.graphics.drawable.Drawable;
@@ -39,14 +34,21 @@
 import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 import android.view.IWindowManager;
+import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.Dependency;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
 import com.android.systemui.statusbar.phone.QSTileHost;
 import libcore.util.Objects;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+
 public class CustomTile extends QSTile<QSTile.State> implements TileChangeListener {
     public static final String PREFIX = "custom(";
 
@@ -63,7 +65,6 @@
     private final IQSTileService mService;
     private final TileServiceManager mServiceManager;
     private final int mUser;
-    private Context mAppContext;
     private android.graphics.drawable.Icon mDefaultIcon;
 
     private boolean mListening;
@@ -81,10 +82,6 @@
         mService = mServiceManager.getTileService();
         mServiceManager.setTileChangeListener(this);
         mUser = ActivityManager.getCurrentUser();
-        try {
-            mAppContext = mContext.createPackageContext(mComponent.getPackageName(), 0);
-        } catch (NameNotFoundException e) {
-        }
     }
 
     private void setTileIcon() {
@@ -287,27 +284,17 @@
         if (mServiceManager.hasPendingBind()) {
             tileState = Tile.STATE_UNAVAILABLE;
         }
+        state.state = tileState;
         Drawable drawable;
-        boolean mHasRes = false;
-        android.graphics.drawable.Icon icon = mTile.getIcon();
         try {
-            drawable = icon.loadDrawable(mAppContext);
-            mHasRes = icon.getType() == android.graphics.drawable.Icon.TYPE_RESOURCE;
+            drawable = mTile.getIcon().loadDrawable(mContext);
         } catch (Exception e) {
             Log.w(TAG, "Invalid icon, forcing into unavailable state");
             tileState = Tile.STATE_UNAVAILABLE;
-            drawable = mDefaultIcon.loadDrawable(mAppContext);
+            drawable = mDefaultIcon.loadDrawable(mContext);
         }
-        final int color = TileColorPicker.getInstance(mContext).getColor(tileState);
-        drawable.setTint(color);
-        state.icon = mHasRes ? new DrawableIconWithRes(drawable, icon.getResId())
-                : new DrawableIcon(drawable);
+        state.icon = new DrawableIcon(drawable);
         state.label = mTile.getLabel();
-        if (tileState == Tile.STATE_UNAVAILABLE) {
-            state.label = new SpannableStringBuilder().append(state.label,
-                    new ForegroundColorSpan(color),
-                    SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
-        }
         if (mTile.getContentDescription() != null) {
             state.contentDescription = mTile.getContentDescription();
         } else {
@@ -321,13 +308,10 @@
     }
 
     public void startUnlockAndRun() {
-        mHost.startRunnableDismissingKeyguard(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mService.onUnlockComplete();
-                } catch (RemoteException e) {
-                }
+        Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
+            try {
+                mService.onUnlockComplete();
+            } catch (RemoteException e) {
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 015a4c0..5c23eb7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -30,13 +30,13 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.quicksettings.IQSService;
-import android.service.quicksettings.IQSTileService;
 import android.service.quicksettings.Tile;
 import android.service.quicksettings.TileService;
 import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.phone.QSTileHost;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -284,13 +284,13 @@
 
     @Override
     public boolean isLocked() {
-        KeyguardMonitor keyguardMonitor = mHost.getKeyguardMonitor();
+        KeyguardMonitor keyguardMonitor = Dependency.get(KeyguardMonitor.class);
         return keyguardMonitor.isShowing();
     }
 
     @Override
     public boolean isSecure() {
-        KeyguardMonitor keyguardMonitor = mHost.getKeyguardMonitor();
+        KeyguardMonitor keyguardMonitor = Dependency.get(KeyguardMonitor.class);
         return keyguardMonitor.isSecure() && keyguardMonitor.isShowing();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index e57cd58..e8f90ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -23,6 +23,7 @@
 import android.net.ConnectivityManager;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
@@ -92,6 +93,7 @@
         } else {
             state.icon = mDisable;
         }
+        state.state = airplaneMode ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.contentDescription = state.label;
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
                 = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 833375e..7e04b67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -19,10 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.service.quicksettings.Tile;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
@@ -41,11 +38,12 @@
 import com.android.settingslib.BatteryInfo;
 import com.android.settingslib.graph.UsageView;
 import com.android.systemui.BatteryMeterDrawable;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.external.TileColorPicker;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.qs.external.TileColorPicker;
 import com.android.systemui.statusbar.policy.BatteryController;
 
 import java.text.NumberFormat;
@@ -63,7 +61,7 @@
 
     public BatteryTile(Host host) {
         super(host);
-        mBatteryController = host.getBatteryController();
+        mBatteryController = Dependency.get(BatteryController.class);
     }
 
     @Override
@@ -105,6 +103,11 @@
 
     @Override
     protected void handleClick() {
+        mBatteryController.setPowerSaveMode(!mPowerSave);
+    }
+
+    @Override
+    protected void handleSecondaryClick() {
         showDetail(true);
     }
 
@@ -118,26 +121,10 @@
         int level = (arg != null) ? (Integer) arg : mLevel;
         String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
 
-        state.icon = new Icon() {
-            @Override
-            public Drawable getDrawable(Context context) {
-                BatteryMeterDrawable drawable =
-                        new BatteryMeterDrawable(context,
-                        context.getColor(R.color.batterymeter_frame_color));
-                drawable.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
-                drawable.onPowerSaveChanged(mPowerSave);
-                final int color = TileColorPicker.getInstance(context).getColor(Tile.STATE_ACTIVE);
-                drawable.setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_IN));
-                return drawable;
-            }
-
-            @Override
-            public int getPadding() {
-                return mHost.getContext().getResources().getDimensionPixelSize(
-                        R.dimen.qs_battery_padding);
-            }
-        };
-        state.label = percentage;
+        state.state = mCharging ? Tile.STATE_UNAVAILABLE
+                : mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+        state.icon = ResourceIcon.get(R.drawable.ic_qs_battery_saver);
+        state.label = mContext.getString(R.string.battery_detail_switch_title);
         state.contentDescription = mContext.getString(R.string.accessibility_quick_settings_battery,
                 percentage) + "," +
                 (mPowerSave ? mContext.getString(R.string.battery_saver_notification_title)
@@ -153,7 +140,7 @@
         mLevel = level;
         mPluggedIn = pluggedIn;
         mCharging = charging;
-        refreshState((Integer) level);
+        refreshState(level);
         if (mDetailShown) {
             mBatteryDetail.postBindView();
         }
@@ -213,11 +200,6 @@
             mDrawable.onBatteryLevelChanged(100, false, false);
             mDrawable.onPowerSaveChanged(true);
             mDrawable.disableShowPercent();
-
-            final int color = TileColorPicker.getInstance(mCurrentView.getContext())
-                    .getColor(Tile.STATE_ACTIVE);
-            mDrawable.setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_IN));
-
             ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
             Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
             checkbox.setChecked(mPowerSave);
@@ -293,7 +275,7 @@
                 mDetailShown = true;
                 v.getContext().registerReceiver(mReceiver,
                         new IntentFilter(Intent.ACTION_TIME_TICK), null,
-                        PhoneStatusBar.getTimeTickHandler(v.getContext()));
+                        Dependency.get(Dependency.TIME_TICK_HANDLER));
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index f83b279..91e76ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -31,7 +32,9 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSDetailItems;
 import com.android.systemui.qs.QSDetailItems.Item;
@@ -47,10 +50,12 @@
 
     private final BluetoothController mController;
     private final BluetoothDetailAdapter mDetailAdapter;
+    private final ActivityStarter mActivityStarter;
 
     public BluetoothTile(Host host) {
         super(host);
-        mController = host.getBluetoothController();
+        mController = Dependency.get(BluetoothController.class);
+        mActivityStarter = Dependency.get(ActivityStarter.class);
         mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
     }
 
@@ -74,7 +79,7 @@
     }
 
     @Override
-    protected void handleSecondaryClick() {
+    protected void handleClick() {
         // Secondary clicks are header clicks, just toggle.
         final boolean isEnabled = (Boolean)mState.value;
         MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
@@ -87,9 +92,10 @@
     }
 
     @Override
-    protected void handleClick() {
+    protected void handleSecondaryClick() {
         if (!mController.canConfigBluetooth()) {
-            mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
+            mActivityStarter.postStartActivityDismissingKeyguard(
+                    new Intent(Settings.ACTION_BLUETOOTH_SETTINGS), 0);
             return;
         }
         showDetail(true);
@@ -140,11 +146,13 @@
             if (TextUtils.isEmpty(state.label)) {
                 state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
             }
+            state.state = Tile.STATE_ACTIVE;
         } else {
             state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_off);
             state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
             state.contentDescription = mContext.getString(
                     R.string.accessibility_quick_settings_bluetooth_off);
+            state.state = Tile.STATE_INACTIVE;
         }
 
         CharSequence bluetoothName = state.label;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 8afa91e..7415765 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
@@ -27,7 +28,9 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSDetailItems;
 import com.android.systemui.qs.QSDetailItems.Item;
@@ -48,12 +51,14 @@
     private final CastDetailAdapter mDetailAdapter;
     private final KeyguardMonitor mKeyguard;
     private final Callback mCallback = new Callback();
+    private final ActivityStarter mActivityStarter;
 
     public CastTile(Host host) {
         super(host);
-        mController = host.getCastController();
+        mController = Dependency.get(CastController.class);
         mDetailAdapter = new CastDetailAdapter();
-        mKeyguard = host.getKeyguardMonitor();
+        mKeyguard = Dependency.get(KeyguardMonitor.class);
+        mActivityStarter = Dependency.get(ActivityStarter.class);
     }
 
     @Override
@@ -93,15 +98,17 @@
     }
 
     @Override
+    protected void handleSecondaryClick() {
+        handleClick();
+    }
+
+    @Override
     protected void handleClick() {
         if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) {
-            mHost.startRunnableDismissingKeyguard(new Runnable() {
-                @Override
-                public void run() {
-                    MetricsLogger.action(mContext, getMetricsCategory());
-                    showDetail(true);
-                    mHost.openPanels();
-                }
+            mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+                MetricsLogger.action(mContext, getMetricsCategory());
+                showDetail(true);
+                mHost.openPanels();
             });
             return;
         }
@@ -135,6 +142,7 @@
         if (!state.value && connecting) {
             state.label = mContext.getString(R.string.quick_settings_connecting);
         }
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_cast_on
                 : R.drawable.ic_qs_cast_off);
         mDetailAdapter.updateItems(devices);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 1406c9f..bdc95c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.service.quicksettings.Tile;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,14 +29,16 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSIconView;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.SignalTileView;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.SignalCallbackAdapter;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 /** Quick settings tile: Cellular **/
 public class CellularTile extends QSTile<QSTile.SignalState> {
@@ -47,10 +50,12 @@
     private final CellularDetailAdapter mDetailAdapter;
 
     private final CellSignalCallback mSignalCallback = new CellSignalCallback();
+    private final ActivityStarter mActivityStarter;
 
     public CellularTile(Host host) {
         super(host);
-        mController = host.getNetworkController();
+        mController = Dependency.get(NetworkController.class);
+        mActivityStarter = Dependency.get(ActivityStarter.class);
         mDataController = mController.getMobileDataController();
         mDetailAdapter = new CellularDetailAdapter();
     }
@@ -86,11 +91,16 @@
 
     @Override
     protected void handleClick() {
+        mDataController.setMobileDataEnabled(!mDataController.isMobileDataEnabled());
+    }
+
+    @Override
+    protected void handleSecondaryClick() {
         MetricsLogger.action(mContext, getMetricsCategory());
         if (mDataController.isMobileDataSupported()) {
             showDetail(true);
         } else {
-            mHost.startActivityDismissingKeyguard(CELLULAR_SETTINGS);
+            mActivityStarter.postStartActivityDismissingKeyguard(CELLULAR_SETTINGS, 0);
         }
     }
 
@@ -111,10 +121,13 @@
                 : !cb.enabled || cb.airplaneModeEnabled ? R.drawable.ic_qs_signal_disabled
                 : cb.mobileSignalIconId > 0 ? cb.mobileSignalIconId
                 : R.drawable.ic_qs_signal_no_signal;
-        state.icon = ResourceIcon.get(iconId);
+        if (cb.dataTypeIconId != 0) {
+            state.icon = ResourceIcon.get(cb.dataTypeIconId);
+        } else {
+            state.icon = ResourceIcon.get(iconId);
+        }
         state.isOverlayIconWide = cb.isDataTypeIconWide;
         state.autoMirrorDrawable = !cb.noSim;
-        state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) ? cb.dataTypeIconId : 0;
         state.filter = iconId != R.drawable.ic_qs_no_sim;
         state.activityIn = cb.enabled && cb.activityIn;
         state.activityOut = cb.enabled && cb.activityOut;
@@ -148,6 +161,7 @@
                 = Button.class.getName();
         state.value = mDataController.isMobileDataSupported()
                 && mDataController.isMobileDataEnabled();
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
 
     @Override
@@ -183,9 +197,10 @@
         String enabledDesc;
         boolean noSim;
         boolean isDataTypeIconWide;
+        boolean roaming;
     }
 
-    private final class CellSignalCallback extends SignalCallbackAdapter {
+    private final class CellSignalCallback implements SignalCallback {
         private final CallbackInfo mInfo = new CallbackInfo();
         @Override
         public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
@@ -197,7 +212,7 @@
         @Override
         public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
                 int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
-                String description, boolean isWide, int subId) {
+                String description, boolean isWide, int subId, boolean roaming) {
             if (qsIcon == null) {
                 // Not data sim, don't display.
                 return;
@@ -211,6 +226,7 @@
             mInfo.activityOut = activityOut;
             mInfo.enabledDesc = description;
             mInfo.isDataTypeIconWide = qsType != 0 && isWide;
+            mInfo.roaming = roaming;
             refreshState(mInfo);
         }
 
@@ -280,6 +296,8 @@
             final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
             if (info == null) return v;
             v.bind(info);
+            v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
+                    ? View.VISIBLE : View.INVISIBLE);
             return v;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 77f063d..0a8afb0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -19,6 +19,7 @@
 import android.content.Intent;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
+import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
@@ -95,6 +96,7 @@
         final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
         final boolean enabled = value != 0;
         state.value = enabled;
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.label = mContext.getString(R.string.quick_settings_inversion_label);
         state.icon = enabled ? mEnable : mDisable;
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 65432dc..412fe3d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -16,15 +16,18 @@
 
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.NetworkController;
 
 public class DataSaverTile extends QSTile<QSTile.BooleanState> implements
         DataSaverController.Listener{
@@ -33,7 +36,7 @@
 
     public DataSaverTile(Host host) {
         super(host);
-        mDataSaverController = host.getNetworkController().getDataSaverController();
+        mDataSaverController = Dependency.get(NetworkController.class).getDataSaverController();
     }
 
     @Override
@@ -96,6 +99,7 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.value = arg instanceof Boolean ? (Boolean) arg
                 : mDataSaverController.isDataSaverEnabled();
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.label = mContext.getString(R.string.data_saver);
         state.contentDescription = state.label;
         state.icon = ResourceIcon.get(state.value ? R.drawable.ic_data_saver
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 198375d..3c1f504 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -25,6 +25,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.quicksettings.Tile;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
@@ -34,9 +35,11 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SysUIToast;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -73,7 +76,7 @@
 
     public DndTile(Host host) {
         super(host);
-        mController = host.getZenModeController();
+        mController = Dependency.get(ZenModeController.class);
         mDetailAdapter = new DndDetailAdapter();
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
         mReceiverRegistered = true;
@@ -121,7 +124,17 @@
     }
 
     @Override
-    public void handleClick() {
+    protected void handleClick() {
+        if (mState.value) {
+            mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
+        } else {
+            int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
+            mController.setZen(zen, null, TAG);
+        }
+    }
+
+    @Override
+    protected void handleSecondaryClick() {
         if (mController.isVolumeRestricted()) {
             // Collapse the panels, so the user can see the toast.
             mHost.collapsePanels();
@@ -131,13 +144,9 @@
             return;
         }
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
-        if (mState.value) {
-            mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
-        } else {
-            showDetail(true);
-            int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
-            mController.setZen(zen, null, TAG);
-        }
+        showDetail(true);
+        int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
+        mController.setZen(zen, null, TAG);
     }
 
     @Override
@@ -151,6 +160,7 @@
         final boolean newValue = zen != Global.ZEN_MODE_OFF;
         final boolean valueChanged = state.value != newValue;
         state.value = newValue;
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
         switch (zen) {
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
@@ -305,7 +315,8 @@
     private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() {
         @Override
         public void onPrioritySettings() {
-            mHost.startActivityDismissingKeyguard(ZEN_PRIORITY_SETTINGS);
+            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
+                    ZEN_PRIORITY_SETTINGS, 0);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 416c7ce..ac82753 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -27,9 +27,9 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.external.TileColorPicker;
 import com.android.systemui.statusbar.policy.FlashlightController;
 
 /** Quick settings tile: Control flashlight **/
@@ -46,7 +46,7 @@
 
     public FlashlightTile(Host host) {
         super(host);
-        mFlashlightController = host.getFlashlightController();
+        mFlashlightController = Dependency.get(FlashlightController.class);
     }
 
     @Override
@@ -107,17 +107,12 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         if (!mFlashlightController.isAvailable()) {
-            Drawable icon = mHost.getContext().getDrawable(R.drawable.ic_signal_flashlight_disable)
+            Drawable icon = mHost.getContext().getDrawable(R.drawable.ic_signal_flashlight_enable)
                     .mutate();
-            final int disabledColor = TileColorPicker.getInstance(mContext)
-                    .getColor(Tile.STATE_UNAVAILABLE);
-            icon.setTint(disabledColor);
             state.icon = new DrawableIcon(icon);
-            state.label = new SpannableStringBuilder().append(state.label,
-                    new ForegroundColorSpan(disabledColor),
-                    SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
             state.contentDescription = mContext.getString(
                     R.string.accessibility_quick_settings_flashlight_unavailable);
+            state.state = Tile.STATE_UNAVAILABLE;
             return;
         }
         if (arg instanceof Boolean) {
@@ -134,6 +129,7 @@
         state.contentDescription = mContext.getString(R.string.quick_settings_flashlight_label);
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
                 = Switch.class.getName();
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 99485bb..70f8109 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -29,10 +29,10 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.qs.GlobalSetting;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.external.TileColorPicker;
 import com.android.systemui.statusbar.policy.HotspotController;
 
 /** Quick settings tile: Hotspot **/
@@ -53,7 +53,7 @@
 
     public HotspotTile(Host host) {
         super(host);
-        mController = host.getHotspotController();
+        mController = Dependency.get(HotspotController.class);
         mAirplaneMode = new GlobalSetting(mContext, mHandler, Global.AIRPLANE_MODE_ON) {
             @Override
             protected void handleValueChanged(int value) {
@@ -126,11 +126,6 @@
         boolean wasAirplane = state.isAirplaneMode;
         state.isAirplaneMode = mAirplaneMode.getValue() != 0;
         if (state.isAirplaneMode) {
-            final int disabledColor = TileColorPicker.getInstance(mContext)
-                    .getColor(Tile.STATE_UNAVAILABLE);
-            state.label = new SpannableStringBuilder().append(state.label,
-                    new ForegroundColorSpan(disabledColor),
-                    SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
             state.icon = mUnavailable;
         } else if (wasAirplane) {
             state.icon = mDisableNoAnimation;
@@ -138,6 +133,8 @@
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
                 = Switch.class.getName();
         state.contentDescription = state.label;
+        state.state = state.isAirplaneMode ? Tile.STATE_UNAVAILABLE
+                : state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index f968816..fcc9596 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -31,6 +31,8 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.qs.QSTile;
 
 import java.util.Arrays;
@@ -105,7 +107,7 @@
         try {
             if (pi != null) {
                 if (pi.isActivity()) {
-                    getHost().startActivityDismissingKeyguard(pi);
+                    Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(pi);
                 } else {
                     pi.send();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 8a9a696..5374f18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -18,13 +18,15 @@
 
 import android.content.Intent;
 import android.os.UserManager;
-
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.LocationController;
@@ -46,8 +48,8 @@
 
     public LocationTile(Host host) {
         super(host);
-        mController = host.getLocationController();
-        mKeyguard = host.getKeyguardMonitor();
+        mController = Dependency.get(LocationController.class);
+        mKeyguard = Dependency.get(KeyguardMonitor.class);
     }
 
     @Override
@@ -74,18 +76,15 @@
     @Override
     protected void handleClick() {
         if (mKeyguard.isSecure() && mKeyguard.isShowing()) {
-            mHost.startRunnableDismissingKeyguard(new Runnable() {
-                @Override
-                public void run() {
-                    final boolean wasEnabled = (Boolean) mState.value;
-                    mHost.openPanels();
-                    MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
-                    mController.setLocationEnabled(!wasEnabled);
-                }
+            Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
+                final boolean wasEnabled = mState.value;
+                mHost.openPanels();
+                MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
+                mController.setLocationEnabled(!wasEnabled);
             });
             return;
         }
-        final boolean wasEnabled = (Boolean) mState.value;
+        final boolean wasEnabled = mState.value;
         MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
         mController.setLocationEnabled(!wasEnabled);
     }
@@ -115,6 +114,7 @@
             state.contentDescription = mContext.getString(
                     R.string.accessibility_quick_settings_location_off);
         }
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
                 = Switch.class.getName();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
new file mode 100644
index 0000000..967c922
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016, The Android Open Source Project
+ * Contributed by the Paranoid Android Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.nfc.NfcAdapter;
+import android.provider.Settings;
+import android.widget.Switch;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+
+/** Quick settings tile: Enable/Disable NFC **/
+public class NfcTile extends QSTile<QSTile.BooleanState> {
+
+    private NfcAdapter mAdapter;
+
+    private boolean mListening;
+
+    public NfcTile(Host host) {
+        super(host);
+    }
+
+    @Override
+    public BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        mListening = listening;
+        if (mListening) {
+            mContext.registerReceiver(mNfcReceiver,
+                    new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED));
+            if (mAdapter == null) {
+                try {
+                    mAdapter = NfcAdapter.getNfcAdapter(mContext);
+                } catch (UnsupportedOperationException e) {
+                    mAdapter = null;
+                }
+            }
+        } else {
+            mContext.unregisterReceiver(mNfcReceiver);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
+    }
+
+    @Override
+    protected void handleUserSwitch(int newUserId) {
+    }
+
+    @Override
+    public Intent getLongClickIntent() {
+        return new Intent(Settings.ACTION_NFC_SETTINGS);
+    }
+
+    @Override
+    protected void handleClick() {
+        if (mAdapter == null) return;
+        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
+        if (!mAdapter.isEnabled()) {
+            mAdapter.enable();
+        } else {
+            mAdapter.disable();
+        }
+    }
+
+    @Override
+    protected void handleSecondaryClick() {
+        handleClick();
+    }
+
+    @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_nfc_label);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled);
+        final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled);
+        state.value = mAdapter == null ? false : mAdapter.isEnabled();
+        state.label = mContext.getString(R.string.quick_settings_nfc_label);
+        state.icon = new DrawableIcon(state.value ? mEnable : mDisable);
+        state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
+                = Switch.class.getName();
+        state.contentDescription = state.label;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.QS_NFC;
+    }
+
+    @Override
+    protected String composeChangeAnnouncement() {
+        if (mState.value) {
+            return mContext.getString(R.string.quick_settings_nfc_on);
+        } else {
+            return mContext.getString(R.string.quick_settings_nfc_off);
+        }
+    }
+
+    private BroadcastReceiver mNfcReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            refreshState();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 10fde30..b8ba28d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.content.Intent;
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
 import com.android.internal.app.NightDisplayController;
@@ -81,6 +82,7 @@
                 : R.drawable.ic_qs_night_display_off);
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName =
                 Switch.class.getName();
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index cec5f15..2c0af17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -21,10 +21,12 @@
 import android.content.res.Configuration;
 
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.RotationLockController;
@@ -50,7 +52,7 @@
 
     public RotationLockTile(Host host) {
         super(host);
-        mController = host.getRotationLockController();
+        mController = Dependency.get(RotationLockController.class);
     }
 
     @Override
@@ -106,6 +108,7 @@
         state.contentDescription = getAccessibilityString(rotationLocked);
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
                 = Switch.class.getName();
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
 
     public static boolean isCurrentOrientationLockPortrait(RotationLockController controller,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index 246c23e..c20c6bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -22,6 +22,7 @@
 import android.util.Pair;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -35,8 +36,8 @@
 
     public UserTile(Host host) {
         super(host);
-        mUserSwitcherController = host.getUserSwitcherController();
-        mUserInfoController = host.getUserInfoController();
+        mUserSwitcherController = Dependency.get(UserSwitcherController.class);
+        mUserInfoController = Dependency.get(UserInfoController.class);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 3876c88..2d61857 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -30,7 +31,9 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.wifi.AccessPoint;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSDetailItems;
 import com.android.systemui.qs.QSDetailItems.Item;
@@ -40,7 +43,7 @@
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.SignalCallbackAdapter;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 import java.util.List;
 
@@ -54,12 +57,14 @@
     private final QSTile.SignalState mStateBeforeClick = newTileState();
 
     protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
+    private final ActivityStarter mActivityStarter;
 
     public WifiTile(Host host) {
         super(host);
-        mController = host.getNetworkController();
+        mController = Dependency.get(NetworkController.class);
         mWifiController = mController.getAccessPointController();
         mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
+        mActivityStarter = Dependency.get(ActivityStarter.class);
     }
 
     @Override
@@ -104,7 +109,7 @@
     }
 
     @Override
-    protected void handleSecondaryClick() {
+    protected void handleClick() {
         // Secondary clicks are header clicks, just toggle.
         mState.copyTo(mStateBeforeClick);
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
@@ -112,9 +117,10 @@
     }
 
     @Override
-    protected void handleClick() {
+    protected void handleSecondaryClick() {
         if (!mWifiController.canConfigWifi()) {
-            mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_WIFI_SETTINGS));
+            mActivityStarter.postStartActivityDismissingKeyguard(
+                    new Intent(Settings.ACTION_WIFI_SETTINGS), 0);
             return;
         }
         showDetail(true);
@@ -191,6 +197,7 @@
         state.dualLabelContentDescription = wifiName;
         state.expandedAccessibilityClassName = Button.class.getName();
         state.minimalAccessibilityClassName = Switch.class.getName();
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
 
     @Override
@@ -249,7 +256,7 @@
         }
     }
 
-    protected final class WifiSignalCallback extends SignalCallbackAdapter {
+    protected final class WifiSignalCallback implements SignalCallback {
         final CallbackInfo mInfo = new CallbackInfo();
 
         @Override
@@ -327,7 +334,7 @@
 
         @Override
         public void onSettingsActivityTriggered(Intent settingsIntent) {
-            mHost.startActivityDismissingKeyguard(settingsIntent);
+            mActivityStarter.postStartActivityDismissingKeyguard(settingsIntent, 0);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index ce7fbd3..ae4d6c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -18,10 +18,12 @@
 
 import android.content.Intent;
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -40,7 +42,7 @@
 
     public WorkModeTile(Host host) {
         super(host);
-        mProfileController = host.getManagedProfileController();
+        mProfileController = Dependency.get(ManagedProfileController.class);
     }
 
     @Override
@@ -108,6 +110,7 @@
         }
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
                 = Switch.class.getName();
+        state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0265c9e..8d18a75 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -438,7 +438,7 @@
                 ActivityManager.StackId.isHomeOrRecentsStack(runningTask.stackId);
         if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
             logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
-            if (runningTask.isDockable) {
+            if (runningTask.supportsSplitScreenMultiWindow) {
                 if (metricsDockAction != -1) {
                     MetricsLogger.action(mContext, metricsDockAction,
                             runningTask.topActivity.flattenToShortString());
@@ -486,7 +486,6 @@
             case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
                 return COUNTER_WINDOW_UNSUPPORTED;
             case ActivityInfo.RESIZE_MODE_RESIZEABLE:
-            case ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
             case ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION:
                 return COUNTER_WINDOW_SUPPORTED;
             default:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d5a6a58..06fadd1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -76,6 +76,8 @@
 import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
 import com.android.systemui.recents.misc.DozeTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
@@ -599,13 +601,12 @@
                 }
                 return true;
             }
-            case KeyEvent.KEYCODE_DPAD_UP: {
-                EventBus.getDefault().send(
-                        new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
-                return true;
-            }
-            case KeyEvent.KEYCODE_DPAD_DOWN: {
-                EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT: {
+                final Direction direction = NavigateTaskViewEvent.getDirectionFromKeyCode(keyCode);
+                EventBus.getDefault().send(new NavigateTaskViewEvent(direction));
                 return true;
             }
             case KeyEvent.KEYCODE_DEL:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 914035b..a7f6b70 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -50,7 +50,7 @@
     /**
      * Returns the task to focus given the current launch state.
      */
-    public int getInitialFocusTaskIndex(int numTasks) {
+    public int getInitialFocusTaskIndex(int numTasks, boolean useGridLayout) {
         RecentsDebugFlags debugFlags = Recents.getDebugFlags();
         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
         if (launchedFromApp) {
@@ -66,6 +66,11 @@
                 return numTasks - 1;
             }
 
+            if (useGridLayout) {
+                // If coming from another app to the grid layout, focus the front most task
+                return numTasks - 1;
+            }
+
             // If coming from another app, focus the next task
             return Math.max(0, numTasks - 2);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 711f0c6..ff8d4bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -17,12 +17,33 @@
 package com.android.systemui.recents;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
 
 import android.os.SystemProperties;
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.TaskStack;
+
+/**
+ * Represents the dock regions for each orientation.
+ */
+class DockRegion {
+    public static TaskStack.DockState[] PHONE_LANDSCAPE = {
+            // We only allow docking to the left in landscape for now on small devices
+            TaskStack.DockState.LEFT
+    };
+    public static TaskStack.DockState[] PHONE_PORTRAIT = {
+            // We only allow docking to the top for now on small devices
+            TaskStack.DockState.TOP
+    };
+    public static TaskStack.DockState[] TABLET_LANDSCAPE = {
+            TaskStack.DockState.LEFT,
+            TaskStack.DockState.RIGHT
+    };
+    public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
+}
 
 /**
  * Application resources that can be retrieved from the application context and are not specifically
@@ -63,12 +84,14 @@
     // Recents will layout task views in a grid mode when there's enough space in the screen.
     public boolean isGridEnabled;
 
+    private final Context mAppContext;
+
     public RecentsConfiguration(Context context) {
         // Load only resources that can not change after the first load either through developer
         // settings or via multi window
         SystemServicesProxy ssp = Recents.getSystemServices();
-        Context appContext = context.getApplicationContext();
-        Resources res = appContext.getResources();
+        mAppContext = context.getApplicationContext();
+        Resources res = mAppContext.getResources();
         fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
         svelteLevel = res.getInteger(R.integer.recents_svelte_level);
         isGridEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
@@ -86,4 +109,20 @@
     public RecentsActivityLaunchState getLaunchState() {
         return mLaunchState;
     }
+
+    /**
+     * Returns the preferred dock states for the current orientation.
+     * @return a list of dock states for device and its orientation
+     */
+    public TaskStack.DockState[] getDockStatesForCurrentOrientation() {
+        boolean isLandscape = mAppContext.getResources().getConfiguration().orientation ==
+                Configuration.ORIENTATION_LANDSCAPE;
+        RecentsConfiguration config = Recents.getConfiguration();
+        if (config.isLargeScreen) {
+            return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
+        } else {
+            return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
+        }
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7547bc3..cf6357b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -23,6 +23,7 @@
 import static android.view.View.MeasureSpec;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -60,6 +61,7 @@
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
 import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
+import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
 import com.android.systemui.recents.misc.DozeTrigger;
 import com.android.systemui.recents.misc.ForegroundThread;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -131,6 +133,11 @@
                 loader.loadTasks(mContext, plan, launchOpts);
             }
         }
+
+        @Override
+        public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+            EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, snapshot));
+        }
     }
 
     protected static RecentsTaskLoadPlan sInstanceLoadPlan;
@@ -204,8 +211,6 @@
         Resources res = mContext.getResources();
         reloadResources();
         mDummyStackView.reloadOnConfigurationChange();
-        mDummyStackView.getStackAlgorithm().getGridState().setHasDockedTasks(
-            Recents.getSystemServices().hasDockedTask());
     }
 
     /**
@@ -740,8 +745,12 @@
             Task toTask = new Task();
             TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask,
                     windowOverrideRect);
-            Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform,
-                    mThumbTransitionBitmapCache);
+            // When using a grid layout, the header is already visible on screen at the target
+            // location, making it unnecessary to draw it in the transition thumbnail.
+            Bitmap thumbnail = stackView.useGridLayout()
+                    ? mThumbTransitionBitmapCache.createAshmemBitmap()
+                    : drawThumbnailTransitionBitmap(toTask, toTransform,
+                            mThumbTransitionBitmapCache);
             if (thumbnail != null) {
                 RectF toTaskRect = toTransform.rect;
                 return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
@@ -772,7 +781,6 @@
         // Get the transform for the running task
         stackView.updateLayoutAlgorithm(true /* boundScroll */);
         stackView.updateToInitialState();
-        boolean isInSplitScreen = Recents.getSystemServices().hasDockedTask();
         stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
                 stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
         return mTmpTransform;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
new file mode 100644
index 0000000..07c3b3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import android.app.ActivityManager.TaskSnapshot;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Sent when a task snapshot has changed.
+ */
+public class TaskSnapshotChangedEvent extends EventBus.Event {
+
+    public final int taskId;
+    public final TaskSnapshot taskSnapshot;
+
+    public TaskSnapshotChangedEvent(int taskId, TaskSnapshot taskSnapshot) {
+        this.taskId = taskId;
+        this.taskSnapshot = taskSnapshot;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
index b368bd3..e57fa2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
@@ -30,10 +30,17 @@
     public final Task task;
     public final TaskView taskView;
     public final Point tlOffset;
+    public final boolean isUserTouchInitiated;
 
     public DragStartEvent(Task task, TaskView taskView, Point tlOffset) {
+        this(task, taskView, tlOffset, true);
+    }
+
+    public DragStartEvent(Task task, TaskView taskView, Point tlOffset,
+            boolean isUserTouchInitiated) {
         this.task = task;
         this.taskView = taskView;
         this.tlOffset = tlOffset;
+        this.isUserTouchInitiated = isUserTouchInitiated;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
new file mode 100644
index 0000000..5508d26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui.focus;
+
+import android.view.KeyEvent;
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Navigates the task view by arrow keys.
+ */
+public class NavigateTaskViewEvent extends EventBus.Event {
+    public enum Direction {
+        UNDEFINED, UP, DOWN, LEFT, RIGHT;
+    }
+
+    public Direction direction;
+    public NavigateTaskViewEvent(Direction direction) {
+        this.direction = direction;
+    }
+
+    public static Direction getDirectionFromKeyCode(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_UP:
+                return Direction.UP;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                return Direction.DOWN;
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+                return Direction.LEFT;
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                return Direction.RIGHT;
+            default:
+                return Direction.UNDEFINED;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 3587b89..49074a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -24,7 +24,9 @@
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
@@ -77,6 +79,7 @@
 
 import com.android.internal.app.AssistUtils;
 import com.android.internal.os.BackgroundThread;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.pip.tv.PipMenuActivity;
 import com.android.systemui.pip.tv.PipOnboardingActivity;
@@ -148,12 +151,32 @@
      */
     public abstract static class TaskStackListener {
         public void onTaskStackChanged() { }
+        public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
         public void onActivityPinned() { }
-        public void onPinnedActivityRestartAttempt() { }
+        public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) { }
         public void onPinnedStackAnimationEnded() { }
         public void onActivityForcedResizable(String packageName, int taskId) { }
         public void onActivityDismissingDockedStack() { }
         public void onTaskProfileLocked(int taskId, int userId) { }
+
+        /**
+         * Checks that the current user matches the user's SystemUI process. Since
+         * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
+         * TaskStackListener should make this call to verify that we don't act on events from other
+         * user's processes.
+         */
+        protected final boolean checkCurrentUserId(boolean debug) {
+            int processUserId = UserHandle.myUserId();
+            int currentUserId = KeyguardUpdateMonitor.getCurrentUser();
+            if (processUserId != currentUserId) {
+                if (debug) {
+                    Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId
+                            + " and the current user is uid=" + currentUserId);
+                }
+                return false;
+            }
+            return true;
+        }
     }
 
     /**
@@ -175,9 +198,11 @@
         }
 
         @Override
-        public void onPinnedActivityRestartAttempt() throws RemoteException{
+        public void onPinnedActivityRestartAttempt(ComponentName sourceComponent)
+                throws RemoteException{
             mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
-            mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+            mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, sourceComponent)
+                    .sendToTarget();
         }
 
         @Override
@@ -202,6 +227,12 @@
         public void onTaskProfileLocked(int taskId, int userId) {
             mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
         }
+
+        @Override
+        public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
+                throws RemoteException {
+            mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
+        }
     };
 
     /**
@@ -591,17 +622,17 @@
     /** Returns the top task thumbnail for the given task id */
     public ThumbnailData getTaskThumbnail(int taskId) {
         if (mAm == null) return null;
-        ThumbnailData thumbnailData = new ThumbnailData();
 
         // If we are mocking, then just return a dummy thumbnail
         if (RecentsDebugFlags.Static.EnableMockTasks) {
+            ThumbnailData thumbnailData = new ThumbnailData();
             thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
                     mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
             thumbnailData.thumbnail.eraseColor(0xff333333);
             return thumbnailData;
         }
 
-        getThumbnail(taskId, thumbnailData);
+        ThumbnailData thumbnailData = getThumbnail(taskId);
         if (thumbnailData.thumbnail != null && !ActivityManager.ENABLE_TASK_SNAPSHOTS) {
             thumbnailData.thumbnail.setHasAlpha(false);
             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
@@ -621,11 +652,12 @@
     /**
      * Returns a task thumbnail from the activity manager
      */
-    public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
+    public @NonNull ThumbnailData getThumbnail(int taskId) {
         if (mAm == null) {
-            return;
+            return new ThumbnailData();
         }
 
+        final ThumbnailData thumbnailData;
         if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
             ActivityManager.TaskSnapshot snapshot = null;
             try {
@@ -634,16 +666,14 @@
                 Log.w(TAG, "Failed to retrieve snapshot", e);
             }
             if (snapshot != null) {
-                thumbnailDataOut.thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot());
-                thumbnailDataOut.orientation = snapshot.getOrientation();
-                thumbnailDataOut.insets.set(snapshot.getContentInsets());
+                thumbnailData = ThumbnailData.createFromTaskSnapshot(snapshot);
             } else {
-                thumbnailDataOut.thumbnail = null;
+                return new ThumbnailData();
             }
         } else {
             ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
             if (taskThumbnail == null) {
-                return;
+                return new ThumbnailData();
             }
 
             Bitmap thumbnail = taskThumbnail.mainThumbnail;
@@ -658,10 +688,12 @@
                 } catch (IOException e) {
                 }
             }
-            thumbnailDataOut.thumbnail = thumbnail;
-            thumbnailDataOut.orientation = taskThumbnail.thumbnailInfo.screenOrientation;
-            thumbnailDataOut.insets.setEmpty();
+            thumbnailData = new ThumbnailData();
+            thumbnailData.thumbnail = thumbnail;
+            thumbnailData.orientation = taskThumbnail.thumbnailInfo.screenOrientation;
+            thumbnailData.insets.setEmpty();
         }
+        return thumbnailData;
     }
 
     /**
@@ -1172,12 +1204,13 @@
 
     private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
-        private static final int ON_ACTIVITY_PINNED = 2;
-        private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
-        private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
-        private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
-        private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6;
-        private static final int ON_TASK_PROFILE_LOCKED = 7;
+        private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
+        private static final int ON_ACTIVITY_PINNED = 3;
+        private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
+        private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
+        private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
+        private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
+        private static final int ON_TASK_PROFILE_LOCKED = 8;
 
         @Override
         public void handleMessage(Message msg) {
@@ -1188,6 +1221,13 @@
                     }
                     break;
                 }
+                case ON_TASK_SNAPSHOT_CHANGED: {
+                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                        mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
+                                (TaskSnapshot) msg.obj);
+                    }
+                    break;
+                }
                 case ON_ACTIVITY_PINNED: {
                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                         mTaskStackListeners.get(i).onActivityPinned();
@@ -1196,7 +1236,8 @@
                 }
                 case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                        mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
+                        mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
+                                (ComponentName) msg.obj);
                     }
                     break;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 5c7496d..11b5984 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -197,7 +197,7 @@
             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
                     thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
                     activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
-                    t.isDockable, t.bounds, t.taskDescription, t.resizeMode, t.topActivity,
+                    t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode, t.topActivity,
                     isLocked);
 
             allTasks.add(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 6ea51e5..ededf96 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -209,7 +209,8 @@
                             // When svelte, we trim the memory to just the visible thumbnails when
                             // leaving, so don't thrash the cache as the user scrolls (just load
                             // them from scratch each time)
-                            if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) {
+                            if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE
+                                    && !ActivityManager.ENABLE_TASK_SNAPSHOTS) {
                                 mThumbnailCache.put(t.key, cachedThumbnailData);
                             }
                         }
@@ -553,7 +554,9 @@
                 // Load the thumbnail from the system
                 thumbnailData = ssp.getTaskThumbnail(taskKey.id);
                 if (thumbnailData.thumbnail != null) {
-                    mThumbnailCache.put(taskKey, thumbnailData);
+                    if (!ActivityManager.ENABLE_TASK_SNAPSHOTS) {
+                        mThumbnailCache.put(taskKey, thumbnailData);
+                    }
                     return thumbnailData.thumbnail;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 178cb9f..9b25ef8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -247,6 +247,9 @@
      */
     public static class DockState implements DropTarget {
 
+        public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
+        public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
+
         // The rotation to apply to the hint text
         @Retention(RetentionPolicy.SOURCE)
         @IntDef({HORIZONTAL, VERTICAL})
@@ -319,7 +322,8 @@
             private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
                     int hintTextResId) {
                 dockAreaAlpha = areaAlpha;
-                dockAreaOverlay = new ColorDrawable(0xFFffffff);
+                dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled
+                        ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
                 dockAreaOverlay.setAlpha(0);
                 hintTextAlpha = hintAlpha;
                 hintTextOrientation = hintOrientation;
@@ -435,7 +439,7 @@
          * @param createMode used to pass to ActivityManager to dock the task
          * @param touchArea the area in which touch will initiate this dock state
          * @param dockArea the visible dock area
-         * @param expandedTouchDockArea the areain which touch will continue to dock after entering
+         * @param expandedTouchDockArea the area in which touch will continue to dock after entering
          *                              the initial touch area.  This is also the new dock area to
          *                              draw.
          */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
index 18735ac..09a3712 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.model;
 
+import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 
@@ -23,7 +24,17 @@
  * Data for a single thumbnail.
  */
 public class ThumbnailData {
+
+    // TODO: Make these final once the non-snapshot path is removed.
     public Bitmap thumbnail;
     public int orientation;
     public final Rect insets = new Rect();
+
+    public static ThumbnailData createFromTaskSnapshot(TaskSnapshot snapshot) {
+        ThumbnailData out = new ThumbnailData();
+        out.thumbnail = Bitmap.createHardwareBitmap(snapshot.getSnapshot());
+        out.insets.set(snapshot.getContentInsets());
+        out.orientation = snapshot.getOrientation();
+        return out;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
index d64a676..79a774f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
@@ -30,6 +30,7 @@
 import android.util.Log;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
 
 /**
@@ -90,8 +91,10 @@
         mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
         mCornerShadowPaint.setStyle(Paint.Style.FILL);
         mCornerShadowPaint.setDither(true);
-        mCornerRadius = resources.getDimensionPixelSize(
-                R.dimen.recents_task_view_rounded_corners_radius);
+        mCornerRadius = Recents.getConfiguration().isGridEnabled ?
+                resources.getDimensionPixelSize(
+                    R.dimen.recents_grid_task_view_rounded_corners_radius) :
+                resources.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
         mCardBounds = new RectF();
         mEdgeShadowPaint = new Paint(mCornerShadowPaint);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index ee79330..8f9c457 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -454,7 +454,7 @@
     }
 
     public final void onBusEvent(DragStartEvent event) {
-        updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
+        updateVisibleDockRegions(Recents.getConfiguration().getDockStatesForCurrentOrientation(),
                 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
                 TaskStack.DockState.NONE.viewState.hintTextAlpha,
                 true /* animateAlpha */, false /* animateBounds */);
@@ -471,7 +471,8 @@
 
     public final void onBusEvent(DragDropTargetChangedEvent event) {
         if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) {
-            updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
+            updateVisibleDockRegions(
+                    Recents.getConfiguration().getDockStatesForCurrentOrientation(),
                     true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
                     TaskStack.DockState.NONE.viewState.hintTextAlpha,
                     true /* animateAlpha */, true /* animateBounds */);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index d3ec984..db0c95e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -28,7 +28,6 @@
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
 import com.android.systemui.recents.events.activity.HideRecentsEvent;
@@ -45,25 +44,6 @@
 import java.util.ArrayList;
 
 /**
- * Represents the dock regions for each orientation.
- */
-class DockRegion {
-    public static TaskStack.DockState[] PHONE_LANDSCAPE = {
-            // We only allow docking to the left in landscape for now on small devices
-            TaskStack.DockState.LEFT
-    };
-    public static TaskStack.DockState[] PHONE_PORTRAIT = {
-            // We only allow docking to the top for now on small devices
-            TaskStack.DockState.TOP
-    };
-    public static TaskStack.DockState[] TABLET_LANDSCAPE = {
-            TaskStack.DockState.LEFT,
-            TaskStack.DockState.RIGHT
-    };
-    public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
-}
-
-/**
  * Handles touch events for a RecentsView.
  */
 public class RecentsViewTouchHandler {
@@ -111,20 +91,6 @@
     }
 
     /**
-     * Returns the preferred dock states for the current orientation.
-     */
-    public TaskStack.DockState[] getDockStatesForCurrentOrientation() {
-        boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
-                Configuration.ORIENTATION_LANDSCAPE;
-        RecentsConfiguration config = Recents.getConfiguration();
-        if (config.isLargeScreen) {
-            return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
-        } else {
-            return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
-        }
-    }
-
-    /**
      * Returns the set of visible dock states for this current drag.
      */
     public ArrayList<TaskStack.DockState> getVisibleDockStates() {
@@ -162,10 +128,14 @@
         mRv.getLocationInWindow(recentsViewLocation);
         mTaskViewOffset.set(mTaskView.getLeft() - recentsViewLocation[0] + event.tlOffset.x,
                 mTaskView.getTop() - recentsViewLocation[1] + event.tlOffset.y);
-        float x = mDownPos.x - mTaskViewOffset.x;
-        float y = mDownPos.y - mTaskViewOffset.y;
-        mTaskView.setTranslationX(x);
-        mTaskView.setTranslationY(y);
+
+        // Change space coordinates relative to the view to RecentsView when user initiates a touch
+        if (event.isUserTouchInitiated) {
+            float x = mDownPos.x - mTaskViewOffset.x;
+            float y = mDownPos.y - mTaskViewOffset.y;
+            mTaskView.setTranslationX(x);
+            mTaskView.setTranslationY(y);
+        }
 
         mVisibleDockStates.clear();
         if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()
@@ -176,7 +146,8 @@
                 EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
             } else {
                 // Add the dock state drop targets (these take priority)
-                TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
+                TaskStack.DockState[] dockStates = Recents.getConfiguration()
+                        .getDockStatesForCurrentOrientation();
                 for (TaskStack.DockState dockState : dockStates) {
                     registerDropTargetForCurrentDrag(dockState);
                     dockState.update(mRv.getContext());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index ed86d4c..a2ee4c5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -105,6 +105,7 @@
     private static final Interpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
             Interpolators.LINEAR_OUT_SLOW_IN;
 
+    private final int mEnterAndExitFromHomeTranslationOffset;
     private TaskStackView mStackView;
 
     private TaskViewTransform mTmpTransform = new TaskViewTransform();
@@ -113,6 +114,8 @@
 
     public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
         mStackView = stackView;
+        mEnterAndExitFromHomeTranslationOffset = Recents.getConfiguration().isGridEnabled
+                ? 0 : DOUBLE_FRAME_OFFSET_MS;
     }
 
     /**
@@ -260,7 +263,7 @@
                 AnimationProps taskAnimation = new AnimationProps()
                         .setInitialPlayTime(AnimationProps.BOUNDS,
                                 Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
-                                        DOUBLE_FRAME_OFFSET_MS)
+                                        mEnterAndExitFromHomeTranslationOffset)
                         .setStartDelay(AnimationProps.ALPHA,
                                 Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
                                         FRAME_OFFSET_MS)
@@ -321,7 +324,7 @@
             AnimationProps taskAnimation;
             if (animated) {
                 int delay = Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS , taskIndexFromFront) *
-                        DOUBLE_FRAME_OFFSET_MS;
+                        mEnterAndExitFromHomeTranslationOffset;
                 taskAnimation = new AnimationProps()
                         .setStartDelay(AnimationProps.BOUNDS, delay)
                         .setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 3499dfd..4fa7ecb5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -213,38 +213,10 @@
     }
 
     /**
-     * The state telling the algorithm whether to use grid layout or not.
+     * @return True if we should use the grid layout.
      */
-    public static class GridState {
-        private boolean mDraggingOverDockedState;
-        private boolean mHasDockedTask;
-
-        private GridState() {
-            mDraggingOverDockedState = false;
-            mHasDockedTask = false;
-        }
-
-        /**
-         * Check whether we should use the grid layout.
-         * We use the grid layout for Recents iff all the following is true:
-         *  1. Grid-mode is enabled.
-         *  2. The activity is not in split screen mode (there's no docked task).
-         *  3. The user is not dragging a task view over the dock state.
-         * @return True if we should use the grid layout.
-         */
-        boolean useGridLayout() {
-            return Recents.getConfiguration().isGridEnabled &&
-                !mDraggingOverDockedState &&
-                !mHasDockedTask;
-        }
-
-        public void setDragging(boolean draggingOverDockedState) {
-            mDraggingOverDockedState = draggingOverDockedState;
-        }
-
-        public void setHasDockedTasks(boolean hasDockedTask) {
-            mHasDockedTask = hasDockedTask;
-        }
+    boolean useGridLayout() {
+        return Recents.getConfiguration().isGridEnabled;
     }
 
     // A report of the visibility state of the stack
@@ -261,7 +233,6 @@
 
     Context mContext;
     private StackState mState = StackState.SPLIT;
-    private GridState mGridState = new GridState();
     private TaskStackLayoutAlgorithmCallbacks mCb;
 
     // The task bounds (untransformed) for layout.  This rect is anchored at mTaskRoot.
@@ -320,6 +291,9 @@
     @ViewDebug.ExportedProperty(category="recents")
     private int mStackBottomOffset;
 
+    /** The height, in pixels, of each task view's title bar. */
+    private int mTitleBarHeight;
+
     // The paths defining the motion of the tasks when the stack is focused and unfocused
     private Path mUnfocusedCurve;
     private Path mFocusedCurve;
@@ -432,6 +406,14 @@
         mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
         mFreeformStackGap =
                 res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin);
+        mTitleBarHeight = getDimensionForDevice(mContext,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_grid_task_view_header_height);
     }
 
     /**
@@ -516,7 +498,7 @@
         }
 
         // Initialize the grid layout
-        mTaskGridLayoutAlgorithm.initialize(displayRect, windowRect);
+        mTaskGridLayoutAlgorithm.initialize(windowRect);
     }
 
     /**
@@ -769,7 +751,7 @@
     }
 
     public Rect getStackActionButtonRect() {
-        return mGridState.useGridLayout()
+        return useGridLayout()
                 ? mTaskGridLayoutAlgorithm.getStackActionButtonRect() : mStackActionButtonRect;
     }
 
@@ -795,13 +777,6 @@
     }
 
     /**
-     * Returns the current grid layout state.
-     */
-    public GridState getGridState() {
-        return mGridState;
-    }
-
-    /**
      * Returns whether this stack layout has been initialized.
      */
     public boolean isInitialized() {
@@ -902,7 +877,7 @@
         if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
             mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
             return transformOut;
-        } else if (mGridState.useGridLayout()) {
+        } else if (useGridLayout()) {
             int taskIndex = mTaskIndexMap.get(task.key.id);
             int taskCount = mTaskIndexMap.size();
             mTaskGridLayoutAlgorithm.getTransform(taskIndex, taskCount, transformOut, this);
@@ -939,12 +914,17 @@
      * Transforms the given {@param transformOut} to the screen coordinates, overriding the current
      * window rectangle with {@param windowOverrideRect} if non-null.
      */
-    public TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut,
+    TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut,
             Rect windowOverrideRect) {
         Rect windowRect = windowOverrideRect != null
                 ? windowOverrideRect
                 : Recents.getSystemServices().getWindowRect();
         transformOut.rect.offset(windowRect.left, windowRect.top);
+        if (useGridLayout()) {
+            // Draw the thumbnail a little lower to perfectly coincide with the view we are
+            // transitioning to, where the header bar has already been drawn.
+            transformOut.rect.offset(0, mTitleBarHeight);
+        }
         return transformOut;
     }
 
@@ -1323,7 +1303,7 @@
      * Returns the proper task rectangle according to the current grid state.
      */
     public Rect getTaskRect() {
-        return mGridState.useGridLayout() ? mTaskGridLayoutAlgorithm.getTaskGridRect() : mTaskRect;
+        return useGridLayout() ? mTaskGridLayoutAlgorithm.getTaskGridRect() : mTaskRect;
     }
 
     public void dump(String prefix, PrintWriter writer) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 326a280..8ae7a83 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -70,6 +70,7 @@
 import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
 import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
@@ -86,6 +87,7 @@
 import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
 import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
 import com.android.systemui.recents.misc.DozeTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
@@ -93,6 +95,7 @@
 import com.android.systemui.recents.model.TaskStack;
 
 import com.android.systemui.recents.views.grid.GridTaskView;
+import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -206,6 +209,10 @@
     private int mLastWidth;
     private int mLastHeight;
 
+    // We keep track of the task view focused by user interaction and draw a frame around it in the
+    // grid layout.
+    private TaskViewFocusFrame mTaskViewFocusFrame;
+
     // A convenience update listener to request updating clipping of tasks
     private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
             new ValueAnimator.AnimatorUpdateListener() {
@@ -259,12 +266,21 @@
         mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
         mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
         mAnimationHelper = new TaskStackAnimationHelper(context, this);
-        mTaskCornerRadiusPx = res.getDimensionPixelSize(
-                R.dimen.recents_task_view_rounded_corners_radius);
+        mTaskCornerRadiusPx = Recents.getConfiguration().isGridEnabled ?
+                res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
+                res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
         mDividerSize = ssp.getDockedDividerSize(context);
         mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
         mDisplayRect = ssp.getDisplayRect();
 
+        // Create a frame to draw around the focused task view
+        if (Recents.getConfiguration().isGridEnabled) {
+            mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
+                mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
+            addView(mTaskViewFocusFrame);
+            getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
+        }
+
         int taskBarDismissDozeDelaySeconds = getResources().getInteger(
                 R.integer.recents_task_bar_dismiss_delay_seconds);
         mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
@@ -878,7 +894,7 @@
      *
      * @return whether or not the stack will scroll as a part of this focus change
      */
-    private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
+    public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
             final boolean requestViewFocus) {
         return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
     }
@@ -888,7 +904,7 @@
      *
      * @return whether or not the stack will scroll as a part of this focus change
      */
-    private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
+    public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
             boolean requestViewFocus, int timerIndicatorDuration) {
         // Find the next task to focus
         int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
@@ -940,6 +956,10 @@
                     newFocusedTaskView.setFocusedState(true, requestViewFocus);
                 }
             }
+            // Any time a task view gets the focus, we move the focus frame around it.
+            if (mTaskViewFocusFrame != null) {
+                mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
+            }
         }
         return willScroll;
     }
@@ -1005,20 +1025,28 @@
             float stackScroll = mStackScroller.getStackScroll();
             ArrayList<Task> tasks = mStack.getStackTasks();
             int taskCount = tasks.size();
-            if (forward) {
-                // Walk backwards and focus the next task smaller than the current stack scroll
-                for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
-                    float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
-                    if (Float.compare(taskP, stackScroll) <= 0) {
-                        break;
-                    }
-                }
+            if (useGridLayout()) {
+                // For the grid layout, we directly set focus to the most recently used task
+                // no matter we're moving forwards or backwards.
+                newIndex = taskCount - 1;
             } else {
-                // Walk forwards and focus the next task larger than the current stack scroll
-                for (newIndex = 0; newIndex < taskCount; newIndex++) {
-                    float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
-                    if (Float.compare(taskP, stackScroll) >= 0) {
-                        break;
+                // For the grid layout we pick a proper task to focus, according to the current
+                // stack scroll.
+                if (forward) {
+                    // Walk backwards and focus the next task smaller than the current stack scroll
+                    for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
+                        float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+                        if (Float.compare(taskP, stackScroll) <= 0) {
+                            break;
+                        }
+                    }
+                } else {
+                    // Walk forwards and focus the next task larger than the current stack scroll
+                    for (newIndex = 0; newIndex < taskCount; newIndex++) {
+                        float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+                        if (Float.compare(taskP, stackScroll) >= 0) {
+                            break;
+                        }
                     }
                 }
             }
@@ -1037,20 +1065,23 @@
     /**
      * Resets the focused task.
      */
-    void resetFocusedTask(Task task) {
+    public void resetFocusedTask(Task task) {
         if (task != null) {
             TaskView tv = getChildViewForTask(task);
             if (tv != null) {
                 tv.setFocusedState(false, false /* requestViewFocus */);
             }
         }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+        }
         mFocusedTask = null;
     }
 
     /**
      * Returns the focused task.
      */
-    Task getFocusedTask() {
+    public Task getFocusedTask() {
         return mFocusedTask;
     }
 
@@ -1253,6 +1284,9 @@
         for (int i = 0; i < taskViewCount; i++) {
             measureTaskView(mTmpTaskViews.get(i));
         }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.measure();
+        }
 
         setMeasuredDimension(width, height);
         mLastWidth = width;
@@ -1287,6 +1321,9 @@
         for (int i = 0; i < taskViewCount; i++) {
             layoutTaskView(changed, mTmpTaskViews.get(i));
         }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.layout();
+        }
 
         if (changed) {
             if (mStackScroller.isScrollOutOfBounds()) {
@@ -1339,10 +1376,19 @@
         // until after the enter-animation
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
-        int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
-        if (focusedTaskIndex != -1) {
-            setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
-                    false /* requestViewFocus */);
+
+        // We set the initial focused task view iff the following conditions are satisfied:
+        // 1. Recents is showing task views in stack layout.
+        // 2. Recents is launched with ALT + TAB.
+        boolean setFocusOnFirstLayout = !useGridLayout() ||
+            Recents.getConfiguration().getLaunchState().launchedWithAltTab;
+        if (setFocusOnFirstLayout) {
+            int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount(),
+                useGridLayout());
+            if (focusedTaskIndex != -1) {
+                setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
+                        false /* requestViewFocus */);
+            }
         }
         updateStackActionButtonVisibility();
     }
@@ -1443,6 +1489,11 @@
         // Remove the task from the ignored set
         removeIgnoreTask(removedTask);
 
+        // Resize the grid layout task view focus frame
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.resize();
+        }
+
         // If requested, relayout with the given animation
         if (animation != null) {
             updateLayoutAlgorithm(true /* boundScroll */);
@@ -1740,10 +1791,18 @@
         int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
         animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
                 Interpolators.FAST_OUT_SLOW_IN));
+
+        // Dismiss the grid task view focus frame
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+        }
     }
 
     public final void onBusEvent(DismissFocusedTaskViewEvent event) {
         if (mFocusedTask != null) {
+            if (mTaskViewFocusFrame != null) {
+                mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+            }
             TaskView tv = getChildViewForTask(mFocusedTask);
             if (tv != null) {
                 tv.dismissTask();
@@ -1812,6 +1871,26 @@
         setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
     }
 
+    public final void onBusEvent(NavigateTaskViewEvent event) {
+        if (useGridLayout()) {
+            final int taskCount = mStack.getTaskCount();
+            final int currentIndex = mStack.indexOfStackTask(getFocusedTask());
+            final int nextIndex = mLayoutAlgorithm.mTaskGridLayoutAlgorithm.navigateFocus(taskCount,
+                    currentIndex, event.direction);
+            setFocusedTask(nextIndex, false, true);
+        } else {
+            switch (event.direction) {
+                case UP:
+                    EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
+                    break;
+                case DOWN:
+                    EventBus.getDefault().send(
+                        new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
+                    break;
+            }
+        }
+    }
+
     public final void onBusEvent(UserInteractionEvent event) {
         // Poke the doze trigger on user interaction
         mUIDozeTrigger.poke();
@@ -1858,7 +1937,6 @@
                 Interpolators.FAST_OUT_SLOW_IN);
         boolean ignoreTaskOverrides = false;
         if (event.dropTarget instanceof TaskStack.DockState) {
-            mLayoutAlgorithm.getGridState().setDragging(true);
             // Calculate the new task stack bounds that matches the window size that Recents will
             // have after the drop
             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
@@ -1878,7 +1956,6 @@
             updateLayoutAlgorithm(true /* boundScroll */);
             ignoreTaskOverrides = true;
         } else {
-            mLayoutAlgorithm.getGridState().setDragging(false);
             // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
             // task view, so add it back to the ignore set after updating the layout
             removeIgnoreTask(event.task);
@@ -1889,7 +1966,6 @@
     }
 
     public final void onBusEvent(final DragEndEvent event) {
-        mLayoutAlgorithm.getGridState().setDragging(false);
         // We don't handle drops on the dock regions
         if (event.dropTarget instanceof TaskStack.DockState) {
             // However, we do need to reset the overrides, since the last state of this task stack
@@ -2076,13 +2152,17 @@
         mResetToInitialStateWhenResized = true;
     }
 
+    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+        if (!event.visible && mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+        }
+    }
+
     public void reloadOnConfigurationChange() {
         mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
         mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
 
         boolean hasDockedTask = Recents.getSystemServices().hasDockedTask();
-        mStableLayoutAlgorithm.getGridState().setHasDockedTasks(hasDockedTask);
-        mLayoutAlgorithm.getGridState().setHasDockedTasks(hasDockedTask);
     }
 
     /**
@@ -2139,7 +2219,7 @@
      * Check whether we should use the grid layout.
      */
     public boolean useGridLayout() {
-        return mLayoutAlgorithm.getGridState().useGridLayout();
+        return mLayoutAlgorithm.useGridLayout();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 33fa3b0..5817e92 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -342,8 +342,9 @@
                         mSv.invalidate();
                     }
 
-                    // Reset the focused task after the user has scrolled
-                    if (!mSv.mTouchExplorationEnabled) {
+                    // Reset the focused task after the user has scrolled, but we have no scrolling
+                    // in grid layout and therefore we don't want to reset the focus there.
+                    if (!mSv.mTouchExplorationEnabled && !mSv.useGridLayout()) {
                         mSv.resetFocusedTask(mSv.getFocusedTask());
                     }
                 } else if (mActiveTaskView == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 5f37349..e941c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,7 +22,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Outline;
@@ -184,6 +183,7 @@
         }
         setOutlineProvider(mViewBounds);
         setOnLongClickListener(this);
+        setAccessibilityDelegate(new TaskViewAccessibilityDelegate(this));
     }
 
     /** Set callback */
@@ -258,6 +258,11 @@
     }
 
     @Override
+    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
+        // Prevent any children from being focusable during talkback
+    }
+
+    @Override
     public boolean hasOverlappingRendering() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
new file mode 100644
index 0000000..759daf1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.recents.views;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.TaskStack;
+
+public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
+    private static final String TAG = "TaskViewAccessibilityDelegate";
+
+    private final TaskView mTaskView;
+
+    protected static final int OPEN = R.id.action_open;
+    protected static final int DIMISS = R.id.action_dimiss;
+    protected static final int SPLIT_TASK_TOP = R.id.action_split_task_to_top;
+    protected static final int SPLIT_TASK_LEFT = R.id.action_split_task_to_left;
+    protected static final int SPLIT_TASK_RIGHT = R.id.action_split_task_to_right;
+
+    protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
+
+    public TaskViewAccessibilityDelegate(TaskView taskView) {
+        mTaskView = taskView;
+        Context context = taskView.getContext();
+        mActions.put(OPEN, new AccessibilityAction(OPEN,
+                context.getString(R.string.recents_accessibility_open)));
+        mActions.put(DIMISS, new AccessibilityAction(DIMISS,
+                context.getString(R.string.recents_accessibility_dismissed)));
+        mActions.put(SPLIT_TASK_TOP, new AccessibilityAction(SPLIT_TASK_TOP,
+                context.getString(R.string.recents_accessibility_split_screen_top)));
+        mActions.put(SPLIT_TASK_LEFT, new AccessibilityAction(SPLIT_TASK_LEFT,
+                context.getString(R.string.recents_accessibility_split_screen_left)));
+        mActions.put(SPLIT_TASK_RIGHT, new AccessibilityAction(SPLIT_TASK_RIGHT,
+                context.getString(R.string.recents_accessibility_split_screen_right)));
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(host, info);
+        info.addAction(mActions.get(OPEN));
+        info.addAction(mActions.get(DIMISS));
+        if (ActivityManager.supportsSplitScreenMultiWindow()
+                && !Recents.getSystemServices().hasDockedTask()) {
+            TaskStack.DockState[] dockStates = Recents.getConfiguration()
+                    .getDockStatesForCurrentOrientation();
+            for (TaskStack.DockState dockState: dockStates) {
+                if (dockState == TaskStack.DockState.TOP) {
+                    info.addAction(mActions.get(SPLIT_TASK_TOP));
+                } else if (dockState == TaskStack.DockState.LEFT) {
+                    info.addAction(mActions.get(SPLIT_TASK_LEFT));
+                } else if (dockState == TaskStack.DockState.RIGHT) {
+                    info.addAction(mActions.get(SPLIT_TASK_RIGHT));
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(View host, int action, Bundle args) {
+        if (action == OPEN) {
+            mTaskView.onClick(host);
+        } else if (action == DIMISS) {
+            mTaskView.dismissTask();
+        } else if (action == SPLIT_TASK_TOP) {
+            simulateDragIntoMultiwindow(TaskStack.DockState.TOP);
+        } else if (action == SPLIT_TASK_LEFT) {
+            simulateDragIntoMultiwindow(TaskStack.DockState.LEFT);
+        } else if (action == SPLIT_TASK_RIGHT) {
+            simulateDragIntoMultiwindow(TaskStack.DockState.RIGHT);
+        } else {
+            return super.performAccessibilityAction(host, action, args);
+        }
+        return true;
+    }
+
+    /** Simulate a user drag event to split the screen to the respected side */
+    private void simulateDragIntoMultiwindow(TaskStack.DockState dockState) {
+        int orientation = Utilities.getAppConfiguration(mTaskView.getContext()).orientation;
+        EventBus.getDefault().send(new DragStartEvent(mTaskView.getTask(), mTaskView,
+                new Point(0,0), false /* isUserTouchInitiated */));
+        EventBus.getDefault().send(new DragEndEvent(mTaskView.getTask(), mTaskView, dockState));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index c0cc83f..b318ea7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -201,7 +201,9 @@
         Resources res = context.getResources();
         mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
         mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
-        mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        mCornerRadius = Recents.getConfiguration().isGridEnabled ?
+                res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
+                res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
         mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
         mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
         mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
@@ -460,7 +462,6 @@
                 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
-        mDismissButton.setContentDescription(t.dismissDescription);
         mDismissButton.setOnClickListener(this);
         mDismissButton.setClickable(false);
         ((RippleDrawable) mDismissButton.getBackground()).setForceSoftware(true);
@@ -497,7 +498,6 @@
 
         // In accessibility, a single click on the focused app info button will show it
         if (touchExplorationEnabled) {
-            mIconView.setContentDescription(t.appInfoDescription);
             mIconView.setOnClickListener(this);
             mIconView.setClickable(true);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index e3bf1df..4ac0f9e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.views;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -35,6 +36,9 @@
 import android.view.ViewDebug;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.EventBus.Event;
+import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.ThumbnailData;
@@ -57,11 +61,11 @@
 
     // Drawing
     @ViewDebug.ExportedProperty(category="recents")
-    private Rect mTaskViewRect = new Rect();
+    protected Rect mTaskViewRect = new Rect();
     @ViewDebug.ExportedProperty(category="recents")
-    private Rect mThumbnailRect = new Rect();
+    protected Rect mThumbnailRect = new Rect();
     @ViewDebug.ExportedProperty(category="recents")
-    private float mThumbnailScale;
+    protected float mThumbnailScale;
     private float mFullscreenThumbnailScale;
     /** The height, in pixels, of the task view's title bar. */
     private int mTitleBarHeight;
@@ -69,14 +73,14 @@
     private boolean mOverlayHeaderOnThumbnailActionBar = true;
     private ThumbnailData mThumbnailData;
 
-    private int mCornerRadius;
+    protected int mCornerRadius;
     @ViewDebug.ExportedProperty(category="recents")
     private float mDimAlpha;
     private Matrix mMatrix = new Matrix();
-    private Paint mDrawPaint = new Paint();
+    protected Paint mDrawPaint = new Paint();
     private Paint mLockedPaint = new Paint();
-    private Paint mBgFillPaint = new Paint();
-    private BitmapShader mBitmapShader;
+    protected Paint mBgFillPaint = new Paint();
+    protected BitmapShader mBitmapShader;
     private LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
 
     // Clip the top of the thumbnail against the opaque header bar that overlaps this view
@@ -111,8 +115,12 @@
         mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
         mBgFillPaint.setColor(Color.WHITE);
         mLockedPaint.setColor(Color.WHITE);
-        mFullscreenThumbnailScale = res.getFraction(
-                com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+        if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
+            mFullscreenThumbnailScale = 1f;
+        } else {
+            mFullscreenThumbnailScale = res.getFraction(
+                    com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+        }
         mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
     }
 
@@ -347,6 +355,7 @@
             mBgFillPaint.setColor(t.colorBackground);
         }
         mLockedPaint.setColor(t.colorPrimary);
+        EventBus.getDefault().register(this);
     }
 
     /**
@@ -361,6 +370,14 @@
     void unbindFromTask() {
         mTask = null;
         setThumbnail(null);
+        EventBus.getDefault().unregister(this);
+    }
+
+    public final void onBusEvent(TaskSnapshotChangedEvent event) {
+        if (mTask == null || event.taskId != mTask.key.id || event.taskSnapshot == null) {
+            return;
+        }
+        setThumbnail(ThumbnailData.createFromTaskSnapshot(event.taskSnapshot));
     }
 
     public void dump(String prefix, PrintWriter writer) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
new file mode 100644
index 0000000..2c3e42b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views.grid;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.util.AttributeSet;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.views.TaskViewThumbnail;
+
+public class GridTaskViewThumbnail extends TaskViewThumbnail {
+
+    private Path mThumbnailOutline;
+    private Path mRestBackgroundOutline;
+    private Path mFullBackgroundOutline;
+    // True if either this view's size or thumbnail scale has changed and mThumbnailOutline should
+    // be updated.
+    private boolean mUpdateThumbnailOutline = true;
+
+    public GridTaskViewThumbnail(Context context) {
+        this(context, null);
+    }
+
+    public GridTaskViewThumbnail(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public GridTaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public GridTaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr,
+        int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mCornerRadius = getResources().getDimensionPixelSize(
+                R.dimen.recents_grid_task_view_rounded_corners_radius);
+    }
+
+    /**
+     * Called when the task view frame changes, allowing us to move the contents of the header
+     * to match the frame changes.
+     */
+    public void onTaskViewSizeChanged(int width, int height) {
+        mUpdateThumbnailOutline = true;
+        super.onTaskViewSizeChanged(width, height);
+    }
+
+    /**
+     * Updates the scale of the bitmap relative to this view.
+     */
+    public void updateThumbnailMatrix() {
+        mUpdateThumbnailOutline = true;
+        super.updateThumbnailMatrix();
+    }
+
+    private void updateThumbnailOutline() {
+        final int titleHeight = getResources().getDimensionPixelSize(
+            R.dimen.recents_grid_task_view_header_height);
+        final int viewWidth = mTaskViewRect.width();
+        final int viewHeight = mTaskViewRect.height() - titleHeight;
+        final int thumbnailWidth = Math.min(viewWidth,
+            (int) (mThumbnailRect.width() * mThumbnailScale));
+        final int thumbnailHeight = Math.min(viewHeight,
+            (int) (mThumbnailRect.height() * mThumbnailScale));
+        // Draw the thumbnail, we only round the bottom corners:
+        //
+        // outerLeft                outerRight
+        //    <----------------------->            mRestBackgroundOutline
+        //    _________________________            (thumbnailWidth < viewWidth)
+        //    |_______________________| outerTop     A ____ B
+        //    |                       |    ↑           |  |
+        //    |                       |    |           |  |
+        //    |                       |    |           |  |
+        //    |                       |    |           |  | C
+        //    \_______________________/    ↓           |__/
+        //  mCornerRadius             outerBottom    E    D
+        //
+        //  mRestBackgroundOutline (thumbnailHeight < viewHeight)
+        //  A _________________________ B
+        //    |                       | C
+        //  F \_______________________/
+        //    E                       D
+        final int outerLeft = 0;
+        final int outerTop = 0;
+        final int outerRight = outerLeft + thumbnailWidth;
+        final int outerBottom = outerTop + thumbnailHeight;
+        mThumbnailOutline = new Path();
+        mThumbnailOutline.moveTo(outerLeft, outerTop);
+        mThumbnailOutline.lineTo(outerRight, outerTop);
+        mThumbnailOutline.lineTo(outerRight, outerBottom - mCornerRadius);
+        mThumbnailOutline.arcTo(outerRight -  2 * mCornerRadius, outerBottom - 2 * mCornerRadius,
+                outerRight, outerBottom, 0, 90, false);
+        mThumbnailOutline.lineTo(outerLeft + mCornerRadius, outerBottom);
+        mThumbnailOutline.arcTo(outerLeft, outerBottom - 2 * mCornerRadius,
+                outerLeft + 2 * mCornerRadius, outerBottom, 90, 90, false);
+        mThumbnailOutline.lineTo(outerLeft, outerTop);
+        mThumbnailOutline.close();
+
+        if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+            if (thumbnailWidth < viewWidth) {
+                final int l = Math.max(0, outerRight - mCornerRadius);
+                final int r = outerRight;
+                final int t = outerTop;
+                final int b = outerBottom;
+                mRestBackgroundOutline = new Path();
+                mRestBackgroundOutline.moveTo(l, t); // A
+                mRestBackgroundOutline.lineTo(r, t); // B
+                mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
+                mRestBackgroundOutline.arcTo(r -  2 * mCornerRadius, b - 2 * mCornerRadius, r, b,
+                        0, 90, false); // D
+                mRestBackgroundOutline.lineTo(l, b); // E
+                mRestBackgroundOutline.lineTo(l, t); // A
+                mRestBackgroundOutline.close();
+
+            }
+            if (thumbnailHeight < viewHeight) {
+                final int l = outerLeft;
+                final int r = outerRight;
+                final int t = Math.max(0, thumbnailHeight - mCornerRadius);
+                final int b = outerBottom;
+                mRestBackgroundOutline = new Path();
+                mRestBackgroundOutline.moveTo(l, t); // A
+                mRestBackgroundOutline.lineTo(r, t); // B
+                mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
+                mRestBackgroundOutline.arcTo(r -  2 * mCornerRadius, b - 2 * mCornerRadius, r, b,
+                        0, 90, false); // D
+                mRestBackgroundOutline.lineTo(l + mCornerRadius, b); // E
+                mRestBackgroundOutline.arcTo(l, b - 2 * mCornerRadius, l + 2 * mCornerRadius, b,
+                        90, 90, false); // F
+                mRestBackgroundOutline.lineTo(l, t); // A
+                mRestBackgroundOutline.close();
+
+            }
+        } else {
+            mFullBackgroundOutline = mThumbnailOutline;
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        final int titleHeight = getResources().getDimensionPixelSize(
+            R.dimen.recents_grid_task_view_header_height);
+        final int viewWidth = mTaskViewRect.width();
+        final int viewHeight = mTaskViewRect.height() - titleHeight;
+        final int thumbnailWidth = Math.min(viewWidth,
+            (int) (mThumbnailRect.width() * mThumbnailScale));
+        final int thumbnailHeight = Math.min(viewHeight,
+            (int) (mThumbnailRect.height() * mThumbnailScale));
+
+        if (mUpdateThumbnailOutline) {
+            updateThumbnailOutline();
+            mUpdateThumbnailOutline = false;
+        }
+        if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+            // Draw the background, there will be some small overdraw with the thumbnail
+            if (thumbnailWidth < viewWidth) {
+                // Portrait thumbnail on a landscape task view
+                canvas.drawPath(mRestBackgroundOutline, mBgFillPaint);
+            }
+            if (thumbnailHeight < viewHeight) {
+                // Landscape thumbnail on a portrait task view
+                canvas.drawPath(mRestBackgroundOutline, mBgFillPaint);
+            }
+            canvas.drawPath(mThumbnailOutline, mDrawPaint);
+        } else {
+            canvas.drawPath(mFullBackgroundOutline, mBgFillPaint);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
index 5d969f9..02d1cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -23,6 +23,8 @@
 import android.view.WindowManager;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
+import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
 import com.android.systemui.recents.views.TaskViewTransform;
@@ -39,7 +41,6 @@
     /** The padding between task views. */
     private int mPaddingTaskView;
 
-    private Rect mDisplayRect;
     private Rect mWindowRect;
     private Point mScreenSize = new Point();
 
@@ -52,6 +53,9 @@
     private float mAppAspectRatio;
     private Rect mSystemInsets = new Rect();
 
+    /** The thickness of the focused task view frame. */
+    private int mFocusedFrameThickness;
+
     /**
      * When the amount of tasks is determined, the size and position of every task view can be
      * decided. Each instance of TaskGridRectInfo store the task view information for a certain
@@ -61,39 +65,59 @@
         Rect size;
         int[] xOffsets;
         int[] yOffsets;
+        int tasksPerLine;
+        int lines;
 
-        public TaskGridRectInfo(int taskCount) {
+        TaskGridRectInfo(int taskCount) {
             size = new Rect();
             xOffsets = new int[taskCount];
             yOffsets = new int[taskCount];
 
             int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
 
-            int tasksPerLine = layoutTaskCount < 2 ? 1 : (
+            tasksPerLine = layoutTaskCount < 2 ? 1 : (
                 layoutTaskCount < 5 ? 2 : (
                     layoutTaskCount < 7 ? 3 : 4));
-            int lines = layoutTaskCount < 3 ? 1 : 2;
+            lines = layoutTaskCount < 3 ? 1 : 2;
+
+            // A couple of special cases.
+            boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
+            boolean landscapeTaskView = mAppAspectRatio > 1;
+            // If we're in portrait but task views are landscape, show more lines of fewer tasks.
+            if (!landscapeWindow && landscapeTaskView) {
+                tasksPerLine = layoutTaskCount < 2 ? 1 : 2;
+                lines = layoutTaskCount < 3 ? 1 : (
+                        layoutTaskCount < 5 ? 2 : (
+                                layoutTaskCount < 7 ? 3 : 4));
+            }
+            // If we're in landscape but task views are portrait, show fewer lines of more tasks.
+            if (landscapeWindow && !landscapeTaskView) {
+                tasksPerLine = layoutTaskCount < 7 ? layoutTaskCount : 6;
+                lines = layoutTaskCount < 7 ? 1 : 2;
+            }
 
             int taskWidth, taskHeight;
-            int maxTaskWidth = (mDisplayRect.width() - 2 * mPaddingLeftRight
+            int maxTaskWidth = (mWindowRect.width() - 2 * mPaddingLeftRight
                 - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
-            int maxTaskHeight = (mDisplayRect.height() - 2 * mPaddingTopBottom
+            int maxTaskHeight = (mWindowRect.height() - 2 * mPaddingTopBottom
                 - (lines - 1) * mPaddingTaskView) / lines;
 
             if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
                 // Width bound.
                 taskWidth = maxTaskWidth;
-                taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight);
+                // Here we should round the height to the nearest integer.
+                taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight + 0.5);
             } else {
                 // Height bound.
                 taskHeight = maxTaskHeight;
-                taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio);
+                // Here we should round the width to the nearest integer.
+                taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio + 0.5);
             }
             size.set(0, 0, taskWidth, taskHeight);
 
-            int emptySpaceX = mDisplayRect.width() - 2 * mPaddingLeftRight
+            int emptySpaceX = mWindowRect.width() - 2 * mPaddingLeftRight
                 - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
-            int emptySpaceY = mDisplayRect.height() - 2 * mPaddingTopBottom
+            int emptySpaceY = mWindowRect.height() - 2 * mPaddingTopBottom
                 - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
             for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) {
                 // We also need to invert the index in order to display the most recent tasks first.
@@ -101,9 +125,9 @@
 
                 int xIndex = taskLayoutIndex % tasksPerLine;
                 int yIndex = taskLayoutIndex / tasksPerLine;
-                xOffsets[taskIndex] =
+                xOffsets[taskIndex] = mWindowRect.left +
                     emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
-                yOffsets[taskIndex] =
+                yOffsets[taskIndex] = mWindowRect.top +
                     emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
             }
         }
@@ -113,7 +137,7 @@
      * We can find task view sizes and positions from mTaskGridRectInfoList[k - 1] when there
      * are k tasks.
      */
-    TaskGridRectInfo[] mTaskGridRectInfoList;
+    private TaskGridRectInfo[] mTaskGridRectInfoList;
 
     public TaskGridLayoutAlgorithm(Context context) {
         reloadOnConfigurationChange(context);
@@ -121,9 +145,9 @@
 
     public void reloadOnConfigurationChange(Context context) {
         Resources res = context.getResources();
-        mPaddingLeftRight = res.getDimensionPixelSize(R.dimen.recents_grid_padding_left_right);
-        mPaddingTopBottom = res.getDimensionPixelSize(R.dimen.recents_grid_padding_top_bottom);
         mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
+        mFocusedFrameThickness = res.getDimensionPixelSize(
+            R.dimen.recents_grid_task_view_focused_frame_thickness);
 
         mTaskGridRect = new Rect();
         mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
@@ -147,6 +171,10 @@
      */
     public TaskViewTransform getTransform(int taskIndex, int taskCount,
         TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
+        if (taskCount == 0) {
+            transformOut.reset();
+            return transformOut;
+        }
 
         TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
         mTaskGridRect.set(gridInfo.size);
@@ -162,7 +190,7 @@
 
         // We also need to invert the index in order to display the most recent tasks first.
         int taskLayoutIndex = taskCount - taskIndex - 1;
-        boolean isTaskViewVisible = (taskLayoutIndex < MAX_LAYOUT_TASK_COUNT);
+        boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
 
         // Fill out the transform
         transformOut.scale = 1f;
@@ -178,9 +206,53 @@
         return transformOut;
     }
 
-    public void initialize(Rect displayRect, Rect windowRect) {
-        mDisplayRect = displayRect;
+    /**
+     * Return the proper task index to focus for arrow key navigation.
+     * @param taskCount             The amount of tasks.
+     * @param currentFocusedIndex   The index of the currently focused task.
+     * @param direction             The direction we're navigating.
+     * @return  The index of the task that should get the focus.
+     */
+    public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
+        if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
+            return -1;
+        }
+        if (currentFocusedIndex == -1) {
+            return 0;
+        }
+        int newIndex = currentFocusedIndex;
+        final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+        final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
+        switch (direction) {
+            case UP:
+                newIndex += gridInfo.tasksPerLine;
+                newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
+                break;
+            case DOWN:
+                newIndex -= gridInfo.tasksPerLine;
+                newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
+                break;
+            case LEFT:
+                newIndex++;
+                final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
+                newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
+                break;
+            case RIGHT:
+                newIndex--;
+                int rightMostIndex =
+                    (taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
+                rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
+                newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
+                break;
+        }
+        return newIndex;
+    }
+
+    public void initialize(Rect windowRect) {
         mWindowRect = windowRect;
+        // Define paddings in terms of percentage of the total area.
+        mPaddingLeftRight = (int) (0.025f * Math.min(mWindowRect.width(), mWindowRect.height()));
+        mPaddingTopBottom = (int) (0.1 * mWindowRect.height());
 
         // Pre-calculate the positions and offsets of task views so that we can reuse them directly
         // in the future.
@@ -202,14 +274,25 @@
     }
 
     public Rect getStackActionButtonRect() {
-        Rect buttonRect = new Rect(mDisplayRect);
+        Rect buttonRect = new Rect(mWindowRect);
         buttonRect.right -= mPaddingLeftRight;
         buttonRect.left += mPaddingLeftRight;
         buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
         return buttonRect;
     }
 
+    public void updateTaskGridRect(int taskCount) {
+        if (taskCount > 0) {
+            TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+            mTaskGridRect.set(gridInfo.size);
+        }
+    }
+
     public Rect getTaskGridRect() {
         return mTaskGridRect;
     }
+
+    public int getFocusFrameThickness() {
+        return mFocusedFrameThickness;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
new file mode 100644
index 0000000..86ed583
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views.grid;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
+import com.android.systemui.R;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+
+public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
+
+    private TaskStackView mSv;
+    private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
+    public TaskViewFocusFrame(Context context) {
+        this(context, null);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr,
+        int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setBackground(mContext.getDrawable(
+            R.drawable.recents_grid_task_view_focus_frame_background));
+        setFocusable(false);
+        hide();
+    }
+
+    public TaskViewFocusFrame(Context context, TaskStackView stackView,
+        TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) {
+        this(context);
+        mSv = stackView;
+        mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm;
+    }
+
+    /**
+     * Measure the width and height of the focus frame according to the current grid task view size.
+     */
+    public void measure() {
+        int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+        Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect();
+        measure(
+            MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY),
+            MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY));
+    }
+
+    /**
+     * Layout the focus frame with its size.
+     */
+    public void layout() {
+        layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
+    }
+
+    /**
+     * Update the current size of grid task view and the focus frame.
+     */
+    public void resize() {
+        if (mSv.useGridLayout()) {
+            mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount());
+            measure();
+            requestLayout();
+        }
+    }
+
+    /**
+     * Move the task view focus frame to surround the newly focused view. If it's {@code null} or
+     * it's not an instance of GridTaskView, we hide the focus frame.
+     * @param newFocus The newly focused view.
+     */
+    public void moveGridTaskViewFocus(View newFocus) {
+        if (mSv.useGridLayout()) {
+            // The frame only shows up in the grid layout. It shouldn't show up in the stack
+            // layout including when we're in the split screen.
+            if (newFocus instanceof GridTaskView) {
+                // If the focus goes to a GridTaskView, we show the frame and layout it.
+                int[] location = new int[2];
+                newFocus.getLocationInWindow(location);
+                int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+                setTranslationX(location[0] - thickness);
+                setTranslationY(location[1] - thickness);
+                show();
+            } else {
+                // If focus goes to other views, we hide the frame.
+                hide();
+            }
+        }
+    }
+
+    @Override
+    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+        if (!mSv.useGridLayout()) {
+            return;
+        }
+        if (newFocus == null) {
+            // We're going to touch mode, unset the focus.
+            moveGridTaskViewFocus(null);
+            return;
+        }
+        if (oldFocus == null) {
+            // We're returning from touch mode, set the focus to the previously focused task.
+            final TaskStack stack = mSv.getStack();
+            final int taskCount = stack.getTaskCount();
+            final int k = stack.indexOfStackTask(mSv.getFocusedTask());
+            final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
+            mSv.setFocusedTask(taskIndexToFocus, false, true);
+        }
+    }
+
+    private void show() {
+        setAlpha(1f);
+    }
+
+    private void hide() {
+        setAlpha(0f);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index db021ff..7135caf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -62,6 +62,7 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.util.NotificationChannels;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -178,6 +179,7 @@
 
         // The public notification will show similar info but with the actual screenshot omitted
         mPublicNotificationBuilder = new Notification.Builder(context)
+                .setChannel(NotificationChannels.SCREENSHOTS)
                 .setContentTitle(r.getString(R.string.screenshot_saving_title))
                 .setContentText(r.getString(R.string.screenshot_saving_text))
                 .setSmallIcon(R.drawable.stat_notify_image)
@@ -189,6 +191,7 @@
         SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder);
 
         mNotificationBuilder = new Notification.Builder(context)
+            .setChannel(NotificationChannels.SCREENSHOTS)
             .setTicker(r.getString(R.string.screenshot_saving_ticker)
                     + (mTickerAddSpace ? " " : ""))
             .setContentTitle(r.getString(R.string.screenshot_saving_title))
@@ -332,6 +335,7 @@
 
             // Update the text and the icon for the existing notification
             mPublicNotificationBuilder
+                    .setChannel(NotificationChannels.SCREENSHOTS)
                     .setContentTitle(r.getString(R.string.screenshot_saved_title))
                     .setContentText(r.getString(R.string.screenshot_saved_text))
                     .setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
@@ -340,6 +344,7 @@
                     .setColor(context.getColor(
                             com.android.internal.R.color.system_notification_accent_color));
             mNotificationBuilder
+                .setChannel(NotificationChannels.SCREENSHOTS)
                 .setContentTitle(r.getString(R.string.screenshot_saved_title))
                 .setContentText(r.getString(R.string.screenshot_saved_text))
                 .setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 3059a05..7825e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -37,6 +37,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 
 import java.util.ArrayList;
 
@@ -70,7 +71,7 @@
     private final CurrentUserTracker mUserTracker;
     private final IVrManager mVrManager;
 
-    private Handler mBackgroundHandler;
+    private final Handler mBackgroundHandler;
     private final BrightnessObserver mBrightnessObserver;
 
     private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks =
@@ -276,7 +277,7 @@
         mContext = context;
         mIcon = icon;
         mControl = control;
-        mBackgroundHandler = new Handler(Looper.getMainLooper());
+        mBackgroundHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
         mUserTracker = new CurrentUserTracker(mContext) {
             @Override
             public void onUserSwitched(int newUserId) {
@@ -298,10 +299,6 @@
         mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
     }
 
-    public void setBackgroundLooper(Looper backgroundLooper) {
-        mBackgroundHandler = new Handler(backgroundLooper);
-    }
-
     public void addStateChangedCallback(BrightnessStateChangeCallback cb) {
         mChangeCallbacks.add(cb);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index d1ab96d..b9ed725 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -182,6 +182,7 @@
     private int mStartTint;
     private int mOverrideTint;
     private float mOverrideAmount;
+    private boolean mShadowHidden;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -210,6 +211,7 @@
         super.onFinishInflate();
         mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal);
         mFakeShadow = (FakeShadowView) findViewById(R.id.fake_shadow);
+        mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
         mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
         mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
         mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
@@ -249,7 +251,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         boolean result;
-        if (mDimmed && !isTouchExplorationEnabled()) {
+        if (mDimmed && !isTouchExplorationEnabled() && isInteractive()) {
             boolean wasActivated = mActivated;
             result = handleTouchEventDimmed(event);
             if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
@@ -261,6 +263,13 @@
         return result;
     }
 
+    /**
+     * @return whether this view is interactive and can be double tapped
+     */
+    protected boolean isInteractive() {
+        return true;
+    }
+
     @Override
     public void drawableHotspotChanged(float x, float y) {
         if (!mDimmed){
@@ -454,6 +463,7 @@
         }
         mDark = dark;
         updateBackground();
+        updateBackgroundTint(fade);
         if (!dark && fade && !shouldHideBackground()) {
             fadeInFromDark(delay);
         }
@@ -691,8 +701,8 @@
     protected void updateBackground() {
         cancelFadeAnimations();
         if (shouldHideBackground()) {
-            mBackgroundDimmed.setVisibility(View.INVISIBLE);
-            mBackgroundNormal.setVisibility(View.INVISIBLE);
+            mBackgroundDimmed.setVisibility(INVISIBLE);
+            mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
         } else if (mDimmed) {
             // When groups are animating to the expanded state from the lockscreen, show the
             // normal background instead of the dimmed background
@@ -931,6 +941,9 @@
      * @return the calculated background color
      */
     private int calculateBgColor(boolean withTint, boolean withOverRide) {
+        if (mDark) {
+            return getContext().getColor(R.color.notification_material_background_dark_color);
+        }
         if (withOverRide && mOverrideTint != NO_COLOR) {
             int defaultTint = calculateBgColor(withTint, false);
             return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
@@ -1020,9 +1033,13 @@
     @Override
     public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
             int outlineTranslation) {
-        mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ()
-                + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd,
-                outlineTranslation);
+        boolean hiddenBefore = mShadowHidden;
+        mShadowHidden = shadowIntensity == 0.0f;
+        if (!mShadowHidden || !hiddenBefore) {
+            mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ()
+                            + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd,
+                    outlineTranslation);
+        }
     }
 
     public int getBackgroundColorWithoutTint() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 561b469..9a5e783 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -24,6 +24,7 @@
 import android.app.INotificationManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -41,6 +42,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.AsyncTask;
 import android.os.Build;
@@ -51,6 +53,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -71,7 +74,6 @@
 import android.view.Display;
 import android.view.IWindowManager;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
@@ -92,24 +94,30 @@
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.DejankUtils;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.SystemUI;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeGutsContent;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationGuts.OnGutsClosedListener;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NavigationBarView;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.PreviewInflater;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.util.NotificationChannels;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -122,7 +130,7 @@
 public abstract class BaseStatusBar extends SystemUI implements
         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
         ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
-        ExpandableNotificationRow.OnExpandClickListener, OnGutsClosedListener {
+        ExpandableNotificationRow.OnExpandClickListener {
     public static final String TAG = "StatusBar";
     public static final boolean DEBUG = false;
     public static final boolean MULTIUSER_DEBUG = false;
@@ -223,6 +231,7 @@
 
     protected KeyguardManager mKeyguardManager;
     private LockPatternUtils mLockPatternUtils;
+    private DeviceProvisionedController mDeviceProvisionedController;
 
     // UI-specific methods
 
@@ -237,14 +246,13 @@
 
     protected Display mDisplay;
 
-    private boolean mDeviceProvisioned = false;
-
     protected RecentsComponent mRecents;
 
     protected int mZenMode;
 
     // which notification is currently being longpress-examined by the user
     private NotificationGuts mNotificationGutsExposed;
+    private MenuItem mGutsMenuItem;
 
     private KeyboardShortcuts mKeyboardShortcuts;
 
@@ -270,7 +278,7 @@
 
     @Override  // NotificationData.Environment
     public boolean isDeviceProvisioned() {
-        return mDeviceProvisioned;
+        return mDeviceProvisionedController.isDeviceProvisioned();
     }
 
     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@@ -284,15 +292,17 @@
         return mVrMode;
     }
 
+    private final DeviceProvisionedListener mDeviceProvisionedListener =
+            new DeviceProvisionedListener() {
+        @Override
+        public void onDeviceProvisionedChanged() {
+            updateNotifications();
+        }
+    };
+
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange) {
-            final boolean provisioned = 0 != Settings.Global.getInt(
-                    mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
-            if (provisioned != mDeviceProvisioned) {
-                mDeviceProvisioned = provisioned;
-                updateNotifications();
-            }
             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
             setZenMode(mode);
@@ -315,9 +325,16 @@
     };
 
     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
+        private final int[] mTmpInt2 = new int[2];
+
         @Override
         public boolean onClickHandler(
                 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
+            view.getLocationInWindow(mTmpInt2);
+            wakeUpIfDozing(SystemClock.uptimeMillis(), new PointF(
+                    mTmpInt2[0] + view.getWidth() / 2, mTmpInt2[1] + view.getHeight() / 2));
+
+
             if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
                 return true;
             }
@@ -691,6 +708,7 @@
         }
     }
 
+    @Override
     public void start() {
         mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
@@ -707,9 +725,8 @@
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
 
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
-                mSettingsObserver);
+        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
                 mSettingsObserver);
@@ -866,7 +883,7 @@
                     .setSmallIcon(R.drawable.ic_android)
                     .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
                     .setContentText(mContext.getString(R.string.hidden_notifications_text))
-                    .setPriority(Notification.PRIORITY_HIGH)
+                    .setChannel(NotificationChannels.SECURITY)
                     .setOngoing(true)
                     .setColor(mContext.getColor(colorRes))
                     .setContentIntent(setupIntent)
@@ -957,7 +974,7 @@
             entry.row.reInflateViews();
             if (exposedGuts) {
                 mNotificationGutsExposed = entry.row.getGuts();
-                bindGuts(entry.row);
+                bindGuts(entry.row, mGutsMenuItem);
             }
             inflateViews(entry, mStackScroller);
         }
@@ -1022,6 +1039,7 @@
             @Override
             public boolean onDismiss() {
                 AsyncTask.execute(new Runnable() {
+                    @Override
                     public void run() {
                         TaskStackBuilder.create(mContext)
                                 .addNextIntentWithParentStack(intent)
@@ -1035,51 +1053,74 @@
         }, false /* afterKeyguardGone */);
     }
 
-    private void bindGuts(final ExpandableNotificationRow row) {
-        row.inflateGuts();
-        final StatusBarNotification sbn = row.getStatusBarNotification();
-        PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
-        row.setTag(sbn.getPackageName());
-        final NotificationGuts guts = row.getGuts();
-        guts.setClosedListener(this);
-
-        final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-
-        final String pkg = sbn.getPackageName();
-        final NotificationGuts.OnSettingsClickListener onSettingsClick =
-                (View v, int appUid) -> {
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
-                    guts.resetFalsingCheck();
-                    startAppNotificationSettingsActivity(pkg, appUid);
-                };
-        final View.OnClickListener onDoneClick =
-                (View v) -> {
-                    // If the user has security enabled, show challenge if the setting is changed.
-                    if (guts.hasImportanceChanged()
-                                && isLockscreenPublicMode(sbn.getUser().getIdentifier())
-                                && (mState == StatusBarState.KEYGUARD
-                                        || mState == StatusBarState.SHADE_LOCKED)) {
-                        OnDismissAction dismissAction = new OnDismissAction() {
-                            @Override
-                            public boolean onDismiss() {
-                                closeControls(row, guts, v);
-                                return true;
-                            }
-                        };
-                        onLockedNotificationImportanceChange(dismissAction);
-                    } else {
-                        closeControls(row, guts, v);
-                    }
-                };
-        guts.bindNotification(pmUser, iNotificationManager, sbn, onSettingsClick, onDoneClick,
-                mNonBlockablePkgs);
+    protected void setNotificationSnoozed(StatusBarNotification sbn, long snoozeUntil) {
+        mNotificationListener.snoozeNotification(sbn.getKey(), snoozeUntil);
     }
 
-    private void closeControls(
+    public SnoozeListener getSnoozeListener() {
+        return null;
+    }
+
+    private void bindGuts(final ExpandableNotificationRow row, MenuItem item) {
+        row.inflateGuts();
+        row.setGutsView(item);
+        final StatusBarNotification sbn = row.getStatusBarNotification();
+        row.setTag(sbn.getPackageName());
+        final NotificationGuts guts = row.getGuts();
+        guts.setClosedListener((NotificationGuts g) -> {
+            if (!row.isRemoved()) {
+                mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */);
+            }
+            mNotificationGutsExposed = null;
+        });
+
+        if (item.gutsContent instanceof SnoozeGutsContent) {
+            ((SnoozeGutsContent) item.gutsContent).setSnoozeListener(getSnoozeListener());
+            ((SnoozeGutsContent) item.gutsContent).setStatusBarNotification(sbn);
+        }
+
+        if (item.gutsContent instanceof NotificationInfo) {
+            final NotificationChannel channel = row.getEntry().channel;
+            PackageManager pmUser = getPackageManagerForUser(mContext,
+                    sbn.getUser().getIdentifier());
+            final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
+                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+            final String pkg = sbn.getPackageName();
+            NotificationInfo info = (NotificationInfo) item.gutsContent;
+            final NotificationInfo.OnSettingsClickListener onSettingsClick = (View v,
+                    int appUid) -> {
+                MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
+                guts.resetFalsingCheck();
+                startAppNotificationSettingsActivity(pkg, appUid);
+            };
+            final View.OnClickListener onDoneClick = (View v) -> {
+                // If the user has security enabled, show challenge if the setting is changed.
+                if (info.hasImportanceChanged()
+                        && isLockscreenPublicMode(sbn.getUser().getIdentifier())
+                        && (mState == StatusBarState.KEYGUARD
+                                || mState == StatusBarState.SHADE_LOCKED)) {
+                    OnDismissAction dismissAction = new OnDismissAction() {
+                        @Override
+                        public boolean onDismiss() {
+                            saveAndCloseNotificationMenu(info, row, guts, v);
+                            return true;
+                        }
+                    };
+                    onLockedNotificationImportanceChange(dismissAction);
+                } else {
+                    saveAndCloseNotificationMenu(info, row, guts, v);
+                }
+            };
+            info.bindNotification(pmUser, iNotificationManager, sbn, channel, onSettingsClick,
+                    onDoneClick,
+                    mNonBlockablePkgs);
+        }
+    }
+
+    private void saveAndCloseNotificationMenu(NotificationInfo info,
             ExpandableNotificationRow row, NotificationGuts guts, View done) {
         guts.resetFalsingCheck();
-
+        info.saveImportance();
         int[] rowLocation = new int[2];
         int[] doneLocation = new int[2];
         row.getLocationOnScreen(rowLocation);
@@ -1095,7 +1136,8 @@
     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
         return new SwipeHelper.LongPressListener() {
             @Override
-            public boolean onLongPress(View v, final int x, final int y) {
+            public boolean onLongPress(View v, final int x, final int y,
+                    MenuItem item) {
                 if (!(v instanceof ExpandableNotificationRow)) {
                     return false;
                 }
@@ -1105,10 +1147,10 @@
                 }
 
                 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-                bindGuts(row);
+                bindGuts(row, item);
+                NotificationGuts guts = row.getGuts();
 
                 // Assume we are a status_bar_notification_row
-                final NotificationGuts guts = row.getGuts();
                 if (guts == null) {
                     // This view has no guts. Examples are the more card or the dismiss all view
                     return false;
@@ -1126,7 +1168,13 @@
                 guts.setVisibility(View.INVISIBLE);
                 // Post to ensure the the guts are properly laid out.
                 guts.post(new Runnable() {
+                    @Override
                     public void run() {
+                        if (row.getWindowToken() == null) {
+                            Log.e(TAG, "Trying to show notification guts, but not attached to "
+                                    + "window");
+                            return;
+                        }
                         dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
                                 false /* animate */);
                         guts.setVisibility(View.VISIBLE);
@@ -1149,8 +1197,9 @@
                         guts.setExposed(true /* exposed */,
                                 mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
                         row.closeRemoteInput();
-                        mStackScroller.onHeightChanged(null, true /* needsAnimation */);
+                        mStackScroller.onHeightChanged(row, true /* needsAnimation */);
                         mNotificationGutsExposed = guts;
+                        mGutsMenuItem = item;
                     }
                 });
                 return true;
@@ -1183,12 +1232,6 @@
     }
 
     @Override
-    public void onGutsClosed(NotificationGuts guts) {
-        mStackScroller.onHeightChanged(null, true /* needsAnimation */);
-        mNotificationGutsExposed = null;
-    }
-
-    @Override
     public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
         int msg = MSG_SHOW_RECENT_APPS;
         mHandler.removeMessages(msg);
@@ -1468,6 +1511,7 @@
     }
 
     protected class H extends Handler {
+        @Override
         public void handleMessage(Message m) {
             switch (m.what) {
              case MSG_SHOW_RECENT_APPS:
@@ -1509,8 +1553,9 @@
                 entry.notification.getUser().getIdentifier());
 
         final StatusBarNotification sbn = entry.notification;
+        boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
         try {
-            entry.cacheContentViews(mContext, null);
+            entry.cacheContentViews(mContext, null, isLowPriority);
         } catch (RuntimeException e) {
             Log.e(TAG, "Unable to get notification remote views", e);
             return false;
@@ -1520,6 +1565,7 @@
         final RemoteViews bigContentView = entry.cachedBigContentView;
         final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
         final RemoteViews publicContentView = entry.cachedPublicContentView;
+        final RemoteViews ambientContentView = entry.cachedAmbientContentView;
 
         if (contentView == null) {
             Log.v(TAG, "no contentView for: " + sbn.getNotification());
@@ -1581,6 +1627,7 @@
 
         workAroundBadLayerDrawableOpacity(row);
         bindDismissRunnable(row);
+        row.setIsLowPriority(isLowPriority);
 
         // NB: the large icon is now handled entirely by the template
 
@@ -1600,6 +1647,7 @@
         View bigContentViewLocal = null;
         View headsUpContentViewLocal = null;
         View publicViewLocal = null;
+        View ambientViewLocal = null;
         try {
             contentViewLocal = contentView.apply(
                     sbn.getPackageContext(mContext),
@@ -1622,6 +1670,11 @@
                         sbn.getPackageContext(mContext),
                         contentContainerPublic, mOnClickHandler);
             }
+            if (ambientContentView != null) {
+                ambientViewLocal = ambientContentView.apply(
+                        sbn.getPackageContext(mContext),
+                        contentContainer, mOnClickHandler);
+            }
 
             if (contentViewLocal != null) {
                 contentViewLocal.setIsRootNamespace(true);
@@ -1639,6 +1692,11 @@
                 publicViewLocal.setIsRootNamespace(true);
                 contentContainerPublic.setContractedChild(publicViewLocal);
             }
+
+            if (ambientViewLocal != null) {
+                ambientViewLocal.setIsRootNamespace(true);
+                contentContainer.setAmbientChild(ambientViewLocal);
+            }
         }
         catch (RuntimeException e) {
             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
@@ -1725,6 +1783,7 @@
                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
                 mCurrentUserId);
         dismissKeyguardThenExecute(new OnDismissAction() {
+            @Override
             public boolean onDismiss() {
                 new Thread() {
                     @Override
@@ -1769,13 +1828,23 @@
         return false;
     }
 
+    public void wakeUpIfDozing(long time, PointF where) {
+    }
+
     private final class NotificationClicker implements View.OnClickListener {
+        private final int[] mTmpInt2 = new int[2];
+
+        @Override
         public void onClick(final View v) {
             if (!(v instanceof ExpandableNotificationRow)) {
                 Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
                 return;
             }
 
+            v.getLocationInWindow(mTmpInt2);
+            wakeUpIfDozing(SystemClock.uptimeMillis(),
+                    new PointF(mTmpInt2[0] + v.getWidth() / 2, mTmpInt2[1] + v.getHeight() / 2));
+
             final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
             final StatusBarNotification sbn = row.getStatusBarNotification();
             if (sbn == null) {
@@ -1809,6 +1878,7 @@
                     && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
                             mCurrentUserId);
             dismissKeyguardThenExecute(new OnDismissAction() {
+                @Override
                 public boolean onDismiss() {
                     if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
                         // Release the HUN notification to the shade.
@@ -1971,7 +2041,8 @@
                 Intent.EXTRA_INTENT,
                 callBackPendingIntent.getIntentSender());
         try {
-            ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent);
+            ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent,
+                    null /*options*/);
         } catch (RemoteException ex) {
             // ignore
         }
@@ -2135,6 +2206,7 @@
                 row.setOnKeyguard(false);
                 row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
             }
+            entry.row.setShowAmbient(isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.row.isRemoved();
@@ -2172,6 +2244,10 @@
         mStackScroller.changeViewPosition(mNotificationShelf, mStackScroller.getChildCount() - 3);
     }
 
+    public boolean isDozing() {
+        return false;
+    }
+
     public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
         return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
     }
@@ -2242,7 +2318,8 @@
 
         boolean applyInPlace;
         try {
-            applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
+            applyInPlace = entry.cacheContentViews(mContext, notification.getNotification(),
+                    mNotificationData.isAmbient(key));
         } catch (RuntimeException e) {
             Log.e(TAG, "Unable to get notification remote views", e);
             applyInPlace = false;
@@ -2384,7 +2461,7 @@
             Log.d(TAG, "failed to query dream manager", e);
         }
 
-        if (!inUse) {
+        if (!inUse && !isDozing()) {
             if (DEBUG) {
                 Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
             }
@@ -2449,6 +2526,7 @@
         } catch (RemoteException e) {
             // Ignore.
         }
+        mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index fed28e3..477701c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -185,7 +185,14 @@
     public void animateCollapsePanels() {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_COLLAPSE_PANELS);
-            mHandler.sendEmptyMessage(MSG_COLLAPSE_PANELS);
+            mHandler.obtainMessage(MSG_COLLAPSE_PANELS, 0, 0).sendToTarget();
+        }
+    }
+
+    public void animateCollapsePanels(int flags) {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_COLLAPSE_PANELS);
+            mHandler.obtainMessage(MSG_COLLAPSE_PANELS, flags, 0).sendToTarget();
         }
     }
 
@@ -450,7 +457,7 @@
                     break;
                 case MSG_COLLAPSE_PANELS:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).animateCollapsePanels(0);
+                        mCallbacks.get(i).animateCollapsePanels(msg.arg1);
                     }
                     break;
                 case MSG_EXPAND_SETTINGS:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 173a110..9177f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -22,6 +22,7 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.ColorDrawable;
@@ -50,7 +51,9 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -74,6 +77,7 @@
     private int mMaxHeadsUpHeight;
     private int mNotificationMinHeight;
     private int mNotificationMaxHeight;
+    private int mNotificationAmbientHeight;
     private int mIncreasedPaddingBetweenElements;
 
     /** Does this row contain layouts that can adapt to row expansion */
@@ -117,7 +121,7 @@
     private int mNotificationColor;
     private ExpansionLogger mLogger;
     private String mLoggingKey;
-    private NotificationSettingsIconRow mSettingsIconRow;
+    private NotificationMenuRow mMenuRow;
     private NotificationGuts mGuts;
     private NotificationData.Entry mEntry;
     private StatusBarNotification mStatusBarNotification;
@@ -129,7 +133,7 @@
     private boolean mChildrenExpanded;
     private boolean mIsSummaryWithChildren;
     private NotificationChildrenContainer mChildrenContainer;
-    private ViewStub mSettingsIconRowStub;
+    private ViewStub mMenuRowStub;
     private ViewStub mGutsStub;
     private boolean mIsSystemChildExpanded;
     private boolean mIsPinned;
@@ -197,9 +201,13 @@
     private float mContentTransformationAmount;
     private boolean mIconsVisible = true;
     private boolean mAboveShelf;
+    private boolean mShowAmbient;
     private boolean mIsLastChild;
     private Runnable mOnDismissRunnable;
+    private boolean mIsLowPriority;
+    private boolean mIsColorized;
 
+    @Override
     public boolean isGroupExpansionChanging() {
         if (isChildInGroup()) {
             return mNotificationParent.isGroupExpansionChanging();
@@ -290,6 +298,7 @@
         for (NotificationContentView l : mLayouts) {
             l.onNotificationUpdated(entry);
         }
+        mIsColorized = mStatusBarNotification.getNotification().isColorized();
         mShowingPublicInitialized = false;
         updateNotificationColor();
         if (mIsSummaryWithChildren) {
@@ -307,6 +316,18 @@
         mPublicLayout.updateExpandButtons(true);
         updateLimits();
         updateIconVisibilities();
+        updateShelfIconColor();
+    }
+
+    private void updateShelfIconColor() {
+        StatusBarIconView expandedIcon = mEntry.expandedIcon;
+        boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
+        boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
+                NotificationColorUtil.getInstance(mContext));
+        if (colorize) {
+            int color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded());
+            expandedIcon.setImageTintList(ColorStateList.valueOf(color));
+        }
     }
 
     private void updateLimits() {
@@ -326,7 +347,8 @@
                         != com.android.internal.R.id.status_bar_latest_event_content;
         int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
                 : mMaxHeadsUpHeight;
-        layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
+        layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
+                mNotificationAmbientHeight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
@@ -368,8 +390,8 @@
 
     public void setAppName(String appName) {
         mAppName = appName;
-        if (mSettingsIconRow != null) {
-            mSettingsIconRow.setAppName(mAppName);
+        if (mMenuRow != null) {
+            mMenuRow.setAppName(mAppName);
         }
     }
 
@@ -400,6 +422,7 @@
         row.setIsChildInGroup(false, null);
     }
 
+    @Override
     public boolean isChildInGroup() {
         return mNotificationParent != null;
     }
@@ -437,7 +460,7 @@
 
     @Override
     protected boolean handleSlideBack() {
-        if (mSettingsIconRow != null && mSettingsIconRow.isVisible()) {
+        if (mMenuRow != null && mMenuRow.isVisible()) {
             animateTranslateNotification(0 /* targetLeft */);
             return true;
         }
@@ -661,6 +684,13 @@
         mHeadsUpManager = headsUpManager;
     }
 
+    public void setGutsView(MenuItem item) {
+        if (mGuts != null) {
+            item.gutsContent.setInteractionListener(mGuts);
+            mGuts.setGutsContent(item.gutsContent);
+        }
+    }
+
     public void reInflateViews() {
         initDimens();
         if (mIsSummaryWithChildren) {
@@ -677,16 +707,16 @@
             mGuts.setVisibility(oldGuts.getVisibility());
             addView(mGuts, index);
         }
-        if (mSettingsIconRow != null) {
-            View oldSettings = mSettingsIconRow;
-            int settingsIndex = indexOfChild(oldSettings);
-            removeView(oldSettings);
-            mSettingsIconRow = (NotificationSettingsIconRow) LayoutInflater.from(mContext).inflate(
-                    R.layout.notification_settings_icon_row, this, false);
-            mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this);
-            mSettingsIconRow.setAppName(mAppName);
-            mSettingsIconRow.setVisibility(oldSettings.getVisibility());
-            addView(mSettingsIconRow, settingsIndex);
+        if (mMenuRow != null) {
+            View oldMenu = mMenuRow;
+            int menuIndex = indexOfChild(oldMenu);
+            removeView(oldMenu);
+            mMenuRow = (NotificationMenuRow) LayoutInflater.from(mContext).inflate(
+                    R.layout.notification_menu_row, this, false);
+            mMenuRow.setNotificationRowParent(ExpandableNotificationRow.this);
+            mMenuRow.setAppName(mAppName);
+            mMenuRow.setVisibility(oldMenu.getVisibility());
+            addView(mMenuRow, menuIndex);
 
         }
         for (NotificationContentView l : mLayouts) {
@@ -940,6 +970,14 @@
         return mPrivateLayout.getTranslationY();
     }
 
+    public void setIsLowPriority(boolean isLowPriority) {
+        mIsLowPriority = isLowPriority;
+        mPrivateLayout.setIsLowPriority(isLowPriority);
+        if (mChildrenContainer != null) {
+            mChildrenContainer.setIsLowPriority(isLowPriority);
+        }
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -954,6 +992,7 @@
         mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
         mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
         mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
+        mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
         mMaxHeadsUpHeightLegacy = getFontScaledHeight(
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
@@ -1006,21 +1045,19 @@
         super.onFinishInflate();
         mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
         mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
-
         mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
 
         for (NotificationContentView l : mLayouts) {
             l.setExpandClickListener(mExpandClickListener);
             l.setContainingNotification(this);
         }
-
-        mSettingsIconRowStub = (ViewStub) findViewById(R.id.settings_icon_row_stub);
-        mSettingsIconRowStub.setOnInflateListener(new ViewStub.OnInflateListener() {
+        mMenuRowStub = (ViewStub) findViewById(R.id.menu_row_stub);
+        mMenuRowStub.setOnInflateListener(new ViewStub.OnInflateListener() {
             @Override
             public void onInflate(ViewStub stub, View inflated) {
-                mSettingsIconRow = (NotificationSettingsIconRow) inflated;
-                mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this);
-                mSettingsIconRow.setAppName(mAppName);
+                mMenuRow = (NotificationMenuRow) inflated;
+                mMenuRow.setNotificationRowParent(ExpandableNotificationRow.this);
+                mMenuRow.setAppName(mAppName);
             }
         });
         mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
@@ -1039,6 +1076,7 @@
             @Override
             public void onInflate(ViewStub stub, View inflated) {
                 mChildrenContainer = (NotificationChildrenContainer) inflated;
+                mChildrenContainer.setIsLowPriority(mIsLowPriority);
                 mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
                 mChildrenContainer.onNotificationUpdated();
                 mTranslateableViews.add(mChildrenContainer);
@@ -1051,7 +1089,7 @@
             mTranslateableViews.add(getChildAt(i));
         }
         // Remove views that don't translate
-        mTranslateableViews.remove(mSettingsIconRowStub);
+        mTranslateableViews.remove(mMenuRowStub);
         mTranslateableViews.remove(mChildrenContainerStub);
         mTranslateableViews.remove(mGutsStub);
     }
@@ -1066,8 +1104,8 @@
             }
         }
         invalidateOutline();
-        if (mSettingsIconRow != null) {
-            mSettingsIconRow.resetState();
+        if (mMenuRow != null) {
+            mMenuRow.resetState(true /* notify */);
         }
     }
 
@@ -1094,8 +1132,8 @@
             }
         }
         invalidateOutline();
-        if (mSettingsIconRow != null) {
-            mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth());
+        if (mMenuRow != null) {
+            mMenuRow.updateMenuAlpha(translationX, getMeasuredWidth());
         }
     }
 
@@ -1132,8 +1170,8 @@
 
             @Override
             public void onAnimationEnd(Animator anim) {
-                if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
-                    mSettingsIconRow.resetState();
+                if (!cancelled && mMenuRow != null && leftTarget == 0) {
+                    mMenuRow.resetState(true /* notify */);
                     mTranslateAnim = null;
                 }
             }
@@ -1143,17 +1181,17 @@
     }
 
     public float getSpaceForGear() {
-        if (mSettingsIconRow != null) {
-            return mSettingsIconRow.getSpaceForGear();
+        if (mMenuRow != null) {
+            return mMenuRow.getSpaceForMenu();
         }
         return 0;
     }
 
-    public NotificationSettingsIconRow getSettingsRow() {
-        if (mSettingsIconRow == null) {
-            mSettingsIconRowStub.inflate();
+    public NotificationMenuRow getSettingsRow() {
+        if (mMenuRow == null) {
+            mMenuRowStub.inflate();
         }
-        return mSettingsIconRow;
+        return mMenuRow;
     }
 
     public void inflateGuts() {
@@ -1238,6 +1276,7 @@
      */
     public void setUserExpanded(boolean userExpanded) {
         setUserExpanded(userExpanded, false /* allowChildExpansion */);
+        updateShelfIconColor();
     }
 
     /**
@@ -1297,6 +1336,7 @@
         if (expand != mIsSystemExpanded) {
             final boolean wasExpanded = isExpanded();
             mIsSystemExpanded = expand;
+            updateShelfIconColor();
             notifyHeightChanged(false /* needsAnimation */);
             logExpansionEvent(false, wasExpanded);
             if (mIsSummaryWithChildren) {
@@ -1353,6 +1393,8 @@
             return mGuts.getHeight();
         } else if ((isChildInGroup() && !isGroupExpanded())) {
             return mPrivateLayout.getMinHeight();
+        } else if (mShowAmbient) {
+            return getAmbientHeight();
         } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
             return getMinHeight();
         } else if (mIsSummaryWithChildren && !mOnKeyguard) {
@@ -1372,6 +1414,7 @@
         }
     }
 
+    @Override
     public boolean isGroupExpanded() {
         return mGroupManager.isGroupExpanded(mStatusBarNotification);
     }
@@ -1425,8 +1468,8 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         updateMaxHeights();
-        if (mSettingsIconRow != null) {
-            mSettingsIconRow.updateVerticalLocation();
+        if (mMenuRow != null) {
+            mMenuRow.updateVerticalLocation();
         }
         updateContentShiftHeight();
     }
@@ -1473,6 +1516,7 @@
         mSensitiveHiddenInGeneral = hideSensitive;
     }
 
+    @Override
     public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) {
         mHideSensitiveForIntrinsicHeight = hideSensitive;
         if (mIsSummaryWithChildren) {
@@ -1485,6 +1529,7 @@
         }
     }
 
+    @Override
     public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
             long duration) {
         boolean oldShowingPublic = mShowingPublic;
@@ -1549,6 +1594,7 @@
         }
     }
 
+    @Override
     public boolean mustStayOnScreen() {
         return mIsHeadsUp;
     }
@@ -1683,6 +1729,13 @@
         return showingLayout.getMinHeight();
     }
 
+    private int getAmbientHeight() {
+        NotificationContentView showingLayout = getShowingLayout();
+        return showingLayout.getAmbientChild() != null
+                ? showingLayout.getAmbientChild().getHeight()
+                : getCollapsedHeight();
+    }
+
     @Override
     public int getCollapsedHeight() {
         if (mIsSummaryWithChildren && !mShowingPublic) {
@@ -1814,10 +1867,16 @@
             } else if (isUserLocked()) {
                 return mChildrenContainer.getGroupExpandFraction();
             }
+        } else if (isColorized()) {
+            return -1.0f;
         }
         return 0.0f;
     }
 
+    private boolean isColorized() {
+        return mIsColorized;
+    }
+
     @Override
     protected boolean disallowSingleClick(MotionEvent event) {
         float x = event.getX();
@@ -1879,6 +1938,13 @@
         return mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf);
     }
 
+    public void setShowAmbient(boolean showAmbient) {
+        if (showAmbient != mShowAmbient) {
+            mShowAmbient = showAmbient;
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+    }
+
     public void setAboveShelf(boolean aboveShelf) {
         mAboveShelf = aboveShelf;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 37a900e..6a1f470 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -404,7 +404,9 @@
     }
 
     /**
-     * @return an amount between 0 and 1 of increased padding that this child needs
+     * @return an amount between -1 and 1 of increased padding that this child needs. 1 means it
+     * needs a full increased padding while -1 means it needs no padding at all. For 0.0f the normal
+     * padding is applied.
      */
     public float getIncreasedPaddingAmount() {
         return 0.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index f451aef..d599ec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -42,11 +42,12 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
 import com.android.systemui.statusbar.phone.LockIcon;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.UserInfoController;
 
 /**
  * Controls the indications and error messages shown on the Keyguard
@@ -83,6 +84,8 @@
     private int mChargingWattage;
     private String mMessageToShowOnScreenOn;
 
+    private KeyguardUpdateMonitorCallback mUpdateMonitor;
+
     private final DevicePolicyManager mDevicePolicyManager;
 
     public KeyguardIndicationController(Context context, ViewGroup indicationArea,
@@ -106,14 +109,31 @@
         mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
 
-        KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitor);
+        KeyguardUpdateMonitor.getInstance(context).registerCallback(getKeyguardCallback());
         context.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_TIME_TICK), null,
-                PhoneStatusBar.getTimeTickHandler(mContext));
+                Dependency.get(Dependency.TIME_TICK_HANDLER));
 
         updateDisclosure();
     }
 
+    /**
+     * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
+     * {@link KeyguardIndicationController}.
+     *
+     * <p>Subclasses may override this method to extend or change the callback behavior by extending
+     * the {@link BaseKeyguardCallback}.
+     *
+     * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
+     * same instance.
+     */
+    protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
+        if (mUpdateMonitor == null) {
+            mUpdateMonitor = new BaseKeyguardCallback();
+        }
+        return mUpdateMonitor;
+    }
+
     private void updateDisclosure() {
         if (mDevicePolicyManager == null) {
             return;
@@ -152,6 +172,12 @@
     }
 
     /**
+     * Sets the active controller managing changes and callbacks to user information.
+     */
+    public void setUserInfoController(UserInfoController userInfoController) {
+    }
+
+    /**
      * Hides transient indication in {@param delayMs}.
      */
     public void hideTransientIndicationDelayed(long delayMs) {
@@ -264,8 +290,37 @@
         }
     }
 
-    KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
-        public int mLastSuccessiveErrorMessage = -1;
+    public void setStatusBarKeyguardViewManager(
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+    }
+
+    BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mHandler.post(() -> {
+                if (mVisible) {
+                    updateIndication();
+                }
+            });
+        }
+    };
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
+                mTransientIndication = null;
+                updateIndication();
+            } else if (msg.what == MSG_CLEAR_FP_MSG) {
+                mLockIcon.setTransientFpError(false);
+                hideTransientIndication();
+            }
+        }
+    };
+
+    protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
+        private int mLastSuccessiveErrorMessage = -1;
 
         @Override
         public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
@@ -372,34 +427,4 @@
             }
         }
     };
-
-    BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mHandler.post(() -> {
-                if (mVisible) {
-                    updateIndication();
-                }
-            });
-        }
-    };
-
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
-                mTransientIndication = null;
-                updateIndication();
-            } else if (msg.what == MSG_CLEAR_FP_MSG) {
-                mLockIcon.setTransientFpError(false);
-                hideTransientIndication();
-            }
-        }
-    };
-
-    public void setStatusBarKeyguardViewManager(
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
-        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index ad6a5db..a1299e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -31,6 +31,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
@@ -52,6 +53,7 @@
     private static final int VISIBLE_TYPE_EXPANDED = 1;
     private static final int VISIBLE_TYPE_HEADSUP = 2;
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
+    private static final int VISIBLE_TYPE_AMBIENT = 4;
     public static final int UNDEFINED = -1;
 
     private final Rect mClipBounds = new Rect();
@@ -62,6 +64,7 @@
     private View mExpandedChild;
     private View mHeadsUpChild;
     private HybridNotificationView mSingleLineView;
+    private View mAmbientChild;
 
     private RemoteInputView mExpandedRemoteInput;
     private RemoteInputView mHeadsUpRemoteInput;
@@ -69,6 +72,7 @@
     private NotificationViewWrapper mContractedWrapper;
     private NotificationViewWrapper mExpandedWrapper;
     private NotificationViewWrapper mHeadsUpWrapper;
+    private NotificationViewWrapper mAmbientWrapper;
     private HybridGroupManager mHybridGroupManager;
     private int mClipTopAmount;
     private int mContentHeight;
@@ -81,6 +85,7 @@
     private int mSmallHeight;
     private int mHeadsUpHeight;
     private int mNotificationMaxHeight;
+    private int mNotificationAmbientHeight;
     private StatusBarNotification mStatusBarNotification;
     private NotificationGroupManager mGroupManager;
     private RemoteInputController mRemoteInputController;
@@ -124,6 +129,7 @@
     private boolean mHeadsUpAnimatingAway;
     private boolean mIconsVisible;
     private int mClipBottomAmount;
+    private boolean mIsLowPriority;
 
 
     public NotificationContentView(Context context, AttributeSet attrs) {
@@ -136,10 +142,12 @@
         reset();
     }
 
-    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
+    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
+            int ambientHeight) {
         mSmallHeight = smallHeight;
         mHeadsUpHeight = headsUpMaxHeight;
         mNotificationMaxHeight = maxHeight;
+        mNotificationAmbientHeight = ambientHeight;
     }
 
     @Override
@@ -156,20 +164,31 @@
         if (mExpandedChild != null) {
             int size = Math.min(maxSize, mNotificationMaxHeight);
             ViewGroup.LayoutParams layoutParams = mExpandedChild.getLayoutParams();
+            boolean useExactly = false;
             if (layoutParams.height >= 0) {
                 // An actual height is set
                 size = Math.min(maxSize, layoutParams.height);
+                useExactly = true;
             }
             int spec = size == Integer.MAX_VALUE
                     ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
-                    : MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
+                    : MeasureSpec.makeMeasureSpec(size, useExactly
+                            ? MeasureSpec.EXACTLY
+                            : MeasureSpec.AT_MOST);
             mExpandedChild.measure(widthMeasureSpec, spec);
             maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
         }
         if (mContractedChild != null) {
             int heightSpec;
             int size = Math.min(maxSize, mSmallHeight);
-            if (shouldContractedBeFixedSize()) {
+            ViewGroup.LayoutParams layoutParams = mContractedChild.getLayoutParams();
+            boolean useExactly = false;
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+                useExactly = true;
+            }
+            if (shouldContractedBeFixedSize() || useExactly) {
                 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
             } else {
                 heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
@@ -195,12 +214,15 @@
         if (mHeadsUpChild != null) {
             int size = Math.min(maxSize, mHeadsUpHeight);
             ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
+            boolean useExactly = false;
             if (layoutParams.height >= 0) {
                 // An actual height is set
                 size = Math.min(size, layoutParams.height);
+                useExactly = true;
             }
             mHeadsUpChild.measure(widthMeasureSpec,
-                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
+                    MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
+                            : MeasureSpec.AT_MOST));
             maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
         }
         if (mSingleLineView != null) {
@@ -215,6 +237,20 @@
                     MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
             maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
         }
+        if (mAmbientChild != null) {
+            int size = Math.min(maxSize, mNotificationAmbientHeight);
+            ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
+            boolean useExactly = false;
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+                useExactly = true;
+            }
+            mAmbientChild.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY
+                            : MeasureSpec.AT_MOST));
+            maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
+        }
         int ownHeight = Math.min(maxChildHeight, maxSize);
         setMeasuredDimension(width, ownHeight);
     }
@@ -293,10 +329,6 @@
     }
 
     public void reset() {
-        if (mContractedChild != null) {
-            mContractedChild.animate().cancel();
-            removeView(mContractedChild);
-        }
         mPreviousExpandedRemoteInputIntent = null;
         if (mExpandedRemoteInput != null) {
             mExpandedRemoteInput.onNotificationUpdateOrReset();
@@ -327,7 +359,6 @@
             removeView(mHeadsUpChild);
             mHeadsUpRemoteInput = null;
         }
-        mContractedChild = null;
         mExpandedChild = null;
         mHeadsUpChild = null;
     }
@@ -344,6 +375,10 @@
         return mHeadsUpChild;
     }
 
+    public View getAmbientChild() {
+        return mAmbientChild;
+    }
+
     public void setContractedChild(View child) {
         if (mContractedChild != null) {
             mContractedChild.animate().cancel();
@@ -378,6 +413,17 @@
                 mContainingNotification);
     }
 
+    public void setAmbientChild(View child) {
+        if (mAmbientChild != null) {
+            mAmbientChild.animate().cancel();
+            removeView(mAmbientChild);
+        }
+        addView(child);
+        mAmbientChild = child;
+        mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
+                mContainingNotification);
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -452,6 +498,11 @@
                         com.android.internal.R.dimen.notification_action_list_height);
         }
 
+        if (isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
+            return mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
+                    com.android.internal.R.dimen.notification_action_list_height);
+        }
+
         // Transition between heads-up & expanded, or pinned.
         if (mHeadsUpChild != null && mExpandedChild != null) {
             boolean transitioningBetweenHunAndExpanded =
@@ -573,7 +624,7 @@
     }
 
     public int getMinHeight(boolean likeGroupExpanded) {
-        if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
+        if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded() || mIsLowPriority) {
             return mContractedChild.getHeight();
         } else {
             return mSingleLineView.getHeight();
@@ -656,39 +707,26 @@
     }
 
     private void forceUpdateVisibilities() {
-        boolean contractedVisible = mVisibleType == VISIBLE_TYPE_CONTRACTED
-                || mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED;
-        boolean expandedVisible = mVisibleType == VISIBLE_TYPE_EXPANDED
-                || mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED;
-        boolean headsUpVisible = mVisibleType == VISIBLE_TYPE_HEADSUP
-                || mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP;
-        boolean singleLineVisible = mVisibleType == VISIBLE_TYPE_SINGLELINE
-                || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE;
-        if (!contractedVisible) {
-            mContractedChild.setVisibility(View.INVISIBLE);
+        forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
+        forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
+        // forceUpdateVisibilities cancels outstanding animations without updating the
+        // mAnimationStartVisibleType. Do so here instead.
+        mAnimationStartVisibleType = UNDEFINED;
+    }
+
+    private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
+        if (view == null) {
+            return;
+        }
+        boolean visible = mVisibleType == type
+                || mTransformationStartVisibleType == type;
+        if (!visible) {
+            view.setVisibility(INVISIBLE);
         } else {
-            mContractedWrapper.setVisible(true);
-        }
-        if (mExpandedChild != null) {
-            if (!expandedVisible) {
-                mExpandedChild.setVisibility(View.INVISIBLE);
-            } else {
-                mExpandedWrapper.setVisible(true);
-            }
-        }
-        if (mHeadsUpChild != null) {
-            if (!headsUpVisible) {
-                mHeadsUpChild.setVisibility(View.INVISIBLE);
-            } else {
-                mHeadsUpWrapper.setVisible(true);
-            }
-        }
-        if (mSingleLineView != null) {
-            if (!singleLineVisible) {
-                mSingleLineView.setVisibility(View.INVISIBLE);
-            } else {
-                mSingleLineView.setVisible(true);
-            }
+            wrapper.setVisible(true);
         }
     }
 
@@ -722,19 +760,25 @@
     }
 
     private void updateViewVisibilities(int visibleType) {
-        boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
-        mContractedWrapper.setVisible(contractedVisible);
-        if (mExpandedChild != null) {
-            boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
-            mExpandedWrapper.setVisible(expandedVisible);
-        }
-        if (mHeadsUpChild != null) {
-            boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
-            mHeadsUpWrapper.setVisible(headsUpVisible);
-        }
-        if (mSingleLineView != null) {
-            boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
-            mSingleLineView.setVisible(singleLineVisible);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
+                mContractedChild, mContractedWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
+                mExpandedChild, mExpandedWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
+                mHeadsUpChild, mHeadsUpWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
+                mSingleLineView, mSingleLineView);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
+                mAmbientChild, mAmbientWrapper);
+        // updateViewVisibilities cancels outstanding animations without updating the
+        // mAnimationStartVisibleType. Do so here instead.
+        mAnimationStartVisibleType = UNDEFINED;
+    }
+
+    private void updateViewVisibility(int visibleType, int type, View view,
+            TransformableView wrapper) {
+        if (view != null) {
+            wrapper.setVisible(visibleType == type);
         }
     }
 
@@ -784,6 +828,8 @@
                 return mHeadsUpWrapper;
             case VISIBLE_TYPE_SINGLELINE:
                 return mSingleLineView;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientWrapper;
             default:
                 return mContractedWrapper;
         }
@@ -801,6 +847,8 @@
                 return mHeadsUpChild;
             case VISIBLE_TYPE_SINGLELINE:
                 return mSingleLineView;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientChild;
             default:
                 return mContractedChild;
         }
@@ -814,6 +862,8 @@
                 return mHeadsUpWrapper;
             case VISIBLE_TYPE_CONTRACTED:
                 return mContractedWrapper;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientWrapper;
             default:
                 return null;
         }
@@ -823,6 +873,10 @@
      * @return one of the static enum types in this view, calculated form the current state
      */
     public int calculateVisibleType() {
+        if (mDark && !mIsChildInGroup) {
+            // TODO: Handle notification groups
+            return VISIBLE_TYPE_AMBIENT;
+        }
         if (mUserExpanding) {
             int height = !mIsChildInGroup || isGroupExpanded()
                     || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
@@ -832,7 +886,7 @@
                 height = mContentHeight;
             }
             int expandedVisualType = getVisualTypeForHeight(height);
-            int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
+            int collapsedVisualType = mIsChildInGroup && !isGroupExpanded() && !mIsLowPriority
                     ? VISIBLE_TYPE_SINGLELINE
                     : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
             return mTransformationStartVisibleType == collapsedVisualType
@@ -853,7 +907,7 @@
         if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
             return VISIBLE_TYPE_EXPANDED;
         }
-        if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
+        if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded() && !mIsLowPriority) {
             return VISIBLE_TYPE_SINGLELINE;
         }
 
@@ -895,6 +949,7 @@
         if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
             mSingleLineView.setDark(dark, fade, delay);
         }
+        selectLayout(!dark && fade /* animate */, false /* force */);
     }
 
     public void setHeadsUp(boolean headsUp) {
@@ -937,16 +992,19 @@
         mStatusBarNotification = entry.notification;
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateSingleLineView();
-        applyRemoteInput(entry);
         if (mContractedChild != null) {
-            mContractedWrapper.notifyContentUpdated(entry.notification);
+            mContractedWrapper.notifyContentUpdated(entry.notification, mIsLowPriority);
         }
         if (mExpandedChild != null) {
-            mExpandedWrapper.notifyContentUpdated(entry.notification);
+            mExpandedWrapper.notifyContentUpdated(entry.notification, mIsLowPriority);
         }
         if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.notifyContentUpdated(entry.notification);
+            mHeadsUpWrapper.notifyContentUpdated(entry.notification, mIsLowPriority);
         }
+        if (mAmbientChild != null) {
+            mAmbientWrapper.notifyContentUpdated(entry.notification, mIsLowPriority);
+        }
+        applyRemoteInput(entry);
         updateShowingLegacyBackground();
         mForceSelectNextLayout = true;
         setDark(mDark, false /* animate */, 0 /* delay */);
@@ -988,7 +1046,8 @@
         View bigContentView = mExpandedChild;
         if (bigContentView != null) {
             mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
-                    mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput);
+                    mPreviousExpandedRemoteInputIntent, mCachedExpandedRemoteInput,
+                    mExpandedWrapper);
         } else {
             mExpandedRemoteInput = null;
         }
@@ -1002,7 +1061,7 @@
         View headsUpContentView = mHeadsUpChild;
         if (headsUpContentView != null) {
             mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
-                    mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput);
+                    mPreviousHeadsUpRemoteInputIntent, mCachedHeadsUpRemoteInput, mHeadsUpWrapper);
         } else {
             mHeadsUpRemoteInput = null;
         }
@@ -1016,7 +1075,7 @@
 
     private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
             boolean hasRemoteInput, PendingIntent existingPendingIntent,
-            RemoteInputView cachedView) {
+            RemoteInputView cachedView, NotificationViewWrapper wrapper) {
         View actionContainerCandidate = view.findViewById(
                 com.android.internal.R.id.actions_container);
         if (actionContainerCandidate instanceof FrameLayout) {
@@ -1055,6 +1114,8 @@
                         mContext.getColor(R.color.remote_input_text_enabled),
                         mContext.getColor(R.color.remote_input_hint)));
 
+                existing.setWrapper(wrapper);
+
                 if (existingPendingIntent != null || existing.isActive()) {
                     // The current action could be gone, or the pending intent no longer valid.
                     // If we find a matching action in the new notification, focus, otherwise close.
@@ -1133,6 +1194,9 @@
         if (header == null && mHeadsUpChild != null) {
             header = mHeadsUpWrapper.getNotificationHeader();
         }
+        if (header == null && mAmbientChild != null) {
+            header = mAmbientWrapper.getNotificationHeader();
+        }
         return header;
     }
 
@@ -1200,6 +1264,11 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isAnimatingVisibleType() {
+        return mAnimationStartVisibleType != UNDEFINED;
+    }
+
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
         mHeadsUpAnimatingAway = headsUpAnimatingAway;
         selectLayout(false /* animate */, true /* force */);
@@ -1234,4 +1303,8 @@
             }
         }
     }
+
+    public void setIsLowPriority(boolean isLowPriority) {
+        mIsLowPriority = isLowPriority;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index a6e730d..8c04a1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.Context;
 import android.graphics.drawable.Icon;
@@ -55,6 +56,7 @@
         private static final int COLOR_INVALID = 1;
         public String key;
         public StatusBarNotification notification;
+        public NotificationChannel channel;
         public StatusBarIconView icon;
         public StatusBarIconView expandedIcon;
         public ExpandableNotificationRow row; // the outer expanded view
@@ -67,6 +69,7 @@
         public RemoteViews cachedBigContentView;
         public RemoteViews cachedHeadsUpContentView;
         public RemoteViews cachedPublicContentView;
+        public RemoteViews cachedAmbientContentView;
         public CharSequence remoteInputText;
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -114,18 +117,22 @@
             return row.getPublicLayout().getContractedChild();
         }
 
-        public boolean cacheContentViews(Context ctx, Notification updatedNotification) {
+        public boolean cacheContentViews(Context ctx, Notification updatedNotification,
+                boolean isLowPriority) {
             boolean applyInPlace = false;
             if (updatedNotification != null) {
                 final Notification.Builder updatedNotificationBuilder
                         = Notification.Builder.recoverBuilder(ctx, updatedNotification);
-                final RemoteViews newContentView = updatedNotificationBuilder.createContentView();
-                final RemoteViews newBigContentView =
-                        updatedNotificationBuilder.createBigContentView();
+                final RemoteViews newContentView = createContentView(updatedNotificationBuilder,
+                        isLowPriority);
+                final RemoteViews newBigContentView = createBigContentView(
+                        updatedNotificationBuilder, isLowPriority);
                 final RemoteViews newHeadsUpContentView =
                         updatedNotificationBuilder.createHeadsUpContentView();
                 final RemoteViews newPublicNotification
                         = updatedNotificationBuilder.makePublicContentView();
+                final RemoteViews newAmbientNotification
+                        = updatedNotificationBuilder.makeAmbientNotification();
 
                 boolean sameCustomView = Objects.equals(
                         notification.getNotification().extras.getBoolean(
@@ -136,25 +143,50 @@
                         && compareRemoteViews(cachedBigContentView, newBigContentView)
                         && compareRemoteViews(cachedHeadsUpContentView, newHeadsUpContentView)
                         && compareRemoteViews(cachedPublicContentView, newPublicNotification)
+                        && compareRemoteViews(cachedAmbientContentView, newAmbientNotification)
                         && sameCustomView;
                 cachedPublicContentView = newPublicNotification;
                 cachedHeadsUpContentView = newHeadsUpContentView;
                 cachedBigContentView = newBigContentView;
                 cachedContentView = newContentView;
+                cachedAmbientContentView = newAmbientNotification;
             } else {
                 final Notification.Builder builder
                         = Notification.Builder.recoverBuilder(ctx, notification.getNotification());
 
-                cachedContentView = builder.createContentView();
-                cachedBigContentView = builder.createBigContentView();
+                cachedContentView = createContentView(builder, isLowPriority);
+                cachedBigContentView = createBigContentView(builder, isLowPriority);
                 cachedHeadsUpContentView = builder.createHeadsUpContentView();
                 cachedPublicContentView = builder.makePublicContentView();
+                cachedAmbientContentView = builder.makeAmbientNotification();
 
                 applyInPlace = false;
             }
             return applyInPlace;
         }
 
+        private RemoteViews createBigContentView(Notification.Builder builder,
+                boolean isLowPriority) {
+            RemoteViews bigContentView = builder.createBigContentView();
+            if (bigContentView != null) {
+                return bigContentView;
+            }
+            if (isLowPriority) {
+                RemoteViews contentView = builder.createContentView();
+                Notification.Builder.makeHeaderExpanded(contentView);
+                return contentView;
+            }
+            return null;
+        }
+
+        private RemoteViews createContentView(Notification.Builder builder,
+                boolean isAmbient) {
+            if (isAmbient) {
+                return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
+            }
+            return builder.createContentView();
+        }
+
         // Returns true if the RemoteViews are the same.
         private boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
             return (a == null && b == null) ||
@@ -248,8 +280,9 @@
             }
         }
 
-        public int getContrastedColor(Context context) {
-            int rawColor = notification.getNotification().color;
+        public int getContrastedColor(Context context, boolean ambient) {
+            int rawColor = ambient ? Notification.COLOR_DEFAULT :
+                    notification.getNotification().color;
             if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
                 return mCachedContrastColor;
             }
@@ -423,6 +456,14 @@
          return null;
     }
 
+    public NotificationChannel getChannel(String key) {
+        if (mRankingMap != null) {
+            mRankingMap.getRanking(key, mTmpRanking);
+            return mTmpRanking.getChannel();
+        }
+        return null;
+    }
+
     private void updateRankingAndSort(RankingMap ranking) {
         if (ranking != null) {
             mRankingMap = ranking;
@@ -436,6 +477,7 @@
                         entry.notification.setOverrideGroupKey(overrideGroupKey);
                         mGroupManager.onEntryUpdated(entry, oldSbn);
                     }
+                    entry.channel = getChannel(entry.key);
                 }
             }
         }
@@ -488,20 +530,6 @@
         return false;
     }
 
-    /**
-     * Return whether there are any clearable notifications (that aren't errors).
-     */
-    public boolean hasActiveClearableNotifications() {
-        for (Entry e : mSortedAndFiltered) {
-            if (e.getContentView() != null) { // the view successfully inflated
-                if (e.notification.isClearable()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     // Q: What kinds of notifications should show during setup?
     // A: Almost none! Only things coming from the system (package is "android") that also
     // have special "kind" tags marking them as relevant for setup (see below).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index c7adb60..f6056dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -40,6 +40,7 @@
 import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
 import android.widget.CompoundButton;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RadioButton;
@@ -53,6 +54,8 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsContent;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 import java.util.Set;
@@ -60,7 +63,8 @@
 /**
  * The guts of a notification revealed when performing a long press.
  */
-public class NotificationGuts extends LinearLayout {
+public class NotificationGuts extends FrameLayout
+        implements NotificationMenuRowProvider.GutsInteractionListener {
     private static final String TAG = "NotificationGuts";
     private static final long CLOSE_GUTS_DELAY = 8000;
 
@@ -69,28 +73,14 @@
     private int mClipBottomAmount;
     private int mActualHeight;
     private boolean mExposed;
-    private INotificationManager mINotificationManager;
-    private int mStartingUserImportance;
-    private StatusBarNotification mStatusBarNotification;
-
-    private ImageView mAutoButton;
-    private TextView mImportanceSummary;
-    private TextView mImportanceTitle;
-    private boolean mAuto;
-
-    private View mImportanceGroup;
-    private View mChannelDisabled;
-    private Switch mChannelEnabledSwitch;
-    private RadioButton mMinImportanceButton;
-    private RadioButton mLowImportanceButton;
-    private RadioButton mDefaultImportanceButton;
-    private RadioButton mHighImportanceButton;
 
     private Handler mHandler;
     private Runnable mFalsingCheck;
     private boolean mNeedsFalsingProtection;
     private OnGutsClosedListener mListener;
 
+    private GutsContent mGutsContent;
+
     public interface OnGutsClosedListener {
         public void onGutsClosed(NotificationGuts guts);
     }
@@ -107,11 +97,22 @@
                 }
             }
         };
-        final TypedArray ta =
-                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Theme, 0, 0);
+        final TypedArray ta = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.Theme, 0, 0);
         ta.recycle();
     }
 
+    public NotificationGuts(Context context) {
+        this(context, null);
+
+    }
+
+    public void setGutsContent(GutsContent content) {
+        mGutsContent = content;
+        removeAllViews();
+        addView(mGutsContent.getContentView());
+    }
+
     public void resetFalsingCheck() {
         mHandler.removeCallbacks(mFalsingCheck);
         if (mNeedsFalsingProtection && mExposed) {
@@ -169,213 +170,23 @@
         void onClick(View v, int appUid);
     }
 
-    void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager,
-            final StatusBarNotification sbn, OnSettingsClickListener onSettingsClick,
-            OnClickListener onDoneClick, final Set<String> nonBlockablePkgs) {
-        mINotificationManager = iNotificationManager;
-        mStatusBarNotification = sbn;
-        final NotificationChannel channel = sbn.getNotificationChannel();
-        mStartingUserImportance = channel.getImportance();
-
-        final String pkg = sbn.getPackageName();
-        int appUid = -1;
-        String appname = pkg;
-        Drawable pkgicon = null;
-        try {
-            final ApplicationInfo info = pm.getApplicationInfo(pkg,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES
-                            | PackageManager.MATCH_DISABLED_COMPONENTS
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
-            if (info != null) {
-                appUid = info.uid;
-                appname = String.valueOf(pm.getApplicationLabel(info));
-                pkgicon = pm.getApplicationIcon(info);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // app is gone, just show package name and generic icon
-            pkgicon = pm.getDefaultActivityIcon();
-        }
-
-        // If this is the placeholder channel, don't use our channel-specific text.
-        String appNameText;
-        CharSequence channelNameText;
-        if (channel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
-            appNameText = appname;
-            channelNameText = mContext.getString(R.string.notification_header_default_channel);
-        } else {
-            appNameText = mContext.getString(R.string.notification_importance_header_app, appname);
-            channelNameText = channel.getName();
-        }
-        ((TextView) findViewById(R.id.pkgname)).setText(appNameText);
-        ((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
-
-        // Settings button.
-        final TextView settingsButton = (TextView) findViewById(R.id.more_settings);
-        if (appUid >= 0 && onSettingsClick != null) {
-            final int appUidF = appUid;
-            settingsButton.setOnClickListener(
-                    (View view) -> { onSettingsClick.onClick(view, appUidF); });
-            settingsButton.setText(R.string.notification_more_settings);
-        } else {
-            settingsButton.setVisibility(View.GONE);
-        }
-
-        // Done button.
-        final TextView doneButton = (TextView) findViewById(R.id.done);
-        doneButton.setText(R.string.notification_done);
-        doneButton.setOnClickListener(onDoneClick);
-
-        boolean nonBlockable = false;
-        try {
-            final PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
-            nonBlockable = Utils.isSystemPackage(getResources(), pm, info);
-        } catch (PackageManager.NameNotFoundException e) {
-            // unlikely.
-        }
-        if (nonBlockablePkgs != null) {
-            nonBlockable |= nonBlockablePkgs.contains(pkg);
-        }
-
-        final View importanceButtons = findViewById(R.id.importance_buttons);
-        bindToggles(importanceButtons, mStartingUserImportance, nonBlockable);
-
-        // Importance Text (hardcoded to 4 importance levels)
-        final ViewGroup importanceTextGroup =
-                (ViewGroup) findViewById(R.id.importance_buttons_text);
-        final int size = importanceTextGroup.getChildCount();
-        for (int i = 0; i < size; i++) {
-            int importanceNameResId = 0;
-            int importanceDescResId = 0;
-            switch (i) {
-                case 0:
-                    importanceNameResId = R.string.high_importance;
-                    importanceDescResId = R.string.notification_importance_high;
-                    break;
-                case 1:
-                    importanceNameResId = R.string.default_importance;
-                    importanceDescResId = R.string.notification_importance_default;
-                    break;
-                case 2:
-                    importanceNameResId = R.string.low_importance;
-                    importanceDescResId = R.string.notification_importance_low;
-                    break;
-                case 3:
-                    importanceNameResId = R.string.min_importance;
-                    importanceDescResId = R.string.notification_importance_min;
-                    break;
-                default:
-                    Log.e(TAG, "Too many importance groups in this layout.");
-                    break;
-            }
-            final ViewGroup importanceChildGroup = (ViewGroup) importanceTextGroup.getChildAt(i);
-            ((TextView) importanceChildGroup.getChildAt(0)).setText(importanceNameResId);
-            ((TextView) importanceChildGroup.getChildAt(1)).setText(importanceDescResId);
-        }
-
-        // Top-level importance group
-        mImportanceGroup = findViewById(R.id.importance);
-        mChannelDisabled = findViewById(R.id.channel_disabled);
-        updateImportanceGroup();
-    }
-
-    public boolean hasImportanceChanged() {
-        return mStartingUserImportance != getSelectedImportance();
-    }
-
-    private void saveImportance() {
-        int selectedImportance = getSelectedImportance();
-        if (selectedImportance == mStartingUserImportance) {
-            return;
-        }
-        final NotificationChannel channel = mStatusBarNotification.getNotificationChannel();
-        MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
-                selectedImportance - mStartingUserImportance);
-        channel.setImportance(selectedImportance);
-        try {
-            mINotificationManager.updateNotificationChannelForPackage(
-                    mStatusBarNotification.getPackageName(), mStatusBarNotification.getUid(),
-                    channel);
-        } catch (RemoteException e) {
-            // :(
-        }
-    }
-
-    private int getSelectedImportance() {
-        if (!mChannelEnabledSwitch.isChecked()) {
-            return NotificationManager.IMPORTANCE_NONE;
-        } else if (mMinImportanceButton.isChecked()) {
-            return NotificationManager.IMPORTANCE_MIN;
-        } else if (mLowImportanceButton.isChecked()) {
-            return NotificationManager.IMPORTANCE_LOW;
-        } else if (mDefaultImportanceButton.isChecked()) {
-            return NotificationManager.IMPORTANCE_DEFAULT;
-        } else if (mHighImportanceButton.isChecked()) {
-            return NotificationManager.IMPORTANCE_HIGH;
-        } else {
-            return NotificationManager.IMPORTANCE_UNSPECIFIED;
-        }
-    }
-
-    private void bindToggles(final View importanceButtons, final int importance,
-            final boolean nonBlockable) {
-        // Enabled Switch
-        mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
-        mChannelEnabledSwitch.setChecked(importance != NotificationManager.IMPORTANCE_NONE);
-        mChannelEnabledSwitch.setVisibility(nonBlockable ? View.INVISIBLE : View.VISIBLE);
-
-        // Importance Buttons
-        mMinImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.min_importance);
-        mLowImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.low_importance);
-        mDefaultImportanceButton =
-                (RadioButton) importanceButtons.findViewById(R.id.default_importance);
-        mHighImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.high_importance);
-
-        // Set to current importance setting
-        switch (importance) {
-            case NotificationManager.IMPORTANCE_UNSPECIFIED:
-            case NotificationManager.IMPORTANCE_NONE:
-                break;
-            case NotificationManager.IMPORTANCE_MIN:
-                mMinImportanceButton.setChecked(true);
-                break;
-            case NotificationManager.IMPORTANCE_LOW:
-                mLowImportanceButton.setChecked(true);
-                break;
-            case NotificationManager.IMPORTANCE_DEFAULT:
-                mDefaultImportanceButton.setChecked(true);
-                break;
-            case NotificationManager.IMPORTANCE_HIGH:
-            case NotificationManager.IMPORTANCE_MAX:
-                mHighImportanceButton.setChecked(true);
-                break;
-        }
-
-        // Callback when checked.
-        mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
-            resetFalsingCheck();
-            updateImportanceGroup();
-        });
-        ((RadioGroup) importanceButtons).setOnCheckedChangeListener(
-                (buttonView, isChecked) -> { resetFalsingCheck(); });
-    }
-
-    private void updateImportanceGroup() {
-        final boolean disabled = getSelectedImportance() == NotificationManager.IMPORTANCE_NONE;
-        mImportanceGroup.setVisibility(disabled ? View.GONE : View.VISIBLE);
-        mChannelDisabled.setVisibility(disabled ? View.VISIBLE : View.GONE);
-    }
-
     public void closeControls(int x, int y, boolean saveImportance) {
-        if (saveImportance) {
-            saveImportance();
-        }
         if (getWindowToken() == null) {
             if (mListener != null) {
                 mListener.onGutsClosed(this);
             }
             return;
         }
+        if (mGutsContent == null || !mGutsContent.handleCloseControls()) {
+            animateClose(x, y);
+        }
+        setExposed(false, mNeedsFalsingProtection);
+        if (mListener != null) {
+            mListener.onGutsClosed(this);
+        }
+    }
+
+    private void animateClose(int x, int y) {
         if (x == -1 || y == -1) {
             x = (getLeft() + getRight()) / 2;
             y = (getTop() + getHeight() / 2);
@@ -395,10 +206,6 @@
             }
         });
         a.start();
-        setExposed(false, mNeedsFalsingProtection);
-        if (mListener != null) {
-            mListener.onGutsClosed(this);
-        }
     }
 
     public void setActualHeight(int actualHeight) {
@@ -443,4 +250,14 @@
     public boolean isExposed() {
         return mExposed;
     }
+
+    @Override
+    public void onInteraction(View view) {
+        resetFalsingCheck();
+    }
+
+    @Override
+    public void closeGuts(View view) {
+        closeControls(-1 /* x */, -1 /* y */, true /* notify */);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
new file mode 100644
index 0000000..0989846
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -0,0 +1,319 @@
+package com.android.systemui.statusbar;
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.INotificationManager;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.SeekBar;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsContent;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsInteractionListener;
+import com.android.systemui.statusbar.NotificationGuts.OnSettingsClickListener;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
+
+import java.util.Set;
+
+/**
+ * The guts of a notification revealed when performing a long press.
+ */
+public class NotificationInfo extends LinearLayout implements GutsContent {
+    private static final String TAG = "InfoGuts";
+
+    private INotificationManager mINotificationManager;
+    private int mStartingUserImportance;
+    private StatusBarNotification mStatusBarNotification;
+    private NotificationChannel mNotificationChannel;
+
+    private ImageView mAutoButton;
+    private TextView mImportanceSummary;
+    private TextView mImportanceTitle;
+    private boolean mAuto;
+
+    private View mImportanceGroup;
+    private View mChannelDisabled;
+    private Switch mChannelEnabledSwitch;
+    private RadioButton mMinImportanceButton;
+    private RadioButton mLowImportanceButton;
+    private RadioButton mDefaultImportanceButton;
+    private RadioButton mHighImportanceButton;
+
+    private GutsInteractionListener mGutsInteractionListener;
+
+    public NotificationInfo(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    interface OnSettingsClickListener {
+        void onClick(View v, int appUid);
+    }
+
+    void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager,
+            final StatusBarNotification sbn, final NotificationChannel channel,
+            OnSettingsClickListener onSettingsClick,
+            OnClickListener onDoneClick, final Set<String> nonBlockablePkgs) {
+        mINotificationManager = iNotificationManager;
+        mNotificationChannel = channel;
+        mStatusBarNotification = sbn;
+        mStartingUserImportance = channel.getImportance();
+
+        final String pkg = sbn.getPackageName();
+        int appUid = -1;
+        String appname = pkg;
+        Drawable pkgicon = null;
+        try {
+            final ApplicationInfo info = pm.getApplicationInfo(pkg,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+            if (info != null) {
+                appUid = info.uid;
+                appname = String.valueOf(pm.getApplicationLabel(info));
+                pkgicon = pm.getApplicationIcon(info);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // app is gone, just show package name and generic icon
+            pkgicon = pm.getDefaultActivityIcon();
+        }
+
+        // If this is the placeholder channel, don't use our channel-specific text.
+        String appNameText;
+        CharSequence channelNameText;
+        if (channel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+            appNameText = appname;
+            channelNameText = mContext.getString(R.string.notification_header_default_channel);
+        } else {
+            appNameText = mContext.getString(R.string.notification_importance_header_app, appname);
+            channelNameText = channel.getName();
+        }
+        ((TextView) findViewById(R.id.pkgname)).setText(appNameText);
+        ((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
+
+        // Settings button.
+        final TextView settingsButton = (TextView) findViewById(R.id.more_settings);
+        if (appUid >= 0 && onSettingsClick != null) {
+            final int appUidF = appUid;
+            settingsButton.setOnClickListener(
+                    (View view) -> {
+                        onSettingsClick.onClick(view, appUidF);
+                    });
+            settingsButton.setText(R.string.notification_more_settings);
+        } else {
+            settingsButton.setVisibility(View.GONE);
+        }
+
+        // Done button.
+        final TextView doneButton = (TextView) findViewById(R.id.done);
+        doneButton.setText(R.string.notification_done);
+        doneButton.setOnClickListener(onDoneClick);
+
+        boolean nonBlockable = false;
+        try {
+            final PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
+            nonBlockable = Utils.isSystemPackage(getResources(), pm, info);
+        } catch (PackageManager.NameNotFoundException e) {
+            // unlikely.
+        }
+        if (nonBlockablePkgs != null) {
+            nonBlockable |= nonBlockablePkgs.contains(pkg);
+        }
+
+        final View importanceButtons = findViewById(R.id.importance_buttons);
+        bindToggles(importanceButtons, mStartingUserImportance, nonBlockable);
+
+        // Importance Text (hardcoded to 4 importance levels)
+        final ViewGroup importanceTextGroup = (ViewGroup) findViewById(
+                R.id.importance_buttons_text);
+        final int size = importanceTextGroup.getChildCount();
+        for (int i = 0; i < size; i++) {
+            int importanceNameResId = 0;
+            int importanceDescResId = 0;
+            switch (i) {
+                case 0:
+                    importanceNameResId = R.string.high_importance;
+                    importanceDescResId = R.string.notification_importance_high;
+                    break;
+                case 1:
+                    importanceNameResId = R.string.default_importance;
+                    importanceDescResId = R.string.notification_importance_default;
+                    break;
+                case 2:
+                    importanceNameResId = R.string.low_importance;
+                    importanceDescResId = R.string.notification_importance_low;
+                    break;
+                case 3:
+                    importanceNameResId = R.string.min_importance;
+                    importanceDescResId = R.string.notification_importance_min;
+                    break;
+                default:
+                    Log.e(TAG, "Too many importance groups in this layout.");
+                    break;
+            }
+            final ViewGroup importanceChildGroup = (ViewGroup) importanceTextGroup.getChildAt(i);
+            ((TextView) importanceChildGroup.getChildAt(0)).setText(importanceNameResId);
+            ((TextView) importanceChildGroup.getChildAt(1)).setText(importanceDescResId);
+        }
+
+        // Top-level importance group
+        mImportanceGroup = findViewById(R.id.importance);
+        mChannelDisabled = findViewById(R.id.channel_disabled);
+        updateImportanceGroup();
+    }
+
+    public boolean hasImportanceChanged() {
+        return mStartingUserImportance != getSelectedImportance();
+    }
+
+    public void saveImportance() {
+        int selectedImportance = getSelectedImportance();
+        if (selectedImportance == mStartingUserImportance) {
+            return;
+        }
+        MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
+                selectedImportance - mStartingUserImportance);
+        mNotificationChannel.setImportance(selectedImportance);
+        try {
+            mINotificationManager.updateNotificationChannelForPackage(
+                    mStatusBarNotification.getPackageName(), mStatusBarNotification.getUid(),
+                    mNotificationChannel);
+        } catch (RemoteException e) {
+            // :(
+        }
+    }
+
+    private int getSelectedImportance() {
+        if (!mChannelEnabledSwitch.isChecked()) {
+            return NotificationManager.IMPORTANCE_NONE;
+        } else if (mMinImportanceButton.isChecked()) {
+            return NotificationManager.IMPORTANCE_MIN;
+        } else if (mLowImportanceButton.isChecked()) {
+            return NotificationManager.IMPORTANCE_LOW;
+        } else if (mDefaultImportanceButton.isChecked()) {
+            return NotificationManager.IMPORTANCE_DEFAULT;
+        } else if (mHighImportanceButton.isChecked()) {
+            return NotificationManager.IMPORTANCE_HIGH;
+        } else {
+            return NotificationManager.IMPORTANCE_UNSPECIFIED;
+        }
+    }
+
+    private void bindToggles(final View importanceButtons, final int importance,
+            final boolean nonBlockable) {
+        // Enabled Switch
+        mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
+        mChannelEnabledSwitch.setChecked(importance != NotificationManager.IMPORTANCE_NONE);
+        mChannelEnabledSwitch.setVisibility(nonBlockable ? View.INVISIBLE : View.VISIBLE);
+
+        // Importance Buttons
+        mMinImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.min_importance);
+        mLowImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.low_importance);
+        mDefaultImportanceButton = (RadioButton) importanceButtons
+                .findViewById(R.id.default_importance);
+        mHighImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.high_importance);
+
+        // Set to current importance setting
+        switch (importance) {
+            case NotificationManager.IMPORTANCE_UNSPECIFIED:
+            case NotificationManager.IMPORTANCE_NONE:
+                break;
+            case NotificationManager.IMPORTANCE_MIN:
+                mMinImportanceButton.setChecked(true);
+                break;
+            case NotificationManager.IMPORTANCE_LOW:
+                mLowImportanceButton.setChecked(true);
+                break;
+            case NotificationManager.IMPORTANCE_DEFAULT:
+                mDefaultImportanceButton.setChecked(true);
+                break;
+            case NotificationManager.IMPORTANCE_HIGH:
+            case NotificationManager.IMPORTANCE_MAX:
+                mHighImportanceButton.setChecked(true);
+                break;
+        }
+
+        // Callback when checked.
+        mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
+            mGutsInteractionListener.onInteraction(NotificationInfo.this);
+            updateImportanceGroup();
+        });
+        ((RadioGroup) importanceButtons).setOnCheckedChangeListener(
+                (buttonView, isChecked) -> {
+                    mGutsInteractionListener.onInteraction(NotificationInfo.this);
+                });
+    }
+
+    private void updateImportanceGroup() {
+        final boolean disabled = getSelectedImportance() == NotificationManager.IMPORTANCE_NONE;
+        mImportanceGroup.setVisibility(disabled ? View.GONE : View.VISIBLE);
+        mChannelDisabled.setVisibility(disabled ? View.VISIBLE : View.GONE);
+    }
+
+    public void closeControls() {
+        if (mGutsInteractionListener != null) {
+            mGutsInteractionListener.closeGuts(this);
+        }
+    }
+
+    @Override
+    public void setInteractionListener(GutsInteractionListener listener) {
+        mGutsInteractionListener = listener;
+    }
+
+    @Override
+    public View getContentView() {
+        return this;
+    }
+
+    @Override
+    public boolean handleCloseControls() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
new file mode 100644
index 0000000..fad63dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.OnMenuClickListener;
+
+public class NotificationMenuRow extends FrameLayout
+        implements PluginListener<NotificationMenuRowProvider>, View.OnClickListener {
+
+    private static final int ICON_ALPHA_ANIM_DURATION = 200;
+
+    private ExpandableNotificationRow mParent;
+    private OnMenuClickListener mListener;
+    private NotificationMenuRowProvider mMenuProvider;
+    private ArrayList<MenuItem> mMenuItems = new ArrayList<>();
+
+    private ValueAnimator mFadeAnimator;
+    private boolean mMenuFadedIn = false;
+    private boolean mAnimating = false;
+    private boolean mOnLeft = true;
+    private boolean mDismissing = false;
+    private boolean mSnapping = false;
+    private boolean mIconsPlaced = false;
+
+    private int[] mIconLocation = new int[2];
+    private int[] mParentLocation = new int[2];
+
+    private float mHorizSpaceForIcon;
+    private int mVertSpaceForIcons;
+
+    private int mIconPadding;
+    private int mIconTint;
+
+    private float mAlpha = 0f;
+
+    public NotificationMenuRow(Context context) {
+        this(context, null);
+    }
+
+    public NotificationMenuRow(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NotificationMenuRow(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public NotificationMenuRow(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs);
+        PluginManager.getInstance(getContext()).addPluginListener(
+                NotificationMenuRowProvider.ACTION, this,
+                NotificationMenuRowProvider.VERSION, false /* Allow multiple */);
+        mMenuItems.addAll(getDefaultNotificationMenuItems());
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        final Resources res = getResources();
+        mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
+        mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
+        mIconPadding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
+        mIconTint = res.getColor(R.color.notification_gear_color);
+        updateMenu(false /* notify */);
+    }
+
+    public static MenuItem getLongpressMenuItem(Context context) {
+        Resources res = context.getResources();
+        Drawable settingsIcon = res.getDrawable(R.drawable.ic_settings);
+        String settingsDescription = res.getString(R.string.notification_menu_gear_description);
+        NotificationInfo settingsContent = (NotificationInfo) LayoutInflater.from(context).inflate(
+                R.layout.notification_info, null, false);
+        MenuItem settings = new MenuItem(settingsIcon, settingsDescription, settingsContent);
+        return settings;
+    }
+
+    public ArrayList<MenuItem> getDefaultNotificationMenuItems() {
+        ArrayList<MenuItem> items = new ArrayList<MenuItem>();
+        Resources res = getResources();
+
+        Drawable snoozeIcon = res.getDrawable(R.drawable.ic_snooze);
+        NotificationSnooze content = (NotificationSnooze) LayoutInflater.from(mContext)
+                .inflate(R.layout.notification_snooze, null, false);
+        String snoozeDescription = res.getString(R.string.notification_menu_snooze_description);
+        MenuItem snooze = new MenuItem(snoozeIcon, snoozeDescription, content);
+        items.add(snooze);
+
+        Drawable settingsIcon = res.getDrawable(R.drawable.ic_settings);
+        String settingsDescription = res.getString(R.string.notification_menu_gear_description);
+        NotificationInfo settingsContent = (NotificationInfo) LayoutInflater.from(mContext).inflate(
+                R.layout.notification_info, null, false);
+        MenuItem settings = new MenuItem(settingsIcon, settingsDescription, settingsContent);
+        items.add(settings);
+        return items;
+    }
+
+    private void updateMenu(boolean notify) {
+        removeAllViews();
+        mMenuItems.clear();
+        if (mMenuProvider != null) {
+            mMenuItems.addAll(mMenuProvider.getMenuItems(getContext()));
+        }
+        mMenuItems.addAll(getDefaultNotificationMenuItems());
+        for (int i = 0; i < mMenuItems.size(); i++) {
+            final View v = createMenuView(mMenuItems.get(i));
+            mMenuItems.get(i).menuView = v;
+        }
+        resetState(notify);
+    }
+
+    private View createMenuView(MenuItem item) {
+        AlphaOptimizedImageView iv = new AlphaOptimizedImageView(getContext());
+        addView(iv);
+        iv.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
+        iv.setImageDrawable(item.icon);
+        iv.setOnClickListener(this);
+        iv.setColorFilter(mIconTint);
+        iv.setAlpha(mAlpha);
+        FrameLayout.LayoutParams lp = (LayoutParams) iv.getLayoutParams();
+        lp.width = (int) mHorizSpaceForIcon;
+        lp.height = (int) mHorizSpaceForIcon;
+        return iv;
+    }
+
+    public void resetState(boolean notify) {
+        setMenuAlpha(0f);
+        mIconsPlaced = false;
+        mMenuFadedIn = false;
+        mAnimating = false;
+        mSnapping = false;
+        mDismissing = false;
+        setMenuLocation(mOnLeft ? 1 : -1 /* on left */);
+        if (mListener != null && notify) {
+            mListener.onMenuReset(mParent);
+        }
+    }
+
+    public void setMenuClickListener(OnMenuClickListener listener) {
+        mListener = listener;
+    }
+
+    public void setNotificationRowParent(ExpandableNotificationRow parent) {
+        mParent = parent;
+        setMenuLocation(mOnLeft ? 1 : -1);
+    }
+
+    public void setAppName(String appName) {
+        Resources res = getResources();
+        final int count = mMenuItems.size();
+        for (int i = 0; i < count; i++) {
+            MenuItem item = mMenuItems.get(i);
+            String description = String.format(
+                    res.getString(R.string.notification_menu_accessibility),
+                    appName, item.menuDescription);
+            item.menuView.setContentDescription(description);
+        }
+    }
+
+    public ExpandableNotificationRow getNotificationParent() {
+        return mParent;
+    }
+
+    public void setMenuAlpha(float alpha) {
+        mAlpha = alpha;
+        if (alpha == 0) {
+            mMenuFadedIn = false; // Can fade in again once it's gone.
+            setVisibility(View.INVISIBLE);
+        } else {
+            setVisibility(View.VISIBLE);
+        }
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            getChildAt(i).setAlpha(mAlpha);
+        }
+    }
+
+    /**
+     * Returns whether the menu is displayed on the left side of the view or not.
+     */
+    public boolean isMenuOnLeft() {
+        return mOnLeft;
+    }
+
+    /**
+     * Returns the horizontal space in pixels required to display the menu.
+     */
+    public float getSpaceForMenu() {
+        return mHorizSpaceForIcon * getChildCount();
+    }
+
+    /**
+     * Indicates whether the menu is visible at 1 alpha. Does not indicate if entire view is
+     * visible.
+     */
+    public boolean isVisible() {
+        return mAlpha > 0;
+    }
+
+    public void cancelFadeAnimator() {
+        if (mFadeAnimator != null) {
+            mFadeAnimator.cancel();
+        }
+    }
+
+    public void updateMenuAlpha(final float transX, final float size) {
+        if (mAnimating || !mMenuFadedIn) {
+            // Don't adjust when animating, or if the menu hasn't been shown yet.
+            return;
+        }
+
+        final float fadeThreshold = size * 0.3f;
+        final float absTrans = Math.abs(transX);
+        float desiredAlpha = 0;
+
+        if (absTrans == 0) {
+            desiredAlpha = 0;
+        } else if (absTrans <= fadeThreshold) {
+            desiredAlpha = 1;
+        } else {
+            desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold));
+        }
+        setMenuAlpha(desiredAlpha);
+    }
+
+    public void fadeInMenu(final boolean fromLeft, final float transX,
+            final float notiThreshold) {
+        if (mDismissing || mAnimating) {
+            return;
+        }
+        if (isMenuLocationChange(transX)) {
+            setMenuAlpha(0f);
+        }
+        setMenuLocation((int) transX);
+        mFadeAnimator = ValueAnimator.ofFloat(mAlpha, 1);
+        mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                final float absTrans = Math.abs(transX);
+
+                boolean pastGear = (fromLeft && transX <= notiThreshold)
+                        || (!fromLeft && absTrans <= notiThreshold);
+                if (pastGear && !mMenuFadedIn) {
+                    setMenuAlpha((float) animation.getAnimatedValue());
+                }
+            }
+        });
+        mFadeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mAnimating = true;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                // TODO should animate back to 0f from current alpha
+                setMenuAlpha(0f);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimating = false;
+                mMenuFadedIn = mAlpha == 1;
+            }
+        });
+        mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
+        mFadeAnimator.setDuration(ICON_ALPHA_ANIM_DURATION);
+        mFadeAnimator.start();
+    }
+
+    public void updateVerticalLocation() {
+        if (mParent == null || mMenuItems.size() == 0) {
+            return;
+        }
+        int parentHeight = mParent.getCollapsedHeight();
+        float translationY;
+        if (parentHeight < mVertSpaceForIcons) {
+            translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2);
+        } else {
+            translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2;
+        }
+        setTranslationY(translationY);
+    }
+
+    @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        mIconsPlaced = false;
+        setMenuLocation(mOnLeft ? 1 : -1);
+    }
+
+    public void setMenuLocation(int translation) {
+        boolean onLeft = translation > 0;
+        if ((mIconsPlaced && onLeft == mOnLeft) || mSnapping || mParent == null) {
+            // Do nothing
+            return;
+        }
+        final boolean isRtl = mParent.isLayoutRtl();
+        final int count = getChildCount();
+        final int width = getWidth();
+        for (int i = 0; i < count; i++) {
+            final View v = getChildAt(i);
+            final float left = isRtl
+                    ? -(width - mHorizSpaceForIcon * (i + 1))
+                    : i * mHorizSpaceForIcon;
+            final float right = isRtl
+                    ? -i * mHorizSpaceForIcon
+                    : width - (mHorizSpaceForIcon * (i + 1));
+            v.setTranslationX(onLeft ? left : right);
+        }
+        mOnLeft = onLeft;
+        mIconsPlaced = true;
+    }
+
+    public boolean isMenuLocationChange(float translation) {
+        boolean onLeft = translation > mIconPadding;
+        boolean onRight = translation < -mIconPadding;
+        if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
+            return true;
+        }
+        return false;
+    }
+
+    public void setDismissing() {
+        mDismissing = true;
+    }
+
+    public void setSnapping(boolean snapping) {
+        mSnapping = snapping;
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mListener == null) {
+            // Nothing to do
+            return;
+        }
+        v.getLocationOnScreen(mIconLocation);
+        mParent.getLocationOnScreen(mParentLocation);
+        final int centerX = (int) (mHorizSpaceForIcon / 2);
+        final int centerY = (int) (v.getTranslationY() * 2 + v.getHeight()) / 2;
+        final int x = mIconLocation[0] - mParentLocation[0] + centerX;
+        final int y = mIconLocation[1] - mParentLocation[1] + centerY;
+        final int index = indexOfChild(v);
+        mListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
+    }
+
+    @Override
+    public void onPluginConnected(NotificationMenuRowProvider plugin, Context pluginContext) {
+        mMenuProvider = plugin;
+        updateMenu(false /* notify */);
+    }
+
+    @Override
+    public void onPluginDisconnected(NotificationMenuRowProvider plugin) {
+        mMenuProvider = null;
+        updateMenu(false /* notify */);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
deleted file mode 100644
index 4315647..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-
-public class NotificationSettingsIconRow extends FrameLayout implements View.OnClickListener {
-
-    private static final int GEAR_ALPHA_ANIM_DURATION = 200;
-
-    public interface SettingsIconRowListener {
-        /**
-         * Called when the gear behind a notification is touched.
-         */
-        public void onGearTouched(ExpandableNotificationRow row, int x, int y);
-
-        /**
-         * Called when a notification is slid back over the gear.
-         */
-        public void onSettingsIconRowReset(ExpandableNotificationRow row);
-    }
-
-    private ExpandableNotificationRow mParent;
-    private AlphaOptimizedImageView mGearIcon;
-    private float mHorizSpaceForGear;
-    private SettingsIconRowListener mListener;
-
-    private ValueAnimator mFadeAnimator;
-    private boolean mSettingsFadedIn = false;
-    private boolean mAnimating = false;
-    private boolean mOnLeft = true;
-    private boolean mDismissing = false;
-    private boolean mSnapping = false;
-    private boolean mIconPlaced = false;
-
-    private int[] mGearLocation = new int[2];
-    private int[] mParentLocation = new int[2];
-    private int mVertSpaceForGear;
-
-    public NotificationSettingsIconRow(Context context) {
-        this(context, null);
-    }
-
-    public NotificationSettingsIconRow(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public NotificationSettingsIconRow(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public NotificationSettingsIconRow(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mGearIcon = (AlphaOptimizedImageView) findViewById(R.id.gear_icon);
-        mGearIcon.setOnClickListener(this);
-        setOnClickListener(this);
-        mHorizSpaceForGear =
-                getResources().getDimensionPixelOffset(R.dimen.notification_gear_width);
-        mVertSpaceForGear = getResources().getDimensionPixelOffset(R.dimen.notification_min_height);
-        resetState();
-    }
-
-    public void resetState() {
-        setGearAlpha(0f);
-        mIconPlaced = false;
-        mSettingsFadedIn = false;
-        mAnimating = false;
-        mSnapping = false;
-        mDismissing = false;
-        setIconLocation(true /* on left */);
-        if (mListener != null) {
-            mListener.onSettingsIconRowReset(mParent);
-        }
-    }
-
-    public void setGearListener(SettingsIconRowListener listener) {
-        mListener = listener;
-    }
-
-    public void setNotificationRowParent(ExpandableNotificationRow parent) {
-        mParent = parent;
-        setIconLocation(mOnLeft);
-    }
-
-    public void setAppName(String appName) {
-        Resources res = getResources();
-        String description = String.format(res.getString(R.string.notification_gear_accessibility),
-                appName);
-        mGearIcon.setContentDescription(description);
-    }
-
-    public ExpandableNotificationRow getNotificationParent() {
-        return mParent;
-    }
-
-    public void setGearAlpha(float alpha) {
-        if (alpha == 0) {
-            mSettingsFadedIn = false; // Can fade in again once it's gone.
-            setVisibility(View.INVISIBLE);
-        } else {
-            setVisibility(View.VISIBLE);
-        }
-        mGearIcon.setAlpha(alpha);
-    }
-
-    /**
-     * Returns whether the icon is on the left side of the view or not.
-     */
-    public boolean isIconOnLeft() {
-        return mOnLeft;
-    }
-
-    /**
-     * Returns the horizontal space in pixels required to display the gear behind a notification.
-     */
-    public float getSpaceForGear() {
-        return mHorizSpaceForGear;
-    }
-
-    /**
-     * Indicates whether the gear is visible at 1 alpha. Does not indicate
-     * if entire view is visible.
-     */
-    public boolean isVisible() {
-        return mGearIcon.getAlpha() > 0;
-    }
-
-    public void cancelFadeAnimator() {
-        if (mFadeAnimator != null) {
-            mFadeAnimator.cancel();
-        }
-    }
-
-    public void updateSettingsIcons(final float transX, final float size) {
-        if (mAnimating || !mSettingsFadedIn) {
-            // Don't adjust when animating, or if the gear hasn't been shown yet.
-            return;
-        }
-
-        final float fadeThreshold = size * 0.3f;
-        final float absTrans = Math.abs(transX);
-        float desiredAlpha = 0;
-
-        if (absTrans == 0) {
-            desiredAlpha = 0;
-        } else if (absTrans <= fadeThreshold) {
-            desiredAlpha = 1;
-        } else {
-            desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold));
-        }
-        setGearAlpha(desiredAlpha);
-    }
-
-    public void fadeInSettings(final boolean fromLeft, final float transX,
-            final float notiThreshold) {
-        if (mDismissing || mAnimating) {
-            return;
-        }
-        if (isIconLocationChange(transX)) {
-            setGearAlpha(0f);
-        }
-        setIconLocation(transX > 0 /* fromLeft */);
-        mFadeAnimator = ValueAnimator.ofFloat(mGearIcon.getAlpha(), 1);
-        mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                final float absTrans = Math.abs(transX);
-
-                boolean pastGear = (fromLeft && transX <= notiThreshold)
-                        || (!fromLeft && absTrans <= notiThreshold);
-                if (pastGear && !mSettingsFadedIn) {
-                    setGearAlpha((float) animation.getAnimatedValue());
-                }
-            }
-        });
-        mFadeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mAnimating = true;
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                // TODO should animate back to 0f from current alpha
-                mGearIcon.setAlpha(0f);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimating = false;
-                mSettingsFadedIn = mGearIcon.getAlpha() == 1;
-            }
-        });
-        mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
-        mFadeAnimator.setDuration(GEAR_ALPHA_ANIM_DURATION);
-        mFadeAnimator.start();
-    }
-
-    public void updateVerticalLocation() {
-        if (mParent == null) {
-            return;
-        }
-        int parentHeight = mParent.getCollapsedHeight();
-        if (parentHeight < mVertSpaceForGear) {
-            mGearIcon.setTranslationY((parentHeight / 2) - (mGearIcon.getHeight() / 2));
-        } else {
-            mGearIcon.setTranslationY((mVertSpaceForGear - mGearIcon.getHeight()) / 2);
-        }
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        setIconLocation(mOnLeft);
-    }
-
-    public void setIconLocation(boolean onLeft) {
-        if ((mIconPlaced && onLeft == mOnLeft) || mSnapping || mParent == null
-                || mGearIcon.getWidth() == 0) {
-            // Do nothing
-            return;
-        }
-        final boolean isRtl = mParent.isLayoutRtl();
-        final float left = isRtl
-                ? -(mParent.getWidth() - mHorizSpaceForGear)
-                : 0;
-        final float right = isRtl
-                ? 0
-                : mParent.getWidth() - mHorizSpaceForGear;
-        final float centerX = ((mHorizSpaceForGear - mGearIcon.getWidth()) / 2);
-        setTranslationX(onLeft ? left + centerX : right + centerX);
-        mOnLeft = onLeft;
-        mIconPlaced = true;
-    }
-
-    public boolean isIconLocationChange(float translation) {
-        boolean onLeft = translation > mGearIcon.getPaddingStart();
-        boolean onRight = translation < -mGearIcon.getPaddingStart();
-        if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
-            return true;
-        }
-        return false;
-    }
-
-    public void setDismissing() {
-        mDismissing = true;
-    }
-
-    public void setSnapping(boolean snapping) {
-        mSnapping = snapping;
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v.getId() == R.id.gear_icon) {
-            if (mListener != null) {
-                mGearIcon.getLocationOnScreen(mGearLocation);
-                mParent.getLocationOnScreen(mParentLocation);
-
-                final int centerX = (int) (mHorizSpaceForGear / 2);
-                final int centerY =
-                        (int) (mGearIcon.getTranslationY() * 2 + mGearIcon.getHeight())/ 2;
-                final int x = mGearLocation[0] - mParentLocation[0] + centerX;
-                final int y = mGearLocation[1] - mParentLocation[1] + centerY;
-                mListener.onGearTouched(mParent, x, y);
-            }
-        } else {
-            // Do nothing when the background is touched.
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index bc1b9fb..e8e9d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -67,6 +67,7 @@
     private int mStatusBarState;
     private float mMaxShelfEnd;
     private int mRelativeOffset;
+    private boolean mInteractive;
 
     public NotificationShelf(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -128,6 +129,7 @@
         } else {
             mViewInvertHelper.update(dark);
         }
+        mShelfIcons.setAmbient(dark);
     }
 
     @Override
@@ -555,13 +557,18 @@
     }
 
     private void updateInteractiveness() {
-        boolean interactive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf;
-        setClickable(interactive);
-        setFocusable(interactive);
-        setImportantForAccessibility(interactive ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
+        mInteractive = mStatusBarState == StatusBarState.KEYGUARD && mHasItemsInStableShelf;
+        setClickable(mInteractive);
+        setFocusable(mInteractive);
+        setImportantForAccessibility(mInteractive ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
                 : View.IMPORTANT_FOR_ACCESSIBILITY_NO);
     }
 
+    @Override
+    protected boolean isInteractive() {
+        return mInteractive;
+    }
+
     public void setMaxShelfEnd(float maxShelfEnd) {
         mMaxShelfEnd = maxShelfEnd;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
new file mode 100644
index 0000000..670d73e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -0,0 +1,176 @@
+package com.android.systemui.statusbar;
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
+
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsInteractionListener;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.service.notification.StatusBarNotification;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioGroup;
+import android.widget.RadioGroup.OnCheckedChangeListener;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.android.systemui.R;
+
+public class NotificationSnooze extends LinearLayout
+        implements NotificationMenuRowProvider.SnoozeGutsContent, View.OnClickListener {
+
+    private GutsInteractionListener mGutsInteractionListener;
+    private SnoozeListener mSnoozeListener;
+    private StatusBarNotification mSbn;
+
+    private TextView mSelectedOption;
+    private TextView mUndo;
+    private ViewGroup mSnoozeOptionView;
+
+    private long mTimeToSnooze;
+
+    // Default is the first option in this list
+    private static final int[] SNOOZE_OPTIONS = {
+            R.string.snooze_option_15_min, R.string.snooze_option_30_min,
+            R.string.snooze_option_1_hour
+    };
+
+    public NotificationSnooze(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        // Create the different options based on list
+        createOptionViews();
+
+        // Snackbar
+        mSelectedOption = (TextView) findViewById(R.id.snooze_option_default);
+        mSelectedOption.setOnClickListener(this);
+        mUndo = (TextView) findViewById(R.id.undo);
+        mUndo.setOnClickListener(this);
+
+        // Default to first option in list
+        setTimeToSnooze(SNOOZE_OPTIONS[0]);
+    }
+
+    private void createOptionViews() {
+        mSnoozeOptionView = (ViewGroup) findViewById(R.id.snooze_options);
+        mSnoozeOptionView.setVisibility(View.GONE);
+        final Resources res = getResources();
+        final int textSize = res.getDimensionPixelSize(R.dimen.snooze_option_text_size);
+        final int p = res.getDimensionPixelSize(R.dimen.snooze_option_padding);
+        for (int i = 0; i < SNOOZE_OPTIONS.length; i++) {
+            TextView tv = new TextView(getContext());
+            tv.setTextColor(Color.WHITE);
+            tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+            tv.setPadding(p, p, p, p);
+            mSnoozeOptionView.addView(tv);
+            tv.setText(SNOOZE_OPTIONS[i]);
+            tv.setTag(SNOOZE_OPTIONS[i]);
+            tv.setOnClickListener(this);
+        }
+    }
+
+    private void showSnoozeOptions(boolean show) {
+        mSelectedOption.setVisibility(show ? View.GONE : View.VISIBLE);
+        mUndo.setVisibility(show ? View.GONE : View.VISIBLE);
+        mSnoozeOptionView.setVisibility(show ? View.VISIBLE : View.GONE);
+    }
+
+    private void setTimeToSnooze(int optionId) {
+        long snoozeUntilMillis = Calendar.getInstance().getTimeInMillis();
+        switch (optionId) {
+            case R.string.snooze_option_15_min:
+                snoozeUntilMillis += TimeUnit.MINUTES.toMillis(15);
+                break;
+            case R.string.snooze_option_30_min:
+                snoozeUntilMillis += TimeUnit.MINUTES.toMillis(30);
+                break;
+            case R.string.snooze_option_1_hour:
+                snoozeUntilMillis += TimeUnit.MINUTES.toMillis(60);
+                break;
+        }
+        mTimeToSnooze = snoozeUntilMillis;
+        final Resources res = getResources();
+        String selectedString = String.format(
+                res.getString(R.string.snoozed_for_time), res.getString(optionId));
+        mSelectedOption.setText(selectedString);
+        showSnoozeOptions(false);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mGutsInteractionListener != null) {
+            mGutsInteractionListener.onInteraction(this);
+        }
+        final int id = v.getId();
+        final Integer tag = (Integer) v.getTag();
+        if (tag != null) {
+            // From the option list
+            setTimeToSnooze(tag);
+        } else if (id == R.id.snooze_option_default) {
+            // Show more snooze options
+            showSnoozeOptions(true);
+        } else if (id == R.id.undo) {
+            mTimeToSnooze = -1;
+            mGutsInteractionListener.closeGuts(this);
+        }
+    }
+
+    @Override
+    public View getContentView() {
+        return this;
+    }
+
+    @Override
+    public void setStatusBarNotification(StatusBarNotification sbn) {
+        mSbn = sbn;
+    }
+
+    @Override
+    public void setInteractionListener(GutsInteractionListener listener) {
+        mGutsInteractionListener = listener;
+    }
+
+    @Override
+    public void setSnoozeListener(SnoozeListener listener) {
+        mSnoozeListener = listener;
+    }
+
+    @Override
+    public boolean handleCloseControls() {
+        // When snooze is closed (i.e. there was interaction outside of the notification)
+        // then we commit the snooze action.
+        if (mSnoozeListener != null && mTimeToSnooze != -1) {
+            mSnoozeListener.snoozeNotification(mSbn, mTimeToSnooze);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 68d5cd4..9a76ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -16,15 +16,18 @@
 
 package com.android.systemui.statusbar;
 
+import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.telephony.SubscriptionInfo;
 import android.util.ArraySet;
 import android.util.AttributeSet;
@@ -37,8 +40,10 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
@@ -62,8 +67,8 @@
     private static final String SLOT_WIFI = "wifi";
     private static final String SLOT_ETHERNET = "ethernet";
 
-    NetworkControllerImpl mNC;
-    SecurityController mSC;
+    private final NetworkController mNetworkController;
+    private final SecurityController mSecurityController;
 
     private boolean mNoSimsVisible = false;
     private boolean mVpnVisible = false;
@@ -72,8 +77,10 @@
     private boolean mEthernetVisible = false;
     private int mEthernetIconId = 0;
     private int mLastEthernetIconId = -1;
+    private int mWifiBadgeId = -1;
     private boolean mWifiVisible = false;
     private int mWifiStrengthId = 0;
+    private int mLastWifiBadgeId = -1;
     private int mLastWifiStrengthId = -1;
     private boolean mIsAirplaneMode = false;
     private int mAirplaneIconId = 0;
@@ -131,6 +138,8 @@
         TypedValue typedValue = new TypedValue();
         res.getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
         mIconScaleFactor = typedValue.getFloat();
+        mNetworkController = Dependency.get(NetworkController.class);
+        mSecurityController = Dependency.get(SecurityController.class);
     }
 
     @Override
@@ -151,24 +160,11 @@
             mBlockEthernet = blockEthernet;
             mBlockWifi = blockWifi;
             // Re-register to get new callbacks.
-            mNC.removeCallback(this);
-            mNC.addCallback(this);
+            mNetworkController.removeCallback(this);
+            mNetworkController.addCallback(this);
         }
     }
 
-    public void setNetworkController(NetworkControllerImpl nc) {
-        if (DEBUG) Log.d(TAG, "NetworkController=" + nc);
-        mNC = nc;
-    }
-
-    public void setSecurityController(SecurityController sc) {
-        if (DEBUG) Log.d(TAG, "SecurityController=" + sc);
-        mSC = sc;
-        mSC.addCallback(this);
-        mVpnVisible = mSC.isVpnEnabled();
-        mVpnIconId = currentVpnIconId(mSC.isVpnBranded());
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -212,9 +208,13 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
+        mVpnVisible = mSecurityController.isVpnEnabled();
+        mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
 
         for (PhoneState state : mPhoneStates) {
-            mMobileSignalGroup.addView(state.mMobileGroup);
+            if (state.mMobileGroup.getParent() == null) {
+                mMobileSignalGroup.addView(state.mMobileGroup);
+            }
         }
 
         int endPadding = mMobileSignalGroup.getChildCount() > 0 ? mMobileSignalGroupEndPadding : 0;
@@ -224,15 +224,16 @@
 
         apply();
         applyIconTint();
-        mNC.addCallback(this);
+        mNetworkController.addCallback(this);
+        mSecurityController.addCallback(this);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         mMobileSignalGroup.removeAllViews();
         TunerService.get(mContext).removeTunable(this);
-        mSC.removeCallback(this);
-        mNC.removeCallback(this);
+        mSecurityController.removeCallback(this);
+        mNetworkController.removeCallback(this);
 
         super.onDetachedFromWindow();
     }
@@ -251,8 +252,8 @@
         post(new Runnable() {
             @Override
             public void run() {
-                mVpnVisible = mSC.isVpnEnabled();
-                mVpnIconId = currentVpnIconId(mSC.isVpnBranded());
+                mVpnVisible = mSecurityController.isVpnEnabled();
+                mVpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
                 apply();
             }
         });
@@ -263,6 +264,7 @@
             boolean activityIn, boolean activityOut, String description) {
         mWifiVisible = statusIcon.visible && !mBlockWifi;
         mWifiStrengthId = statusIcon.icon;
+        mWifiBadgeId = statusIcon.iconOverlay;
         mWifiDescription = statusIcon.contentDescription;
 
         apply();
@@ -271,7 +273,7 @@
     @Override
     public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
             int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
-            String description, boolean isWide, int subId) {
+            String description, boolean isWide, int subId, boolean roaming) {
         PhoneState state = getState(subId);
         if (state == null) {
             return;
@@ -282,6 +284,7 @@
         state.mMobileDescription = statusIcon.contentDescription;
         state.mMobileTypeDescription = typeContentDescription;
         state.mIsMobileTypeIconWide = statusType != 0 && isWide;
+        state.mRoaming = roaming;
 
         apply();
     }
@@ -403,6 +406,7 @@
             mWifi.setImageDrawable(null);
             mWifiDark.setImageDrawable(null);
             mLastWifiStrengthId = -1;
+            mLastWifiBadgeId = -1;
         }
 
         for (PhoneState state : mPhoneStates) {
@@ -468,10 +472,16 @@
                     (mEthernetVisible ? "VISIBLE" : "GONE")));
 
         if (mWifiVisible) {
-            if (mWifiStrengthId != mLastWifiStrengthId) {
-                setIconForView(mWifi, mWifiStrengthId);
-                setIconForView(mWifiDark, mWifiStrengthId);
+            if (mWifiStrengthId != mLastWifiStrengthId || mWifiBadgeId != mLastWifiBadgeId) {
+                if (mWifiBadgeId == -1) {
+                    setIconForView(mWifi, mWifiStrengthId);
+                    setIconForView(mWifiDark, mWifiStrengthId);
+                } else {
+                    setBadgedWifiIconForView(mWifi, mWifiStrengthId, mWifiBadgeId);
+                    setBadgedWifiIconForView(mWifiDark, mWifiStrengthId, mWifiBadgeId);
+                }
                 mLastWifiStrengthId = mWifiStrengthId;
+                mLastWifiBadgeId = mWifiBadgeId;
             }
             mWifiGroup.setContentDescription(mWifiDescription);
             mWifiGroup.setVisibility(View.VISIBLE);
@@ -533,6 +543,10 @@
         // Using the imageView's context to retrieve the Drawable so that theme is preserved.
         Drawable icon = imageView.getContext().getDrawable(iconId);
 
+        setScaledIcon(imageView, icon);
+    }
+
+    private void setScaledIcon(ImageView imageView, Drawable icon) {
         if (mIconScaleFactor == 1.f) {
             imageView.setImageDrawable(icon);
         } else {
@@ -540,6 +554,33 @@
         }
     }
 
+    /**
+     * Creates and sets a LayerDrawable from the given ids on the given view.
+     *
+     * <p>This method will also scale the icon by {@link #mIconScaleFactor} if appropriate.
+     */
+    private void setBadgedWifiIconForView(ImageView imageView, @DrawableRes int wifiPieId,
+            @DrawableRes int badgeId) {
+        // Using the imageView's context to retrieve the Drawable so that theme is preserved.;
+        LayerDrawable icon = new LayerDrawable(new Drawable[] {
+                imageView.getContext().getDrawable(wifiPieId),
+                imageView.getContext().getDrawable(badgeId)});
+
+        // The LayerDrawable shares an underlying state so we must mutate the object to change the
+        // color between the light and dark themes.
+        icon.mutate().setTint(getColorAttr(imageView.getContext(), R.attr.singleToneColor));
+
+        setScaledIcon(imageView, icon);
+    }
+
+    /** Returns the given color attribute value, or white if not defined. */
+    @ColorInt private static int getColorAttr(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[] {attr});
+        @ColorInt int colorAccent = ta.getColor(0, Color.WHITE);
+        ta.recycle();
+        return colorAccent;
+    }
+
     public void setIconTint(int tint, float darkIntensity, Rect tintArea) {
         boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity
                 || !mTintArea.equals(tintArea);
@@ -591,7 +632,8 @@
         private String mMobileDescription, mMobileTypeDescription;
 
         private ViewGroup mMobileGroup;
-        private ImageView mMobile, mMobileDark, mMobileType;
+        private ImageView mMobile, mMobileDark, mMobileType, mMobileRoaming;
+        public boolean mRoaming;
 
         public PhoneState(int subId, Context context) {
             ViewGroup root = (ViewGroup) LayoutInflater.from(context)
@@ -605,6 +647,7 @@
             mMobile         = (ImageView) root.findViewById(R.id.mobile_signal);
             mMobileDark     = (ImageView) root.findViewById(R.id.mobile_signal_dark);
             mMobileType     = (ImageView) root.findViewById(R.id.mobile_type);
+            mMobileRoaming  = (ImageView) root.findViewById(R.id.mobile_roaming);
         }
 
         public boolean apply(boolean isSecondaryIcon) {
@@ -640,6 +683,7 @@
                         (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
 
             mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
+            mMobileRoaming.setVisibility(mRoaming ? View.VISIBLE : View.GONE);
 
             return mMobileVisible;
         }
@@ -699,6 +743,8 @@
                     StatusBarIconController.getDarkIntensity(tintArea, mMobile, darkIntensity),
                     mMobile, mMobileDark);
             setTint(mMobileType, StatusBarIconController.getTint(tintArea, mMobileType, tint));
+            setTint(mMobileRoaming, StatusBarIconController.getTint(tintArea, mMobileRoaming,
+                    tint));
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index a2c2fd7..399b0d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -221,6 +221,8 @@
         setContentDescription(icon.contentDescription);
         if (!iconEquals) {
             if (!updateDrawable(false /* no clear */)) return false;
+            // we have to clear the grayscale tag since it may have changed
+            setTag(R.id.icon_is_grayscale, null);
         }
         if (!levelEquals) {
             setImageLevel(icon.iconLevel);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
index dd7c4c7..063252f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TransformableView.java
@@ -22,7 +22,7 @@
  * A view that can be transformed to and from.
  */
 public interface TransformableView {
-    int TRANSFORMING_VIEW_HEADER = 0;
+    int TRANSFORMING_VIEW_ICON = 0;
     int TRANSFORMING_VIEW_TITLE = 1;
     int TRANSFORMING_VIEW_TEXT = 2;
     int TRANSFORMING_VIEW_IMAGE = 3;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index cd6c31f..1c89e32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -23,6 +23,7 @@
 import android.util.ArraySet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.Interpolator;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -298,5 +299,14 @@
                 TransformState otherState) {
             return false;
         }
+
+        /**
+         * Get a custom interpolator for this animation
+         * @param interpolationType the type of the interpolation, i.e TranslationX / TranslationY
+         * @param isFrom true if this transformation from the other view
+         */
+        public Interpolator getCustomInterpolator(int interpolationType, boolean isFrom) {
+            return null;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index 1c8c317..3bbda4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -30,7 +30,7 @@
 import android.widget.LinearLayout;
 
 import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QS.ActivityStarter;
+import com.android.systemui.ActivityStarter;
 
 import java.net.URISyntaxException;
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 7adb36d..f24e40b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -24,8 +24,6 @@
 import android.content.IntentFilter;
 import android.graphics.PixelFormat;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
@@ -35,15 +33,16 @@
 import android.view.WindowManager;
 import android.widget.LinearLayout;
 import com.android.systemui.BatteryMeterView;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
-import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
 
 /**
  * A status bar (and navigation bar) tailored for the automotive use case.
@@ -73,6 +72,7 @@
         SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
         registerPackageChangeReceivers();
 
+        createBatteryController();
         mCarBatteryController.startListening();
         mConnectedDeviceSignalController.startListening();
     }
@@ -105,7 +105,7 @@
                         R.dimen.status_bar_connected_device_signal_margin_end));
 
         mConnectedDeviceSignalController = new ConnectedDeviceSignalController(mContext,
-                mSignalsView, mBluetoothController);
+                mSignalsView);
 
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "makeStatusBarView(). mBatteryMeterView: " + mBatteryMeterView);
@@ -114,8 +114,7 @@
         return statusBarView;
     }
 
-    @Override
-    protected BatteryController createBatteryController() {
+    private BatteryController createBatteryController() {
         mCarBatteryController = new CarBatteryController(mContext);
         mCarBatteryController.addBatteryViewHandler(this);
         return mCarBatteryController;
@@ -211,8 +210,11 @@
 
     @Override
     protected void createUserSwitcher() {
-        if (mUserSwitcherController.useFullscreenUserSwitcher()) {
-            mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, mUserSwitcherController,
+        UserSwitcherController userSwitcherController =
+                Dependency.get(UserSwitcherController.class);
+        if (userSwitcherController.useFullscreenUserSwitcher()) {
+            mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
+                    userSwitcherController,
                     (ViewStub) mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub));
         } else {
             super.createUserSwitcher();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
index a3e1b3a..c308930 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
@@ -15,6 +15,7 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.ImageView;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ScalingDrawableWrapper;
 import com.android.systemui.statusbar.policy.BluetoothController;
@@ -67,10 +68,9 @@
 
     private BluetoothHeadsetClient mBluetoothHeadsetClient;
 
-    public ConnectedDeviceSignalController(Context context, View signalsView,
-            BluetoothController controller) {
+    public ConnectedDeviceSignalController(Context context, View signalsView) {
         mContext = context;
-        mController = controller;
+        mController = Dependency.get(BluetoothController.class);
 
         mSignalsView = signalsView;
         mNetworkSignalView = (ImageView)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/CustomInterpolatorTransformation.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/CustomInterpolatorTransformation.java
new file mode 100644
index 0000000..de4c312
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/CustomInterpolatorTransformation.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.ViewTransformationHelper;
+
+import static com.android.systemui.statusbar.TransformableView.TRANSFORMING_VIEW_TITLE;
+import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
+
+/**
+ * A custom transformation that modifies the interpolator
+ */
+public abstract class CustomInterpolatorTransformation
+        extends ViewTransformationHelper.CustomTransformation {
+
+    private final int mViewType;
+
+    public CustomInterpolatorTransformation(int viewType) {
+        mViewType = viewType;
+    }
+
+    @Override
+    public boolean transformTo(TransformState ownState, TransformableView notification,
+            float transformationAmount) {
+        if (!hasCustomTransformation()) {
+            return false;
+        }
+        TransformState otherState = notification.getCurrentState(mViewType);
+        if (otherState == null) {
+            return false;
+        }
+        View view = ownState.getTransformedView();
+        CrossFadeHelper.fadeOut(view, transformationAmount);
+        ownState.transformViewFullyTo(otherState, this, transformationAmount);
+        otherState.recycle();
+        return true;
+    }
+
+    protected boolean hasCustomTransformation() {
+        return true;
+    }
+
+    @Override
+    public boolean transformFrom(TransformState ownState,
+            TransformableView notification, float transformationAmount) {
+        if (!hasCustomTransformation()) {
+            return false;
+        }
+        TransformState otherState = notification.getCurrentState(mViewType);
+        if (otherState == null) {
+            return false;
+        }
+        View view = ownState.getTransformedView();
+        CrossFadeHelper.fadeIn(view, transformationAmount);
+        ownState.transformViewFullyFrom(otherState, this, transformationAmount);
+        otherState.recycle();
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
deleted file mode 100644
index 9501f90..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.util.Pools;
-import android.view.NotificationHeaderView;
-import android.view.View;
-
-import com.android.systemui.statusbar.CrossFadeHelper;
-
-/**
- * A transform state of a text view.
-*/
-public class HeaderTransformState extends TransformState {
-
-    private static Pools.SimplePool<HeaderTransformState> sInstancePool
-            = new Pools.SimplePool<>(40);
-    private View mExpandButton;
-    private View mWorkProfileIcon;
-    private TransformState mWorkProfileState;
-
-    @Override
-    public void initFrom(View view) {
-        super.initFrom(view);
-        if (view instanceof NotificationHeaderView) {
-            NotificationHeaderView header = (NotificationHeaderView) view;
-            mExpandButton = header.getExpandButton();
-            mWorkProfileState = TransformState.obtain();
-            mWorkProfileIcon = header.getWorkProfileIcon();
-            mWorkProfileState.initFrom(mWorkProfileIcon);
-        }
-    }
-
-    @Override
-    public boolean transformViewTo(TransformState otherState, float transformationAmount) {
-        // if the transforming notification has a header, we have ensured that it looks the same
-        // but the expand button, so lets fade just that one and transform the work profile icon.
-        if (!(mTransformedView instanceof NotificationHeaderView)) {
-            return false;
-        }
-        NotificationHeaderView header = (NotificationHeaderView) mTransformedView;
-        int childCount = header.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View headerChild = header.getChildAt(i);
-            if (headerChild.getVisibility() == View.GONE) {
-                continue;
-            }
-            if (headerChild != mExpandButton) {
-                headerChild.setVisibility(View.INVISIBLE);
-            } else {
-                CrossFadeHelper.fadeOut(mExpandButton, transformationAmount);
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public void transformViewFrom(TransformState otherState, float transformationAmount) {
-        // if the transforming notification has a header, we have ensured that it looks the same
-        // but the expand button, so lets fade just that one and transform the work profile icon.
-        if (!(mTransformedView instanceof NotificationHeaderView)) {
-            return;
-        }
-        NotificationHeaderView header = (NotificationHeaderView) mTransformedView;
-        header.setVisibility(View.VISIBLE);
-        header.setAlpha(1.0f);
-        int childCount = header.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View headerChild = header.getChildAt(i);
-            if (headerChild.getVisibility() == View.GONE) {
-                continue;
-            }
-            if (headerChild == mExpandButton) {
-                CrossFadeHelper.fadeIn(mExpandButton, transformationAmount);
-            } else {
-                headerChild.setVisibility(View.VISIBLE);
-                if (headerChild == mWorkProfileIcon) {
-                    mWorkProfileState.transformViewFullyFrom(
-                            ((HeaderTransformState) otherState).mWorkProfileState,
-                            transformationAmount);
-                }
-            }
-        }
-        return;
-    }
-
-    public static HeaderTransformState obtain() {
-        HeaderTransformState instance = sInstancePool.acquire();
-        if (instance != null) {
-            return instance;
-        }
-        return new HeaderTransformState();
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        sInstancePool.release(this);
-    }
-
-    @Override
-    protected void reset() {
-        super.reset();
-        mExpandButton = null;
-        mWorkProfileState = null;
-        if (mWorkProfileState != null) {
-            mWorkProfileState.recycle();
-            mWorkProfileState = null;
-        }
-    }
-
-    @Override
-    public void setVisible(boolean visible, boolean force) {
-        super.setVisible(visible, force);
-        if (!(mTransformedView instanceof NotificationHeaderView)) {
-            return;
-        }
-        NotificationHeaderView header = (NotificationHeaderView) mTransformedView;
-        int childCount = header.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View headerChild = header.getChildAt(i);
-            if (!force && headerChild.getVisibility() == View.GONE) {
-                continue;
-            }
-            headerChild.animate().cancel();
-            if (headerChild.getVisibility() != View.GONE) {
-                headerChild.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-            }
-            if (headerChild == mExpandButton) {
-                headerChild.setAlpha(visible ? 1.0f : 0.0f);
-            }
-            if (headerChild == mWorkProfileIcon) {
-                headerChild.setTranslationX(0);
-                headerChild.setTranslationY(0);
-            }
-        }
-    }
-
-    @Override
-    public void prepareFadeIn() {
-        super.prepareFadeIn();
-        if (!(mTransformedView instanceof NotificationHeaderView)) {
-            return;
-        }
-        NotificationHeaderView header = (NotificationHeaderView) mTransformedView;
-        int childCount = header.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View headerChild = header.getChildAt(i);
-            if (headerChild.getVisibility() == View.GONE) {
-                continue;
-            }
-            headerChild.animate().cancel();
-            headerChild.setVisibility(View.VISIBLE);
-            headerChild.setAlpha(1.0f);
-            if (headerChild == mWorkProfileIcon) {
-                headerChild.setTranslationX(0);
-                headerChild.setTranslationY(0);
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
index 6084770..78b967a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
@@ -36,8 +36,8 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification) {
-        super.notifyContentUpdated(notification);
+    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
+        super.notifyContentUpdated(notification, isLowPriority);
         updateImageTag(notification);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
index 3f49125..39db243 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
@@ -41,11 +41,11 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification) {
+    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
         resolveViews(notification);
-        super.notifyContentUpdated(notification);
+        super.notifyContentUpdated(notification, isLowPriority);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 85e87dd..3b18886 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -40,9 +40,6 @@
 
     private final ViewInvertHelper mInvertHelper;
     private final Paint mGreyPaint = new Paint();
-    private int mBackgroundColor = 0;
-    private static final int CUSTOM_BACKGROUND_TAG = R.id.custom_background_color;
-    private boolean mShouldInvertDark;
     private boolean mShowingLegacyBackground;
 
     protected NotificationCustomViewWrapper(View view, ExpandableNotificationRow row) {
@@ -106,32 +103,6 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification) {
-        super.notifyContentUpdated(notification);
-        Drawable background = mView.getBackground();
-        mBackgroundColor = 0;
-        if (background instanceof ColorDrawable) {
-            mBackgroundColor = ((ColorDrawable) background).getColor();
-            mView.setBackground(null);
-            mView.setTag(CUSTOM_BACKGROUND_TAG, mBackgroundColor);
-        } else if (mView.getTag(CUSTOM_BACKGROUND_TAG) != null) {
-            mBackgroundColor = (int) mView.getTag(CUSTOM_BACKGROUND_TAG);
-        }
-        mShouldInvertDark = mBackgroundColor == 0 || isColorLight(mBackgroundColor);
-    }
-
-    private boolean isColorLight(int backgroundColor) {
-        return Color.alpha(backgroundColor) == 0
-                || ColorUtils.calculateLuminance(backgroundColor) > 0.5;
-    }
-
-    @Override
-    public int getCustomBackgroundColor() {
-        // Parent notifications should always use the normal background color
-        return mRow.isSummaryWithChildren() ? 0 : mBackgroundColor;
-    }
-
-    @Override
     public void setShowingLegacyBackground(boolean showing) {
         super.setShowingLegacyBackground(showing);
         mShowingLegacyBackground = showing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 3e4c758..8eab2e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -31,8 +31,12 @@
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 import android.widget.ImageView;
+import android.widget.TextView;
 
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -42,17 +46,21 @@
 
 import java.util.Stack;
 
+import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
+
 /**
  * Wraps a notification header view.
  */
 public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
 
+    private static final Interpolator LOW_PRIORITY_HEADER_CLOSE
+            = new PathInterpolator(0.4f, 0f, 0.7f, 1f);
     private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
             0, PorterDuff.Mode.SRC_ATOP);
     private final int mIconDarkAlpha;
     private final int mIconDarkColor = 0xffffffff;
-    protected final ViewInvertHelper mInvertHelper;
 
+    protected final ViewInvertHelper mInvertHelper;
     protected final ViewTransformationHelper mTransformationHelper;
 
     protected int mColor;
@@ -60,19 +68,50 @@
 
     private ImageView mExpandButton;
     private NotificationHeaderView mNotificationHeader;
+    private TextView mHeaderText;
+    private ImageView mWorkProfileImage;
+    private boolean mIsLowPriority;
 
     protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
         super(view, row);
         mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
         mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
         mTransformationHelper = new ViewTransformationHelper();
+
+        // we want to avoid that the header clashes with the other text when transforming
+        // low-priority
+        mTransformationHelper.setCustomTransformation(
+                new CustomInterpolatorTransformation(TRANSFORMING_VIEW_TITLE) {
+
+                    @Override
+                    public Interpolator getCustomInterpolator(int interpolationType,
+                            boolean isFrom) {
+                        boolean isLowPriority = mView instanceof NotificationHeaderView;
+                        if (interpolationType == TRANSFORM_Y) {
+                            if (isLowPriority && !isFrom
+                                    || !isLowPriority && isFrom) {
+                                return Interpolators.LINEAR_OUT_SLOW_IN;
+                            } else {
+                                return LOW_PRIORITY_HEADER_CLOSE;
+                            }
+                        }
+                        return null;
+                    }
+
+                    @Override
+                    protected boolean hasCustomTransformation() {
+                        return mIsLowPriority;
+                    }
+                }, TRANSFORMING_VIEW_TITLE);
         resolveHeaderViews();
         updateInvertHelper();
     }
 
     protected void resolveHeaderViews() {
         mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
+        mHeaderText = (TextView) mView.findViewById(com.android.internal.R.id.header_text);
         mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
+        mWorkProfileImage = (ImageView) mView.findViewById(com.android.internal.R.id.profile_badge);
         mColor = resolveColor(mExpandButton);
         mNotificationHeader = (NotificationHeaderView) mView.findViewById(
                 com.android.internal.R.id.notification_header);
@@ -89,9 +128,9 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification) {
-        super.notifyContentUpdated(notification);
-
+    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
+        super.notifyContentUpdated(notification, isLowPriority);
+        mIsLowPriority = isLowPriority;
         ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
 
         // Reinspect the notification.
@@ -100,6 +139,11 @@
         updateTransformedTypes();
         addRemainingTransformTypes();
         updateCropToPaddingForImageViews();
+        mIcon.setTag(ImageTransformState.ICON_TAG, notification.getNotification().getSmallIcon());
+        // The work profile image is always the same lets just set the icon tag for it not to
+        // animate
+        mWorkProfileImage.setTag(ImageTransformState.ICON_TAG,
+                notification.getNotification().getSmallIcon());
 
         // We need to reset all views that are no longer transforming in case a view was previously
         // transformed, but now we decided to transform its container instead.
@@ -154,8 +198,11 @@
 
     protected void updateTransformedTypes() {
         mTransformationHelper.reset();
-        mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_HEADER,
-                mNotificationHeader);
+        mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon);
+        if (mIsLowPriority) {
+            mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
+                    mHeaderText);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
index 4ce330c..04ee6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
@@ -40,11 +40,11 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification) {
+    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
         resolveViews(notification);
-        super.notifyContentUpdated(notification);
+        super.notifyContentUpdated(notification, isLowPriority);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
index ff2febf..defeab2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
@@ -22,7 +22,11 @@
 
 import android.content.Context;
 import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
 import android.view.View;
+import android.widget.TextView;
+
+import java.util.ArrayList;
 
 /**
  * Wraps a notification containing a messaging template
@@ -30,6 +34,7 @@
 public class NotificationMessagingTemplateViewWrapper extends NotificationTemplateViewWrapper {
 
     private View mContractedMessage;
+    private ArrayList<View> mHistoricMessages = new ArrayList<View>();
 
     protected NotificationMessagingTemplateViewWrapper(Context ctx, View view,
             ExpandableNotificationRow row) {
@@ -44,21 +49,33 @@
                 && ((MessagingLinearLayout) container).getChildCount() > 0) {
             MessagingLinearLayout messagingContainer = (MessagingLinearLayout) container;
 
-            // Only consider the first child - transforming to a position other than the first
-            // looks bad because we have to move across other messages that are fading in.
-            View child = messagingContainer.getChildAt(0);
-            if (child.getId() == messagingContainer.getContractedChildId()) {
-                mContractedMessage = child;
+            int childCount = messagingContainer.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                View child = messagingContainer.getChildAt(i);
+
+                if (child.getVisibility() == View.GONE
+                        && child instanceof TextView
+                        && !TextUtils.isEmpty(((TextView) child).getText())) {
+                    mHistoricMessages.add(child);
+                }
+
+                // Only consider the first visible child - transforming to a position other than the
+                // first looks bad because we have to move across other messages that are fading in.
+                if (child.getId() == messagingContainer.getContractedChildId()) {
+                    mContractedMessage = child;
+                } else if (child.getVisibility() == View.VISIBLE) {
+                    break;
+                }
             }
         }
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification) {
+    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
         resolveViews();
-        super.notifyContentUpdated(notification);
+        super.notifyContentUpdated(notification, isLowPriority);
     }
 
     @Override
@@ -70,4 +87,11 @@
                     mContractedMessage);
         }
     }
+
+    @Override
+    public void setRemoteInputVisible(boolean visible) {
+        for (int i = 0; i < mHistoricMessages.size(); i++) {
+            mHistoricMessages.get(i).setVisibility(visible ? View.VISIBLE : View.GONE);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 7ca2df9..e9956ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -116,8 +116,10 @@
 
     private void resolveTemplateViews(StatusBarNotification notification) {
         mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
-        mPicture.setTag(ImageTransformState.ICON_TAG,
-                notification.getNotification().getLargeIcon());
+        if (mPicture != null) {
+            mPicture.setTag(ImageTransformState.ICON_TAG,
+                    notification.getNotification().getLargeIcon());
+        }
         mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
         mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
         final View progress = mView.findViewById(com.android.internal.R.id.progress);
@@ -131,11 +133,11 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification) {
+    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
         resolveTemplateViews(notification);
-        super.notifyContentUpdated(notification);
+        super.notifyContentUpdated(notification, isLowPriority);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 16348dfe..836482a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -19,12 +19,17 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.ColorMatrix;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.service.notification.StatusBarNotification;
+import android.support.v4.graphics.ColorUtils;
 import android.view.NotificationHeaderView;
 import android.view.View;
 
 import com.android.systemui.Interpolators;
+import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.TransformableView;
@@ -40,6 +45,8 @@
     protected final View mView;
     protected final ExpandableNotificationRow mRow;
     protected boolean mDark;
+    private int mBackgroundColor = 0;
+    protected boolean mShouldInvertDark;
     protected boolean mDarkInitialized = false;
 
     public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
@@ -80,11 +87,24 @@
 
     /**
      * Notifies this wrapper that the content of the view might have changed.
-     * @param notification
+     * @param notification the notification this is wrapped around
+     * @param isLowPriority is this notification low priority
      */
-    public void notifyContentUpdated(StatusBarNotification notification) {
+    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
         mDarkInitialized = false;
-    };
+        Drawable background = mView.getBackground();
+        mBackgroundColor = 0;
+        if (background instanceof ColorDrawable) {
+            mBackgroundColor = ((ColorDrawable) background).getColor();
+            mView.setBackground(null);
+        }
+        mShouldInvertDark = mBackgroundColor == 0 || isColorLight(mBackgroundColor);
+    }
+
+    private boolean isColorLight(int backgroundColor) {
+        return Color.alpha(backgroundColor) == 0
+                || ColorUtils.calculateLuminance(backgroundColor) > 0.5;
+    }
 
 
     protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
@@ -155,7 +175,8 @@
     }
 
     public int getCustomBackgroundColor() {
-        return 0;
+        // Parent notifications should always use the normal background color
+        return mRow.isSummaryWithChildren() ? 0 : mBackgroundColor;
     }
 
     public void setShowingLegacyBackground(boolean showing) {
@@ -163,4 +184,7 @@
 
     public void setContentHeight(int contentHeight, int minHeightHint) {
     }
+
+    public void setRemoteInputVisible(boolean visible) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 770ec95..d15ab10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -18,10 +18,10 @@
 
 import android.util.ArraySet;
 import android.util.Pools;
-import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
@@ -38,10 +38,11 @@
 */
 public class TransformState {
 
+    public static final int TRANSFORM_X = 0x1;
+    public static final int TRANSFORM_Y = 0x10;
+    public static final int TRANSFORM_ALL = TRANSFORM_X | TRANSFORM_Y;
+
     private static final float UNDEFINED = -1f;
-    private static final int TRANSOFORM_X = 0x1;
-    private static final int TRANSOFORM_Y = 0x10;
-    private static final int TRANSOFORM_ALL = TRANSOFORM_X | TRANSOFORM_Y;
     private static final int CLIP_CLIPPING_SET = R.id.clip_children_set_tag;
     private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag;
     private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag;
@@ -80,25 +81,31 @@
     }
 
     public void transformViewFullyFrom(TransformState otherState, float transformationAmount) {
-        transformViewFrom(otherState, TRANSOFORM_ALL, null, transformationAmount);
+        transformViewFrom(otherState, TRANSFORM_ALL, null, transformationAmount);
+    }
+
+    public void transformViewFullyFrom(TransformState otherState,
+            ViewTransformationHelper.CustomTransformation customTransformation,
+            float transformationAmount) {
+        transformViewFrom(otherState, TRANSFORM_ALL, customTransformation, transformationAmount);
     }
 
     public void transformViewVerticalFrom(TransformState otherState,
             ViewTransformationHelper.CustomTransformation customTransformation,
             float transformationAmount) {
-        transformViewFrom(otherState, TRANSOFORM_Y, customTransformation, transformationAmount);
+        transformViewFrom(otherState, TRANSFORM_Y, customTransformation, transformationAmount);
     }
 
     public void transformViewVerticalFrom(TransformState otherState, float transformationAmount) {
-        transformViewFrom(otherState, TRANSOFORM_Y, null, transformationAmount);
+        transformViewFrom(otherState, TRANSFORM_Y, null, transformationAmount);
     }
 
     private void transformViewFrom(TransformState otherState, int transformationFlags,
             ViewTransformationHelper.CustomTransformation customTransformation,
             float transformationAmount) {
         final View transformedView = mTransformedView;
-        boolean transformX = (transformationFlags & TRANSOFORM_X) != 0;
-        boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
+        boolean transformX = (transformationFlags & TRANSFORM_X) != 0;
+        boolean transformY = (transformationFlags & TRANSFORM_Y) != 0;
         boolean transformScale = transformScale();
         // lets animate the positions correctly
         if (transformationAmount == 0.0f
@@ -153,14 +160,30 @@
         float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
                 transformationAmount);
         if (transformX) {
+            float interpolation = interpolatedValue;
+            if (customTransformation != null) {
+                Interpolator customInterpolator =
+                        customTransformation.getCustomInterpolator(TRANSFORM_X, true /* isFrom */);
+                if (customInterpolator != null) {
+                    interpolation = customInterpolator.getInterpolation(transformationAmount);
+                }
+            }
             transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
                     0.0f,
-                    interpolatedValue));
+                    interpolation));
         }
         if (transformY) {
+            float interpolation = interpolatedValue;
+            if (customTransformation != null) {
+                Interpolator customInterpolator =
+                        customTransformation.getCustomInterpolator(TRANSFORM_Y, true /* isFrom */);
+                if (customInterpolator != null) {
+                    interpolation = customInterpolator.getInterpolation(transformationAmount);
+                }
+            }
             transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
                     0.0f,
-                    interpolatedValue));
+                    interpolation));
         }
         if (transformScale) {
             float transformationStartScaleX = getTransformationStartScaleX();
@@ -207,17 +230,23 @@
     }
 
     public void transformViewFullyTo(TransformState otherState, float transformationAmount) {
-        transformViewTo(otherState, TRANSOFORM_ALL, null, transformationAmount);
+        transformViewTo(otherState, TRANSFORM_ALL, null, transformationAmount);
+    }
+
+    public void transformViewFullyTo(TransformState otherState,
+            ViewTransformationHelper.CustomTransformation customTransformation,
+            float transformationAmount) {
+        transformViewTo(otherState, TRANSFORM_ALL, customTransformation, transformationAmount);
     }
 
     public void transformViewVerticalTo(TransformState otherState,
             ViewTransformationHelper.CustomTransformation customTransformation,
             float transformationAmount) {
-        transformViewTo(otherState, TRANSOFORM_Y, customTransformation, transformationAmount);
+        transformViewTo(otherState, TRANSFORM_Y, customTransformation, transformationAmount);
     }
 
     public void transformViewVerticalTo(TransformState otherState, float transformationAmount) {
-        transformViewTo(otherState, TRANSOFORM_Y, null, transformationAmount);
+        transformViewTo(otherState, TRANSFORM_Y, null, transformationAmount);
     }
 
     private void transformViewTo(TransformState otherState, int transformationFlags,
@@ -226,8 +255,8 @@
         // lets animate the positions correctly
 
         final View transformedView = mTransformedView;
-        boolean transformX = (transformationFlags & TRANSOFORM_X) != 0;
-        boolean transformY = (transformationFlags & TRANSOFORM_Y) != 0;
+        boolean transformX = (transformationFlags & TRANSFORM_X) != 0;
+        boolean transformY = (transformationFlags & TRANSFORM_Y) != 0;
         boolean transformScale = transformScale();
         // lets animate the positions correctly
         if (transformationAmount == 0.0f) {
@@ -264,23 +293,37 @@
         int[] ownPosition = getLaidOutLocationOnScreen();
         if (transformX) {
             float endX = otherStablePosition[0] - ownPosition[0];
-            if (customTransformation != null
-                    && customTransformation.customTransformTarget(this, otherState)) {
-                endX = mTransformationEndX;
+            float interpolation = interpolatedValue;
+            if (customTransformation != null) {
+                if (customTransformation.customTransformTarget(this, otherState)) {
+                    endX = mTransformationEndX;
+                }
+                Interpolator customInterpolator =
+                        customTransformation.getCustomInterpolator(TRANSFORM_X, false /* isFrom */);
+                if (customInterpolator != null) {
+                    interpolation = customInterpolator.getInterpolation(transformationAmount);
+                }
             }
             transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
                     endX,
-                    interpolatedValue));
+                    interpolation));
         }
         if (transformY) {
             float endY = otherStablePosition[1] - ownPosition[1];
-            if (customTransformation != null
-                    && customTransformation.customTransformTarget(this, otherState)) {
-                endY = mTransformationEndY;
+            float interpolation = interpolatedValue;
+            if (customTransformation != null) {
+                if (customTransformation.customTransformTarget(this, otherState)) {
+                    endY = mTransformationEndY;
+                }
+                Interpolator customInterpolator =
+                        customTransformation.getCustomInterpolator(TRANSFORM_Y, false /* isFrom */);
+                if (customInterpolator != null) {
+                    interpolation = customInterpolator.getInterpolation(transformationAmount);
+                }
             }
             transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
                     endY,
-                    interpolatedValue));
+                    interpolation));
         }
         if (transformScale) {
             View otherView = otherState.getTransformedView();
@@ -402,11 +445,6 @@
             result.initFrom(view);
             return result;
         }
-        if (view instanceof NotificationHeaderView) {
-            HeaderTransformState result = HeaderTransformState.obtain();
-            result.initFrom(view);
-            return result;
-        }
         if (view instanceof ImageView) {
             ImageTransformState result = ImageTransformState.obtain();
             result.initFrom(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 5047041..a4e5916 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -39,6 +39,7 @@
     private VisibilityLocationProvider mVisibilityLocationProvider;
     private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
     private ArraySet<View> mAddedChildren = new ArraySet<>();
+    private boolean mPulsing;
 
     /**
      * Add a callback to invoke when reordering is allowed again.
@@ -67,8 +68,16 @@
         updateReorderingAllowed();
     }
 
+    /**
+     * @param pulsing whether we are currently pulsing for ambient display.
+     */
+    public void setPulsing(boolean pulsing) {
+        mPulsing = pulsing;
+        updateReorderingAllowed();
+    }
+
     private void updateReorderingAllowed() {
-        boolean reorderingAllowed = !mScreenOn || !mPanelExpanded;
+        boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
         boolean changed = reorderingAllowed && !mReorderingAllowed;
         mReorderingAllowed = reorderingAllowed;
         if (changed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index a011162..31cfa66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -16,7 +16,10 @@
 
 import android.content.Context;
 import android.os.Handler;
+import android.os.Looper;
 import android.provider.Settings.Secure;
+
+import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
 import com.android.systemui.qs.SecureSetting;
@@ -37,12 +40,12 @@
     public AutoTileManager(Context context, QSTileHost host) {
         mContext = context;
         mHost = host;
-        mHandler = new Handler(mHost.getLooper());
+        mHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
         if (!Prefs.getBoolean(context, Key.QS_HOTSPOT_ADDED, false)) {
-            host.getHotspotController().addCallback(mHotspotCallback);
+            Dependency.get(HotspotController.class).addCallback(mHotspotCallback);
         }
         if (!Prefs.getBoolean(context, Key.QS_DATA_SAVER_ADDED, false)) {
-            host.getNetworkController().getDataSaverController().addCallback(mDataSaverListener);
+            Dependency.get(DataSaverController.class).addCallback(mDataSaverListener);
         }
         if (!Prefs.getBoolean(context, Key.QS_INVERT_COLORS_ADDED, false)) {
             mColorsSetting = new SecureSetting(mContext, mHandler,
@@ -52,43 +55,33 @@
                     if (value != 0) {
                         mHost.addTile("inversion");
                         Prefs.putBoolean(mContext, Key.QS_INVERT_COLORS_ADDED, true);
-                        mHandler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                mColorsSetting.setListening(false);
-                            }
-                        });
+                        mHandler.post(() -> mColorsSetting.setListening(false));
                     }
                 }
             };
             mColorsSetting.setListening(true);
         }
         if (!Prefs.getBoolean(context, Key.QS_WORK_ADDED, false)) {
-            host.getManagedProfileController().addCallback(mProfileCallback);
+            Dependency.get(ManagedProfileController.class).addCallback(mProfileCallback);
         }
     }
 
     public void destroy() {
         mColorsSetting.setListening(false);
-        mHost.getHotspotController().removeCallback(mHotspotCallback);
-        mHost.getNetworkController().getDataSaverController().removeCallback(mDataSaverListener);
-        mHost.getManagedProfileController().removeCallback(mProfileCallback);
+        Dependency.get(HotspotController.class).removeCallback(mHotspotCallback);
+        Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
+        Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
     }
 
     private final ManagedProfileController.Callback mProfileCallback =
             new ManagedProfileController.Callback() {
                 @Override
                 public void onManagedProfileChanged() {
-                    if (mHost.getManagedProfileController().hasActiveProfile()) {
+                    if (Dependency.get(ManagedProfileController.class).hasActiveProfile()) {
                         mHost.addTile("work");
                         Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
-                        mHandler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                mHost.getManagedProfileController().removeCallback(
-                                        mProfileCallback);
-                            }
-                        });
+                        mHandler.post(() -> Dependency.get(ManagedProfileController.class)
+                                .removeCallback(mProfileCallback));
                     }
                 }
 
@@ -105,13 +98,8 @@
             if (isDataSaving) {
                 mHost.addTile("saver");
                 Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mHost.getNetworkController().getDataSaverController().removeCallback(
-                                mDataSaverListener);
-                    }
-                });
+                mHandler.post(() -> Dependency.get(DataSaverController.class).removeCallback(
+                        mDataSaverListener));
             }
         }
     };
@@ -122,12 +110,8 @@
             if (enabled) {
                 mHost.addTile("hotspot");
                 Prefs.putBoolean(mContext, Key.QS_HOTSPOT_ADDED, true);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mHost.getHotspotController().removeCallback(mHotspotCallback);
-                    }
-                });
+                mHandler.post(() -> Dependency.get(HotspotController.class)
+                        .removeCallback(mHotspotCallback));
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 01ffe01..b78f748 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -26,6 +26,7 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
@@ -41,7 +42,9 @@
     private final Handler mHandler = new Handler();
     private final ScrimController mScrimController;
 
+    private final Context mContext;
     private final View mStackScroller;
+    private final NotificationPanelView mNotificationPanelView;
 
     private boolean mDozing;
     private DozeHost.PulseCallback mPulseCallback;
@@ -52,10 +55,12 @@
     private float mBehindTarget;
 
     public DozeScrimController(ScrimController scrimController, Context context,
-            View stackScroller) {
+            View stackScroller, NotificationPanelView notificationPanelView) {
+        mContext = context;
         mStackScroller = stackScroller;
         mScrimController = scrimController;
         mDozeParameters = new DozeParameters(context);
+        mNotificationPanelView = notificationPanelView;
     }
 
     public void setDozing(boolean dozing, boolean animate) {
@@ -65,10 +70,7 @@
             abortAnimations();
             mScrimController.setDozeBehindAlpha(1f);
             mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(0f);
-                mHandler.postDelayed(() -> mStackScroller.setAlpha(0f), 30);
-            }
+            mNotificationPanelView.setDark(true);
         } else {
             cancelPulsing();
             if (animate) {
@@ -83,9 +85,8 @@
                 mScrimController.setDozeBehindAlpha(0f);
                 mScrimController.setDozeInFrontAlpha(0f);
             }
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(1f);
-            }
+            // TODO: animate
+            mNotificationPanelView.setDark(false);
         }
     }
 
@@ -123,9 +124,6 @@
         if (isPulsing()) {
             final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
                     || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(1f);
-            }
             startScrimAnimation(true /* inFront */, 0f,
                     mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
                     pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
@@ -291,9 +289,6 @@
         @Override
         public void run() {
             if (DEBUG) Log.d(TAG, "Pulse out finished");
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(0f);
-            }
             DozeLog.tracePulseFinish();
 
             // Signal that the pulse is all finished so we can turn the screen off now.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index a2c106a..7dcf811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -18,6 +18,11 @@
 
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
+import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
+import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
+import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK;
+import static com.android.systemui.tuner.LockscreenFragment.getIntentButton;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -31,7 +36,9 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -43,6 +50,7 @@
 import android.provider.MediaStore;
 import android.service.media.CameraPrewarmService;
 import android.telecom.TelecomManager;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.TypedValue;
@@ -58,6 +66,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistManager;
@@ -66,13 +75,16 @@
 import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.qs.QS.ActivityStarter;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.PreviewInflater;
+import com.android.systemui.tuner.LockscreenFragment;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
 
 /**
  * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
@@ -80,7 +92,8 @@
  */
 public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
         UnlockMethodCache.OnUnlockMethodChangedListener,
-        AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener {
+        AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener,
+        Tunable {
 
     final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
 
@@ -147,7 +160,13 @@
     private Drawable mLeftAssistIcon;
 
     private IntentButton mRightButton = new DefaultRightButton();
+    private IntentButton mRightDefault = mRightButton;
+    private IntentButton mRightPlugin;
+    private String mRightButtonStr;
     private IntentButton mLeftButton = new DefaultLeftButton();
+    private IntentButton mLeftDefault = mLeftButton;
+    private IntentButton mLeftPlugin;
+    private String mLeftButtonStr;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -230,6 +249,11 @@
         mRightAffordanceView.setOnClickListener(this);
         mLeftAffordanceView.setOnClickListener(this);
         initAccessibility();
+        mActivityStarter = Dependency.get(ActivityStarter.class);
+        mFlashlightController = Dependency.get(FlashlightController.class);
+        mAccessibilityController = Dependency.get(AccessibilityController.class);
+        mAssistManager = Dependency.get(AssistManager.class);
+        updateLeftAffordance();
     }
 
     @Override
@@ -239,6 +263,8 @@
                 mRightListener, IntentButtonProvider.VERSION, false /* Only allow one */);
         PluginManager.getInstance(getContext()).addPluginListener(LEFT_BUTTON_PLUGIN,
                 mLeftListener, IntentButtonProvider.VERSION, false /* Only allow one */);
+        TunerService.get(getContext()).addTunable(this, LockscreenFragment.LOCKSCREEN_LEFT_BUTTON,
+                LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON);
     }
 
     @Override
@@ -246,6 +272,7 @@
         super.onDetachedFromWindow();
         PluginManager.getInstance(getContext()).removePluginListener(mRightListener);
         PluginManager.getInstance(getContext()).removePluginListener(mLeftListener);
+        TunerService.get(getContext()).removeTunable(this);
     }
 
     private void initAccessibility() {
@@ -299,20 +326,6 @@
         mRightAffordanceView.setContentDescription(state.contentDescription);
     }
 
-    public void setActivityStarter(ActivityStarter activityStarter) {
-        mActivityStarter = activityStarter;
-    }
-
-    public void setFlashlightController(FlashlightController flashlightController) {
-        mFlashlightController = flashlightController;
-    }
-
-    public void setAccessibilityController(AccessibilityController accessibilityController) {
-        mAccessibilityController = accessibilityController;
-        mLockIcon.setAccessibilityController(accessibilityController);
-        accessibilityController.addStateChangedCallback(this);
-    }
-
     public void setPhoneStatusBar(PhoneStatusBar phoneStatusBar) {
         mPhoneStatusBar = phoneStatusBar;
         updateCameraVisibility(); // in case onFinishInflate() was called too early
@@ -560,8 +573,10 @@
         if (mPhoneStatusBar.isKeyguardCurrentlySecure()) {
             AsyncTask.execute(runnable);
         } else {
+            boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
+                    && TunerService.get(getContext()).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
             mPhoneStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
-                    false /* dismissShade */, false /* afterKeyguardGone */, true /* deferred */);
+                    dismissShade, false /* afterKeyguardGone */, true /* deferred */);
         }
     }
 
@@ -579,7 +594,9 @@
                 }
             });
         } else {
-            mActivityStarter.startActivity(mLeftButton.getIntent(), false /* dismissShade */);
+            boolean dismissShade = !TextUtils.isEmpty(mLeftButtonStr)
+                    && TunerService.get(getContext()).getValue(LOCKSCREEN_LEFT_UNLOCK, 1) != 0;
+            mActivityStarter.startActivity(mLeftButton.getIntent(), dismissShade);
         }
     }
 
@@ -761,11 +778,6 @@
         mIndicationController = keyguardIndicationController;
     }
 
-    public void setAssistManager(AssistManager assistManager) {
-        mAssistManager = assistManager;
-        updateLeftAffordance();
-    }
-
     public void updateLeftAffordance() {
         updateLeftAffordanceIcon();
         updateLeftPreview();
@@ -776,15 +788,33 @@
         inflateCameraPreview();
     }
 
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (LockscreenFragment.LOCKSCREEN_LEFT_BUTTON.equals(key)) {
+            mLeftButtonStr = newValue;
+            mLeftIsVoiceAssist = TextUtils.isEmpty(mLeftButtonStr) && mLeftPlugin == null;
+            mLeftButton = getIntentButton(mContext, mLeftButtonStr, mLeftPlugin, mLeftDefault);
+            updateLeftAffordance();
+        } else {
+            mRightButtonStr = newValue;
+            mRightButton = getIntentButton(mContext, mRightButtonStr, mRightPlugin, mRightDefault);
+            updateRightAffordanceIcon();
+            updateCameraVisibility();
+            inflateCameraPreview();
+        }
+    }
+
     private void setRightButton(IntentButton button) {
-        mRightButton = button;
+        mRightPlugin = button;
+        mRightButton = getIntentButton(mContext, mRightButtonStr, mRightPlugin, mRightDefault);
         updateRightAffordanceIcon();
         updateCameraVisibility();
         inflateCameraPreview();
     }
 
     private void setLeftButton(IntentButton button) {
-        mLeftButton = button;
+        mLeftPlugin = button;
+        mLeftButton = getIntentButton(mContext, mLeftButtonStr, mLeftPlugin, mLeftDefault);
         mLeftIsVoiceAssist = false;
         updateLeftAffordance();
     }
@@ -792,26 +822,26 @@
     private final PluginListener<IntentButtonProvider> mRightListener =
             new PluginListener<IntentButtonProvider>() {
         @Override
-        public void onPluginConnected(IntentButtonProvider plugin) {
+        public void onPluginConnected(IntentButtonProvider plugin, Context pluginContext) {
             setRightButton(plugin.getIntentButton());
         }
 
         @Override
         public void onPluginDisconnected(IntentButtonProvider plugin) {
-            setRightButton(new DefaultRightButton());
+            setRightButton(null);
         }
     };
 
     private final PluginListener<IntentButtonProvider> mLeftListener =
             new PluginListener<IntentButtonProvider>() {
         @Override
-        public void onPluginConnected(IntentButtonProvider plugin) {
+        public void onPluginConnected(IntentButtonProvider plugin, Context pluginContext) {
             setLeftButton(plugin.getIntentButton());
         }
 
         @Override
         public void onPluginDisconnected(IntentButtonProvider plugin) {
-            setLeftButton(new DefaultLeftButton());
+            setLeftButton(null);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 70beac8ea..c78ec83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -68,6 +68,8 @@
     }
 
     private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
+    private int mClockBottom;
+    private boolean mDark;
 
     /**
      * Refreshes the dimension values.
@@ -86,7 +88,8 @@
     }
 
     public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
-            int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount) {
+            int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount,
+            int clockBottom, boolean dark) {
         mMaxKeyguardNotifications = maxKeyguardNotifications;
         mMaxPanelHeight = maxPanelHeight;
         mExpandedHeight = expandedHeight;
@@ -94,6 +97,8 @@
         mHeight = height;
         mKeyguardStatusHeight = keyguardStatusHeight;
         mEmptyDragAmount = emptyDragAmount;
+        mClockBottom = clockBottom;
+        mDark = dark;
     }
 
     public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
@@ -115,6 +120,9 @@
                 result.clockY,
                 y + getClockNotificationsPadding() + mKeyguardStatusHeight);
         result.clockAlpha = getClockAlpha(result.clockScale);
+        if (mDark) {
+            result.stackScrollerPadding = mClockBottom + y;
+        }
     }
 
     private float getClockScale(int notificationPadding, int clockY, int startPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index e4c778c..ff58e54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -28,13 +28,15 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
-import com.android.systemui.BatteryMeterView;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 
 import java.text.NumberFormat;
@@ -43,7 +45,7 @@
  * The header group on Keyguard.
  */
 public class KeyguardStatusBarView extends RelativeLayout
-        implements BatteryController.BatteryStateChangeCallback {
+        implements BatteryStateChangeCallback, OnUserInfoChangedListener {
 
     private boolean mBatteryCharging;
     private boolean mKeyguardUserSwitcherShowing;
@@ -78,6 +80,7 @@
         mCarrierLabel = (TextView) findViewById(R.id.keyguard_carrier_text);
         loadDimens();
         updateUserSwitcher();
+        mBatteryController = Dependency.get(BatteryController.class);
     }
 
     @Override
@@ -203,23 +206,25 @@
         mMultiUserSwitch.setKeyguardMode(keyguardSwitcherAvailable);
     }
 
-    public void setBatteryController(BatteryController batteryController) {
-        mBatteryController = batteryController;
-        ((BatteryMeterView) findViewById(R.id.battery)).setBatteryController(batteryController);
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        UserInfoController userInfoController = Dependency.get(UserInfoController.class);
+        userInfoController.addCallback(this);
+        mUserSwitcherController = Dependency.get(UserSwitcherController.class);
+        mMultiUserSwitch.setUserSwitcherController(mUserSwitcherController);
+        userInfoController.reloadUserInfo();
     }
 
-    public void setUserSwitcherController(UserSwitcherController controller) {
-        mUserSwitcherController = controller;
-        mMultiUserSwitch.setUserSwitcherController(controller);
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        Dependency.get(UserInfoController.class).removeCallback(this);
     }
 
-    public void setUserInfoController(UserInfoController userInfoController) {
-        userInfoController.addCallback(new UserInfoController.OnUserInfoChangedListener() {
-            @Override
-            public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
-                mMultiUserAvatar.setImageDrawable(picture);
-            }
-        });
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
+        mMultiUserAvatar.setImageDrawable(picture);
     }
 
     public void setQSPanel(QSPanel qsp) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 6dddf18..4535992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -19,6 +19,7 @@
 import android.graphics.Rect;
 import android.view.View;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.policy.BatteryController;
 
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -43,17 +44,27 @@
     private boolean mDockedLight;
     private int mLastStatusBarMode;
     private int mLastNavigationBarMode;
+
+    /**
+     * Whether the navigation bar should be light factoring in already how much alpha the scrim has
+     */
     private boolean mNavigationLight;
+
+    /**
+     * Whether the flags indicate that a light status bar is requested. This doesn't factor in the
+     * scrim alpha yet.
+     */
+    private boolean mHasLightNavigationBar;
+    private boolean mScrimAlphaBelowThreshold;
     private float mScrimAlpha;
 
     private final Rect mLastFullscreenBounds = new Rect();
     private final Rect mLastDockedBounds = new Rect();
 
-    public LightBarController(StatusBarIconController statusBarIconController,
-            BatteryController batteryController) {
+    public LightBarController(StatusBarIconController statusBarIconController) {
         mStatusBarIconController = statusBarIconController;
-        mBatteryController = batteryController;
-        batteryController.addCallback(this);
+        mBatteryController = Dependency.get(BatteryController.class);
+        mBatteryController.addCallback(this);
     }
 
     public void setNavigationBar(LightBarTransitionsController navigationBar) {
@@ -101,7 +112,9 @@
         if ((diffVis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
                 || nbModeChanged) {
             boolean last = mNavigationLight;
-            mNavigationLight = isNavigationLight(newVis, navigationBarMode);
+            mHasLightNavigationBar = isLight(vis, navigationBarMode,
+                    View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+            mNavigationLight = mHasLightNavigationBar && mScrimAlphaBelowThreshold;
             if (mNavigationLight != last) {
                 updateNavigation();
             }
@@ -120,12 +133,11 @@
 
     public void setScrimAlpha(float alpha) {
         mScrimAlpha = alpha;
-        reevaluate();
-    }
-
-    private boolean isNavigationLight(int vis, int barMode) {
-        return isLight(vis, barMode, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
-                && mScrimAlpha < NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD;
+        boolean belowThresholdBefore = mScrimAlphaBelowThreshold;
+        mScrimAlphaBelowThreshold = mScrimAlpha < NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD;
+        if (mHasLightNavigationBar && belowThresholdBefore != mScrimAlphaBelowThreshold) {
+            reevaluate();
+        }
     }
 
     private boolean isLight(int vis, int barMode, int flag) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 695b500..ef42b2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -29,11 +29,12 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
 
 /**
  * Manages the different states and animations of the unlock icon.
  */
-public class LockIcon extends KeyguardAffordanceView {
+public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChangedListener {
 
     private static final int FP_DRAW_OFF_TIMEOUT = 800;
 
@@ -49,6 +50,7 @@
     private boolean mDeviceInteractive;
     private boolean mScreenOn;
     private boolean mLastScreenOn;
+    private Drawable mUserAvatarIcon;
     private TrustDrawable mTrustDrawable;
     private final UnlockMethodCache mUnlockMethodCache;
     private AccessibilityController mAccessibilityController;
@@ -80,6 +82,12 @@
         mTrustDrawable.stop();
     }
 
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
+        mUserAvatarIcon = picture;
+        update();
+    }
+
     public void setTransientFpError(boolean transientFpError) {
         mTransientFpError = transientFpError;
         update();
@@ -126,27 +134,33 @@
         boolean trustHidden = anyFingerprintIcon;
         if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
                 || mScreenOn != mLastScreenOn || force) {
-            boolean isAnim = true;
-            int iconRes = getAnimationResForTransition(mLastState, state, mLastDeviceInteractive,
+            int iconAnimRes =
+                getAnimationResForTransition(mLastState, state, mLastDeviceInteractive,
                     mDeviceInteractive, mLastScreenOn, mScreenOn);
-            if (iconRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
+            boolean isAnim = iconAnimRes != -1;
+            if (iconAnimRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
                 anyFingerprintIcon = true;
                 useAdditionalPadding = true;
                 trustHidden = true;
-            } else if (iconRes == R.drawable.trusted_state_to_error_animation) {
+            } else if (iconAnimRes == R.drawable.trusted_state_to_error_animation) {
                 anyFingerprintIcon = true;
                 useAdditionalPadding = false;
                 trustHidden = true;
-            } else if (iconRes == R.drawable.error_to_trustedstate_animation) {
+            } else if (iconAnimRes == R.drawable.error_to_trustedstate_animation) {
                 anyFingerprintIcon = true;
                 useAdditionalPadding = false;
                 trustHidden = false;
             }
-            if (iconRes == -1) {
-                iconRes = getIconForState(state, mScreenOn, mDeviceInteractive);
-                isAnim = false;
+
+            Drawable icon;
+            if (isAnim) {
+                // Load the animation resource.
+                icon = mContext.getDrawable(iconAnimRes);
+            } else {
+                // Load the static icon resource based on the current state.
+                icon = getIconForState(state, mScreenOn, mDeviceInteractive);
             }
-            Drawable icon = mContext.getDrawable(iconRes);
+
             final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
                     ? (AnimatedVectorDrawable) icon
                     : null;
@@ -175,7 +189,7 @@
                 animation.start();
             }
 
-            if (iconRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
+            if (iconAnimRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
                 removeCallbacks(mDrawOffTimeout);
                 postDelayed(mDrawOffTimeout, FP_DRAW_OFF_TIMEOUT);
             } else {
@@ -225,25 +239,38 @@
         mAccessibilityController = accessibilityController;
     }
 
-    private int getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
+    private Drawable getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
+        int iconRes;
         switch (state) {
             case STATE_LOCKED:
-                return R.drawable.ic_lock_24dp;
+                iconRes = R.drawable.ic_lock_24dp;
+                break;
             case STATE_LOCK_OPEN:
-                return R.drawable.ic_lock_open_24dp;
+                if (mUnlockMethodCache.isTrustManaged() && mUnlockMethodCache.isTrusted()
+                    && mUserAvatarIcon != null) {
+                    return mUserAvatarIcon;
+                } else {
+                    iconRes = R.drawable.ic_lock_open_24dp;
+                }
+                break;
             case STATE_FACE_UNLOCK:
-                return com.android.internal.R.drawable.ic_account_circle;
+                iconRes = com.android.internal.R.drawable.ic_account_circle;
+                break;
             case STATE_FINGERPRINT:
                 // If screen is off and device asleep, use the draw on animation so the first frame
                 // gets drawn.
-                return screenOn && deviceInteractive
+                iconRes = screenOn && deviceInteractive
                         ? R.drawable.ic_fingerprint
                         : R.drawable.lockscreen_fingerprint_draw_on_animation;
+                break;
             case STATE_FINGERPRINT_ERROR:
-                return R.drawable.ic_fingerprint_error;
+                iconRes = R.drawable.ic_fingerprint_error;
+                break;
             default:
                 throw new IllegalArgumentException();
         }
+
+        return mContext.getDrawable(iconRes);
     }
 
     private int getAnimationResForTransition(int oldState, int newState,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index fc33ace..316bd5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -38,8 +38,8 @@
     private boolean mListening;
     private int mCurrentUser;
 
-    public ManagedProfileControllerImpl(QSTileHost host) {
-        mContext = host.getContext();
+    public ManagedProfileControllerImpl(Context context) {
+        mContext = context;
         mUserManager = UserManager.get(mContext);
         mProfiles = new LinkedList<UserInfo>();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 4d4f9d2..3cbac17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -29,7 +29,9 @@
 import android.widget.Button;
 import android.widget.FrameLayout;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -65,7 +67,7 @@
 
     public void setQsPanel(QSPanel qsPanel) {
         mQsPanel = qsPanel;
-        setUserSwitcherController(qsPanel.getHost().getUserSwitcherController());
+        setUserSwitcherController(Dependency.get(UserSwitcherController.class));
     }
 
     public boolean hasMultipleUsers() {
@@ -134,7 +136,7 @@
                 Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
                         getContext(), v, ContactsContract.Profile.CONTENT_URI,
                         ContactsContract.QuickContact.MODE_LARGE, null);
-                mQsPanel.getHost().startActivityDismissingKeyguard(intent);
+                Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index c0f245c..d40326a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -45,6 +45,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.support.annotation.VisibleForTesting;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -63,7 +64,9 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.keyguard.LatencyTracker;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.SystemUIApplication;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.fragments.FragmentHostManager;
@@ -123,16 +126,17 @@
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mCommandQueue = SystemUIApplication.getComponent(getContext(), CommandQueue.class);
+        mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
         mCommandQueue.addCallbacks(this);
-        mPhoneStatusBar = SystemUIApplication.getComponent(getContext(), PhoneStatusBar.class);
-        mRecents = SystemUIApplication.getComponent(getContext(), Recents.class);
-        mDivider = SystemUIApplication.getComponent(getContext(), Divider.class);
+        mPhoneStatusBar = SysUiServiceProvider.getComponent(getContext(), PhoneStatusBar.class);
+        mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
+        mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
         mWindowManager = getContext().getSystemService(WindowManager.class);
         mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
         if (savedInstanceState != null) {
             mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
         }
+        mAssistManager = Dependency.get(AssistManager.class);
 
         try {
             WindowManagerGlobal.getWindowManagerService()
@@ -399,10 +403,6 @@
         ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
         homeButton.setOnTouchListener(this::onHomeTouch);
         homeButton.setOnLongClickListener(this::onHomeLongClick);
-
-        if (mAssistManager != null) {
-            mAssistManager.onConfigurationChanged();
-        }
     }
 
     private boolean onHomeTouch(View v, MotionEvent event) {
@@ -435,10 +435,6 @@
     }
 
     private void onVerticalChanged(boolean isVertical) {
-        if (mAssistManager != null) {
-            // TODO: Clean this up.
-            mAssistManager.onConfigurationChanged();
-        }
         mPhoneStatusBar.setQsScrimEnabled(!isVertical);
     }
 
@@ -447,7 +443,8 @@
         return false;
     }
 
-    private boolean onHomeLongClick(View v) {
+    @VisibleForTesting
+    boolean onHomeLongClick(View v) {
         if (shouldDisableNavbarGestures()) {
             return false;
         }
@@ -549,8 +546,7 @@
 
     private boolean onLongPressRecents() {
         if (mRecents == null || !ActivityManager.supportsMultiWindow()
-                || !mDivider.getView().getSnapAlgorithm()
-                .isSplitScreenFeasible()) {
+                || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()) {
             return false;
         }
 
@@ -560,10 +556,6 @@
 
     // ----- Methods that PhoneStatusBar talks to (should be minimized) -----
 
-    public void setAssistManager(AssistManager assistManager) {
-        mAssistManager = assistManager;
-    }
-
     public void setLightBarController(LightBarController lightBarController) {
         mLightBarController = lightBarController;
         mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index b6feb0e..9b4867e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -17,6 +17,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.drawable.Icon;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.Display;
@@ -47,6 +48,8 @@
     private static final String TAG = "NavBarInflater";
 
     public static final String NAV_BAR_VIEWS = "sysui_nav_bar";
+    public static final String NAV_BAR_LEFT = "sysui_nav_bar_left";
+    public static final String NAV_BAR_RIGHT = "sysui_nav_bar_right";
 
     public static final String MENU_IME = "menu_ime";
     public static final String BACK = "back";
@@ -55,6 +58,8 @@
     public static final String NAVSPACE = "space";
     public static final String CLIPBOARD = "clipboard";
     public static final String KEY = "key";
+    public static final String LEFT = "left";
+    public static final String RIGHT = "right";
 
     public static final String GRAVITY_SEPARATOR = ";";
     public static final String BUTTON_SEPARATOR = ",";
@@ -130,7 +135,8 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        TunerService.get(getContext()).addTunable(this, NAV_BAR_VIEWS);
+        TunerService.get(getContext()).addTunable(this, NAV_BAR_VIEWS, NAV_BAR_LEFT,
+                NAV_BAR_RIGHT);
         PluginManager.getInstance(getContext()).addPluginListener(NavBarButtonProvider.ACTION, this,
                 NavBarButtonProvider.VERSION, true /* Allow multiple */);
     }
@@ -148,6 +154,9 @@
                 clearViews();
                 inflateLayout(newValue);
             }
+        } else if (NAV_BAR_LEFT.equals(key) || NAV_BAR_RIGHT.equals(key)) {
+            clearViews();
+            inflateLayout(mCurrentLayout);
         }
     }
 
@@ -268,6 +277,13 @@
             boolean landscape) {
         View v = null;
         String button = extractButton(buttonSpec);
+        if (LEFT.equals(button)) {
+            buttonSpec = TunerService.get(mContext).getValue(NAV_BAR_LEFT, NAVSPACE);
+            button = extractButton(buttonSpec);
+        } else if (RIGHT.equals(button)) {
+            buttonSpec = TunerService.get(mContext).getValue(NAV_BAR_RIGHT, MENU_IME);
+            button = extractButton(buttonSpec);
+        }
         // Let plugins go first so they can override a standard view if they want.
         for (NavBarButtonProvider provider : mPlugins) {
             v = provider.createView(buttonSpec, parent);
@@ -291,7 +307,14 @@
             v = inflater.inflate(R.layout.custom_key, parent, false);
             ((KeyButtonView) v).setCode(code);
             if (uri != null) {
-                ((KeyButtonView) v).loadAsync(uri);
+                if (uri.contains(":")) {
+                    ((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));
+                } else if (uri.contains("/")) {
+                    int index = uri.indexOf('/');
+                    String pkg = uri.substring(0, index);
+                    int id = Integer.parseInt(uri.substring(index + 1));
+                    ((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));
+                }
             }
         }
         return v;
@@ -365,7 +388,7 @@
     }
 
     @Override
-    public void onPluginConnected(NavBarButtonProvider plugin) {
+    public void onPluginConnected(NavBarButtonProvider plugin, Context context) {
         mPlugins.add(plugin);
         clearViews();
         inflateLayout(mCurrentLayout);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 31c78c8..319f124 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -765,7 +765,7 @@
     }
 
     @Override
-    public void onPluginConnected(NavGesture plugin) {
+    public void onPluginConnected(NavGesture plugin, Context context) {
         mGestureHelper = plugin.getGestureHelper();
         updateTaskSwitchHelper();
     }
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 345dcbd..32b9969 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -154,18 +154,6 @@
                 NotificationShelf.SHOW_AMBIENT_ICONS);
 
         applyNotificationIconsTint();
-        ArrayList<NotificationData.Entry> activeNotifications
-                = notificationData.getActiveNotifications();
-        for (int i = 0; i < activeNotifications.size(); i++) {
-            NotificationData.Entry entry = activeNotifications.get(i);
-            boolean isPreL = Boolean.TRUE.equals(entry.expandedIcon.getTag(R.id.icon_is_pre_L));
-            boolean colorize = !isPreL
-                    || NotificationUtils.isGrayscale(entry.expandedIcon, mNotificationColorUtil);
-            if (colorize) {
-                int color = entry.getContrastedColor(mContext);
-                entry.expandedIcon.setImageTintList(ColorStateList.valueOf(color));
-            }
-        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9fb5980..c25a45c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -22,9 +22,7 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.util.AttributeSet;
-import android.util.Property;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -32,7 +30,6 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.stack.AnimationFilter;
 import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.HeadsUpAppearInterpolator;
 import com.android.systemui.statusbar.stack.ViewState;
 
 import java.util.HashMap;
@@ -98,6 +95,7 @@
     private int mActualLayoutWidth = NO_VALUE;
     private float mActualPaddingEnd = NO_VALUE;
     private float mActualPaddingStart = NO_VALUE;
+    private boolean mCentered;
     private boolean mChangingViewPositions;
     private int mAddAnimationStartIndex = -1;
     private int mCannedAnimationStartIndex = -1;
@@ -105,6 +103,7 @@
     private int mIconSize;
     private float mOpenedAmount = 0.0f;
     private float mVisualOverflowAdaption;
+    private boolean mDisallowNextAnimation;
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -165,6 +164,7 @@
         }
         mAddAnimationStartIndex = -1;
         mCannedAnimationStartIndex = -1;
+        mDisallowNextAnimation = false;
     }
 
     @Override
@@ -310,6 +310,15 @@
                 numDots++;
             }
         }
+        if (mCentered && translationX < getLayoutEnd()) {
+            float delta = (getLayoutEnd() - translationX) / 2;
+            for (int i = 0; i < childCount; i++) {
+                View view = getChildAt(i);
+                IconState iconState = mIconStates.get(view);
+                iconState.xTranslation += delta;
+            }
+        }
+
         if (isLayoutRtl()) {
             for (int i = 0; i < childCount; i++) {
                 View view = getChildAt(i);
@@ -379,6 +388,11 @@
         mChangingViewPositions = changingViewPositions;
     }
 
+    public void setAmbient(boolean ambient) {
+        mCentered = ambient;
+        mDisallowNextAnimation = true;
+    }
+
     public IconState getIconState(StatusBarIconView icon) {
         return mIconStates.get(icon);
     }
@@ -469,7 +483,7 @@
                     animate = true;
                 }
                 icon.setVisibleState(visibleState);
-                if (animate) {
+                if (animate && !mDisallowNextAnimation) {
                     animateTo(icon, animationProperties);
                 } else {
                     super.applyToView(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index d48819a..3bdd5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -211,6 +211,7 @@
     private boolean mOpening;
     private int mIndicationBottomPadding;
     private boolean mIsFullWidth;
+    private boolean mDark;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -391,7 +392,9 @@
                     mNotificationStackScroller.getNotGoneChildCount(),
                     getHeight(),
                     mKeyguardStatusView.getHeight(),
-                    mEmptyDragAmount);
+                    mEmptyDragAmount,
+                    mKeyguardStatusView.getClockBottom(),
+                    mDark);
             mClockPositionAlgorithm.run(mClockPositionResult);
             if (animate || mClockAnimator != null) {
                 startClockAnimation(mClockPositionResult.clockY);
@@ -2453,4 +2456,10 @@
             }
         }
     };
+
+    public void setDark(boolean dark) {
+        mDark = dark;
+        mKeyguardStatusView.setDark(dark);
+        positionClockAndNotifications();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index b338420..7d82477 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -20,6 +20,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
+
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -71,13 +72,11 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -120,15 +119,18 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.BatteryMeterView;
+import com.android.systemui.ActivityStarterDelegate;
 import com.android.systemui.DemoMode;
+import com.android.systemui.Dependency;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Interpolators;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.SystemUIApplication;
 import com.android.systemui.SystemUIFactory;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.doze.DozeHost;
@@ -137,8 +139,9 @@
 import com.android.systemui.fragments.PluginFragmentListener;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.plugins.qs.QS.ActivityStarter;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener;
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.recents.ScreenPinningRequest;
@@ -167,27 +170,22 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
-import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.BatteryControllerImpl;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.PreviewInflater;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -208,7 +206,7 @@
 
 public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
-        OnHeadsUpChangedListener, VisualStabilityManager.Callback {
+        OnHeadsUpChangedListener, VisualStabilityManager.Callback, SnoozeListener {
     static final String TAG = "PhoneStatusBar";
     public static final boolean DEBUG = BaseStatusBar.DEBUG;
     public static final boolean SPEW = false;
@@ -304,25 +302,8 @@
 
     PhoneStatusBarPolicy mIconPolicy;
 
-    // These are no longer handled by the policy, because we need custom strategies for them
-    protected BluetoothControllerImpl mBluetoothController;
-    SecurityControllerImpl mSecurityController;
-    protected BatteryController mBatteryController;
-    LocationControllerImpl mLocationController;
-    NetworkControllerImpl mNetworkController;
-    HotspotControllerImpl mHotspotController;
-    RotationLockControllerImpl mRotationLockController;
-    UserInfoControllerImpl mUserInfoController;
-    protected ZenModeController mZenModeController;
-    CastControllerImpl mCastController;
     VolumeComponent mVolumeComponent;
-    KeyguardUserSwitcher mKeyguardUserSwitcher;
-    FlashlightControllerImpl mFlashlightController;
-    protected UserSwitcherController mUserSwitcherController;
-    NextAlarmControllerImpl mNextAlarmController;
-    protected KeyguardMonitorImpl mKeyguardMonitor;
     BrightnessMirrorController mBrightnessMirrorController;
-    AccessibilityController mAccessibilityController;
     protected FingerprintUnlockController mFingerprintUnlockController;
     LightBarController mLightBarController;
     protected LockscreenWallpaper mLockscreenWallpaper;
@@ -409,23 +390,16 @@
         : null;
 
     private ScreenPinningRequest mScreenPinningRequest;
-    private HandlerThread mHandlerThread;
-    private HandlerThread mTimeTickThread;
-    private Handler mTimeTickHandler;
 
     // ensure quick settings is disabled until the current user makes it through the setup wizard
     private boolean mUserSetup = false;
-    private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
+    private DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
         @Override
-        public void onChange(boolean selfChange) {
-            final boolean userSetup = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.USER_SETUP_COMPLETE,
-                    0 /*default */,
-                    mCurrentUserId);
+        public void onUserSetupChanged() {
+            final boolean userSetup = mDeviceProvisionedController.isUserSetup(
+                    mDeviceProvisionedController.getCurrentUser());
             if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
-                    "selfChange=%s userSetup=%s mUserSetup=%s",
-                    selfChange, userSetup, mUserSetup));
+                    "userSetup=%s mUserSetup=%s", userSetup, mUserSetup));
 
             if (userSetup != mUserSetup) {
                 mUserSetup = userSetup;
@@ -434,9 +408,7 @@
                 if (mKeyguardBottomArea != null) {
                     mKeyguardBottomArea.setUserSetupComplete(mUserSetup);
                 }
-                if (mNetworkController != null) {
-                    mNetworkController.setUserSetupComplete(mUserSetup);
-                }
+                updateQsExpansionEnabled();
             }
             if (mIconPolicy != null) {
                 mIconPolicy.setCurrentUserSetup(mUserSetup);
@@ -637,6 +609,13 @@
         }
     };
 
+    private KeyguardUserSwitcher mKeyguardUserSwitcher;
+    private UserSwitcherController mUserSwitcherController;
+    private NetworkController mNetworkController;
+    private KeyguardMonitorImpl mKeyguardMonitor;
+    private BatteryController mBatteryController;
+    private DeviceProvisionedController mDeviceProvisionedController;
+
     private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
         final int N = array.size();
         for (int i = 0 ; i < N; i++) {
@@ -670,19 +649,20 @@
 
     @Override
     public void start() {
+        mNetworkController = Dependency.get(NetworkController.class);
+        mUserSwitcherController = Dependency.get(UserSwitcherController.class);
+        mKeyguardMonitor = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
+        mBatteryController = Dependency.get(BatteryController.class);
+        mAssistManager = Dependency.get(AssistManager.class);
+        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+
         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay();
         updateDisplaySize();
         mScrimSrcModeEnabled = mContext.getResources().getBoolean(
                 R.bool.config_status_bar_scrim_behind_use_src);
 
-        // Background thread for any controllers that need it.
-        mHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
-        mHandlerThread.start();
-        mTimeTickThread = new HandlerThread("TimeTick");
-        mTimeTickThread.start();
-        mTimeTickHandler = new Handler(mTimeTickThread.getLooper());
-        DateTimeView.setReceiverHandler(mTimeTickHandler);
+        DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
         putComponent(PhoneStatusBar.class, this);
 
         super.start(); // calls createAndAddWindows()
@@ -693,9 +673,7 @@
         // in session state
 
         // Lastly, call to the icon policy to install/update all the icons.
-        mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController, mCastController,
-                mHotspotController, mUserInfoController, mBluetoothController,
-                mRotationLockController, mNetworkController.getDataSaverController(), mNextAlarmController);
+        mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
         mIconPolicy.setCurrentUserSetup(mUserSetup);
         mSettingsObserver.onChange(false); // set up
 
@@ -716,12 +694,11 @@
         mDozeServiceHost = new DozeServiceHost();
         putComponent(DozeHost.class, mDozeServiceHost);
 
-        setControllerUsers();
-
         notifyUserAboutHiddenNotifications();
 
         mScreenPinningRequest = new ScreenPinningRequest(mContext);
         mFalsingManager = FalsingManager.getInstance(mContext);
+        Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
     }
 
     protected void createIconController() {
@@ -795,8 +772,6 @@
             // no window manager? good luck with that
         }
 
-        mAssistManager = SystemUIFactory.getInstance().createAssistManager(this, context);
-
         // figure out which pixel-format to use for the status bar.
         mPixelFormat = PixelFormat.OPAQUE;
 
@@ -827,9 +802,8 @@
                 (KeyguardStatusView) mStatusBarWindow.findViewById(R.id.keyguard_status_view);
         mKeyguardBottomArea =
                 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
-        mKeyguardBottomArea.setActivityStarter(this);
-        mKeyguardBottomArea.setAssistManager(mAssistManager);
-        mKeyguardIndicationController = new KeyguardIndicationController(mContext,
+        mKeyguardIndicationController =
+                SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
                 (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
                 mKeyguardBottomArea.getLockIcon());
         mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
@@ -839,7 +813,7 @@
 
         createIconController();
 
-        mBatteryController = createBatteryController();
+        // TODO: Find better place for this callback.
         mBatteryController.addCallback(new BatteryStateChangeCallback() {
             @Override
             public void onPowerSaveChanged(boolean isPowerSave) {
@@ -848,13 +822,14 @@
                     mDozeServiceHost.firePowerSaveChanged(isPowerSave);
                 }
             }
+
             @Override
             public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
                 // noop
             }
         });
 
-        mLightBarController = new LightBarController(mIconController, mBatteryController);
+        mLightBarController = new LightBarController(mIconController);
         if (mNavigationBar != null) {
             mNavigationBar.setLightBarController(mLightBarController);
         }
@@ -879,39 +854,16 @@
         mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
         mStatusBarView.setScrimController(mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller);
+        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller,
+                mNotificationPanel);
 
         // Other icons
-        mLocationController = new LocationControllerImpl(mContext,
-                mHandlerThread.getLooper()); // will post a notification
-        mNetworkController = new NetworkControllerImpl(mContext, mHandlerThread.getLooper());
-        mNetworkController.setUserSetupComplete(mUserSetup);
-        mHotspotController = new HotspotControllerImpl(mContext);
-        mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper());
-        mSecurityController = new SecurityControllerImpl(mContext);
-        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
-            mRotationLockController = new RotationLockControllerImpl(mContext);
-        }
-        mUserInfoController = new UserInfoControllerImpl(mContext);
         mVolumeComponent = getComponent(VolumeComponent.class);
-        if (mVolumeComponent != null) {
-            mZenModeController = mVolumeComponent.getZenController();
-        }
-        mCastController = new CastControllerImpl(mContext);
 
-        initSignalCluster(mStatusBarView);
-        initSignalCluster(mKeyguardStatusBar);
         initEmergencyCryptkeeperText();
 
-        mFlashlightController = new FlashlightControllerImpl(mContext);
-        mKeyguardBottomArea.setFlashlightController(mFlashlightController);
         mKeyguardBottomArea.setPhoneStatusBar(this);
         mKeyguardBottomArea.setUserSetupComplete(mUserSetup);
-        mAccessibilityController = new AccessibilityController(mContext);
-        mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
-        mNextAlarmController = new NextAlarmControllerImpl(mContext);
-        mKeyguardMonitor = new KeyguardMonitorImpl(mContext);
-            mUserSwitcherController = createUserSwitcherController();
         if (UserManager.get(mContext).isUserSwitcherEnabled()) {
             createUserSwitcher();
         }
@@ -920,15 +872,13 @@
         View container = mStatusBarWindow.findViewById(R.id.qs_frame);
         if (container != null) {
             FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
-            new PluginFragmentListener(container, QS.TAG, R.id.qs_frame, QSFragment.class, QS.class)
+            fragmentHostManager.getFragmentManager().beginTransaction()
+                    .replace(R.id.qs_frame, new QSFragment(), QS.TAG)
+                    .commit();
+            new PluginFragmentListener(container, QS.TAG, QSFragment.class, QS.class)
                     .startListening(QS.ACTION, QS.VERSION);
             final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
-                    mBluetoothController, mLocationController, mRotationLockController,
-                    mNetworkController, mZenModeController, mHotspotController,
-                    mCastController, mFlashlightController,
-                    mUserSwitcherController, mUserInfoController, mKeyguardMonitor,
-                    mSecurityController, mBatteryController, mIconController,
-                    mNextAlarmController);
+                    mIconController);
             mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
             fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
                 QS qs = (QS) f;
@@ -938,21 +888,9 @@
                     mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
                     mKeyguardStatusBar.setQSPanel(mQSPanel);
                 }
-                mHeader = qs.getHeader();
-                initSignalCluster(mHeader);
-                mHeader.setActivityStarter(PhoneStatusBar.this);
             });
         }
 
-        // User info. Trigger first load.
-        mKeyguardStatusBar.setUserInfoController(mUserInfoController);
-        mKeyguardStatusBar.setUserSwitcherController(mUserSwitcherController);
-        mUserInfoController.reloadUserInfo();
-
-        ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(
-                mBatteryController);
-        mKeyguardStatusBar.setBatteryController(mBatteryController);
-
         mReportRejectedTouch = mStatusBarWindow.findViewById(R.id.report_rejected_touch);
         if (mReportRejectedTouch != null) {
             updateReportRejectedTouchVisibility();
@@ -1013,7 +951,8 @@
                 android.Manifest.permission.DUMP, null);
 
         // listen for USER_SETUP_COMPLETE setting (per-user)
-        resetUserSetupObserver();
+        mDeviceProvisionedController.addCallback(mUserSetupObserver);
+        mUserSetupObserver.onUserSetupChanged();
 
         // disable profiling bars, since they overlap and clutter the output on app windows
         ThreadedRenderer.overrideProperty("disableProfileBars", "true");
@@ -1024,19 +963,9 @@
         return mStatusBarView;
     }
 
-    public Handler getTimeTickHandler() {
-        return mTimeTickHandler;
-    }
-
-    public static Handler getTimeTickHandler(Context context) {
-        return ((SystemUIApplication) context.getApplicationContext())
-                .getComponent(PhoneStatusBar.class).getTimeTickHandler();
-    }
-
     protected void createNavigationBar() {
         mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
             mNavigationBar = (NavigationBarFragment) fragment;
-            mNavigationBar.setAssistManager(mAssistManager);
             if (mLightBarController != null) {
                 mNavigationBar.setLightBarController(mLightBarController);
             }
@@ -1062,10 +991,6 @@
         }
     }
 
-    protected BatteryController createBatteryController() {
-        return new BatteryControllerImpl(mContext);
-    }
-
     private void inflateShelf() {
         mNotificationShelf =
                 (NotificationShelf) LayoutInflater.from(mContext).inflate(
@@ -1091,10 +1016,10 @@
         inflateEmptyShadeView();
         updateEmptyShadeView();
         mStatusBarKeyguardViewManager.onDensityOrFontScaleChanged();
-        mUserInfoController.onDensityOrFontScaleChanged();
-        if (mUserSwitcherController != null) {
-            mUserSwitcherController.onDensityOrFontScaleChanged();
-        }
+        // TODO: Bring these out of PhoneStatusBar.
+        ((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
+                .onDensityOrFontScaleChanged();
+        Dependency.get(UserSwitcherController.class).onDensityOrFontScaleChanged();
         if (mKeyguardUserSwitcher != null) {
             mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
         }
@@ -1124,8 +1049,6 @@
                                 R.dimen.signal_cluster_margin_start),
                         0, 0, 0);
                 newCluster.setLayoutParams(layoutParams);
-                newCluster.setSecurityController(mSecurityController);
-                newCluster.setNetworkController(mNetworkController);
                 viewParent.addView(newCluster, index);
                 return newCluster;
             }
@@ -1156,11 +1079,7 @@
     protected void createUserSwitcher() {
         mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
                 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
-                mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
-    }
-
-    protected UserSwitcherController createUserSwitcherController() {
-        return new UserSwitcherController(mContext, mKeyguardMonitor, mHandler, this);
+                mKeyguardStatusBar, mNotificationPanel);
     }
 
     protected void inflateStatusBarWindow(Context context) {
@@ -1168,15 +1087,6 @@
                 R.layout.super_status_bar, null);
     }
 
-    protected void initSignalCluster(View containerView) {
-        SignalClusterView signalCluster =
-                (SignalClusterView) containerView.findViewById(R.id.signal_cluster);
-        if (signalCluster != null) {
-            signalCluster.setSecurityController(mSecurityController);
-            signalCluster.setNetworkController(mNetworkController);
-        }
-    }
-
     public void clearAllNotifications() {
 
         // animate-swipe all dismissable notifications, then animate the shade closed
@@ -1195,8 +1105,10 @@
                 List<ExpandableNotificationRow> children = row.getNotificationChildren();
                 if (row.areChildrenExpanded() && children != null) {
                     for (ExpandableNotificationRow childRow : children) {
-                        if (childRow.getVisibility() == View.VISIBLE) {
-                            viewsToHide.add(childRow);
+                        if (mStackScroller.canChildBeDismissed(childRow)) {
+                            if (childRow.getVisibility() == View.VISIBLE) {
+                                viewsToHide.add(childRow);
+                            }
                         }
                     }
                 }
@@ -1269,6 +1181,8 @@
                 mFingerprintUnlockController);
         mKeyguardIndicationController.setStatusBarKeyguardViewManager(
                 mStatusBarKeyguardViewManager);
+        mKeyguardIndicationController.setUserInfoController(
+                Dependency.get(UserInfoController.class));
         mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mIconPolicy.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
@@ -1478,7 +1392,7 @@
             newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
 
             StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
-                    sbn.getOpPkg(), sbn.getNotificationChannel(),
+                    sbn.getOpPkg(),
                     sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
                     newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
 
@@ -1544,8 +1458,15 @@
             }
             List<ExpandableNotificationRow> notificationChildren =
                     entry.row.getNotificationChildren();
-            ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>(notificationChildren);
-            for (int i = 0; i < toRemove.size(); i++) {
+            ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow row = notificationChildren.get(i);
+                if ((row.getStatusBarNotification().getNotification().flags
+                        & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+                    // the child is a forground service notification which we can't remove!
+                    continue;
+                }
+                toRemove.add(row);
                 toRemove.get(i).setKeepInParent(true);
                 // we need to set this state earlier as otherwise we might generate some weird
                 // animations
@@ -1817,10 +1738,27 @@
     private void updateClearAll() {
         boolean showDismissView =
                 mState != StatusBarState.KEYGUARD &&
-                mNotificationData.hasActiveClearableNotifications();
+               hasActiveClearableNotifications();
         mStackScroller.updateDismissView(showDismissView);
     }
 
+    /**
+     * Return whether there are any clearable notifications
+     */
+    private boolean hasActiveClearableNotifications() {
+        int childCount = mStackScroller.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = mStackScroller.getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                continue;
+            }
+            if (((ExpandableNotificationRow) child).canViewBeDismissed()) {
+                    return true;
+            }
+        }
+        return false;
+    }
+
     private void updateEmptyShadeView() {
         boolean showEmptyShade =
                 mState != StatusBarState.KEYGUARD &&
@@ -1867,7 +1805,7 @@
 
         if (SPEW) {
             final boolean clearable = hasActiveNotifications() &&
-                    mNotificationData.hasActiveClearableNotifications();
+                    hasActiveClearableNotifications();
             Log.d(TAG, "setAreThereNotifications: N=" +
                     mNotificationData.getActiveNotifications().size() + " any=" +
                     hasActiveNotifications() + " clearable=" + clearable);
@@ -2352,6 +2290,11 @@
     }
 
     @Override
+    public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
+        startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade);
+    }
+
+    @Override
     public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
         startActivityDismissingKeyguard(intent, false, dismissShade, callback);
     }
@@ -2375,6 +2318,7 @@
         return getBarState() == StatusBarState.KEYGUARD;
     }
 
+    @Override
     public boolean isDozing() {
         return mDozing;
     }
@@ -2461,6 +2405,9 @@
             }
         } else {
             updateNotificationRanking(null);
+            if (isHeadsUp) {
+                mDozeServiceHost.fireNotificationHeadsUp();
+            }
         }
 
     }
@@ -2860,9 +2807,6 @@
 
     @Override // CommandQueue
     public void buzzBeepBlinked() {
-        if (mDozeServiceHost != null) {
-            mDozeServiceHost.fireBuzzBeepBlinked();
-        }
     }
 
     @Override
@@ -3197,30 +3141,7 @@
         if (mStatusBarWindowManager != null) {
             mStatusBarWindowManager.dump(fd, pw, args);
         }
-        if (mNetworkController != null) {
-            mNetworkController.dump(fd, pw, args);
-        }
-        if (mBluetoothController != null) {
-            mBluetoothController.dump(fd, pw, args);
-        }
-        if (mHotspotController != null) {
-            mHotspotController.dump(fd, pw, args);
-        }
-        if (mCastController != null) {
-            mCastController.dump(fd, pw, args);
-        }
-        if (mUserSwitcherController != null) {
-            mUserSwitcherController.dump(fd, pw, args);
-        }
-        if (mBatteryController != null) {
-            mBatteryController.dump(fd, pw, args);
-        }
-        if (mNextAlarmController != null) {
-            mNextAlarmController.dump(fd, pw, args);
-        }
-        if (mSecurityController != null) {
-            mSecurityController.dump(fd, pw, args);
-        }
+
         if (mHeadsUpManager != null) {
             mHeadsUpManager.dump(fd, pw, args);
         } else {
@@ -3234,9 +3155,6 @@
         if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
             KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
         }
-        if (mFlashlightController != null) {
-            mFlashlightController.dump(fd, pw, args);
-        }
 
         FalsingManager.getInstance(mContext).dump(pw);
         FalsingLog.dump(pw);
@@ -3463,7 +3381,6 @@
 
         updateRowStates();
         mScreenPinningRequest.onConfigurationChanged();
-        mNetworkController.onConfigurationChanged();
     }
 
     @Override
@@ -3473,8 +3390,6 @@
         animateCollapsePanels();
         updatePublicMode();
         updateNotifications();
-        resetUserSetupObserver();
-        setControllerUsers();
         clearCurrentMediaNotification();
         setLockscreenUser(newUserId);
     }
@@ -3485,23 +3400,6 @@
         updateMediaMetaData(true, false);
     }
 
-    private void setControllerUsers() {
-        if (mZenModeController != null) {
-            mZenModeController.setUserId(mCurrentUserId);
-        }
-        if (mSecurityController != null) {
-            mSecurityController.onUserSwitched(mCurrentUserId);
-        }
-    }
-
-    private void resetUserSetupObserver() {
-        mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
-        mUserSetupObserver.onChange(false);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
-                mUserSetupObserver, mCurrentUserId);
-    }
-
     /**
      * Reload some of our resources when the configuration changes.
      *
@@ -3682,32 +3580,24 @@
         }
     };
 
+    @Override
     public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mLeaveOpenOnKeyguardHide = true;
-                executeRunnableDismissingKeyguard(runnable, null, false, false, false);
-            }
+        mHandler.post(() -> {
+            mLeaveOpenOnKeyguardHide = true;
+            executeRunnableDismissingKeyguard(() -> mHandler.post(runnable), null, false, false,
+                    false);
         });
     }
 
+    @Override
     public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                startPendingIntentDismissingKeyguard(intent);
-            }
-        });
+        mHandler.post(() -> startPendingIntentDismissingKeyguard(intent));
     }
 
+    @Override
     public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/);
-            }
-        }, delay);
+        mHandler.postDelayed(() ->
+                handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/), delay);
     }
 
     private void handleStartActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
@@ -3759,10 +3649,6 @@
             mWindowManager.removeViewImmediate(mNavigationBarView);
             mNavigationBarView = null;
         }
-        if (mHandlerThread != null) {
-            mHandlerThread.quitSafely();
-            mHandlerThread = null;
-        }
         mContext.unregisterReceiver(mBroadcastReceiver);
         mContext.unregisterReceiver(mDemoReceiver);
         mAssistManager.destroy();
@@ -3773,12 +3659,11 @@
                 (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
         final SignalClusterView signalClusterQs =
                 (SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
-        mNetworkController.removeCallback(signalCluster);
-        mNetworkController.removeCallback(signalClusterKeyguard);
-        mNetworkController.removeCallback(signalClusterQs);
         if (mQSPanel != null && mQSPanel.getHost() != null) {
             mQSPanel.getHost().destroy();
         }
+        Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null);
+        mDeviceProvisionedController.removeCallback(mUserSetupObserver);
     }
 
     private boolean mDemoModeAllowed;
@@ -4208,6 +4093,7 @@
         mDozeScrimController.setDozing(mDozing &&
                 mFingerprintUnlockController.getMode()
                         != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
+        updateRowStates();
         Trace.endSection();
     }
 
@@ -4723,12 +4609,13 @@
         return !mNotificationData.getActiveNotifications().isEmpty();
     }
 
-    public void wakeUpIfDozing(long time, MotionEvent event) {
+    @Override
+    public void wakeUpIfDozing(long time, PointF where) {
         if (mDozing && mDozeScrimController.isPulsing()) {
             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
             pm.wakeUp(time, "com.android.systemui:NODOZE");
             mWakeUpComingFromTouch = true;
-            mWakeUpTouchLocation = new PointF(event.getX(), event.getY());
+            mWakeUpTouchLocation = where;
             mNotificationPanel.setTouchDisabled(false);
             mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
             mFalsingManager.onScreenOnFromTouch();
@@ -4878,9 +4765,9 @@
             }
         }
 
-        public void fireBuzzBeepBlinked() {
+        public void fireNotificationHeadsUp() {
             for (Callback callback : mCallbacks) {
-                callback.onBuzzBeepBlinked();
+                callback.onNotificationHeadsUp();
             }
         }
 
@@ -4924,12 +4811,14 @@
                 public void onPulseStarted() {
                     callback.onPulseStarted();
                     mStackScroller.setPulsing(true);
+                    mVisualStabilityManager.setPulsing(true);
                 }
 
                 @Override
                 public void onPulseFinished() {
                     callback.onPulseFinished();
                     mStackScroller.setPulsing(false);
+                    mVisualStabilityManager.setPulsing(false);
                 }
             }, reason);
         }
@@ -4950,7 +4839,7 @@
 
         @Override
         public boolean isPowerSaveActive() {
-            return mBatteryController != null && mBatteryController.isPowerSave();
+            return mBatteryController.isPowerSave();
         }
 
         @Override
@@ -4970,4 +4859,14 @@
         }
 
     }
+
+    @Override
+    public SnoozeListener getSnoozeListener() {
+        return this;
+    }
+
+    @Override
+    public void snoozeNotification(StatusBarNotification sbn, long snoozeUntil) {
+        setNotificationSnoozed(sbn, snoozeUntil);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 9ee1e8f..1044ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -36,6 +36,7 @@
 
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
@@ -99,22 +100,19 @@
 
     private BluetoothController mBluetooth;
 
-    public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController,
-            CastController cast, HotspotController hotspot, UserInfoController userInfoController,
-            BluetoothController bluetooth, RotationLockController rotationLockController,
-            DataSaverController dataSaver, NextAlarmController nextAlarm) {
+    public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {
         mContext = context;
         mIconController = iconController;
-        mCast = cast;
-        mHotspot = hotspot;
-        mBluetooth = bluetooth;
+        mCast = Dependency.get(CastController.class);
+        mHotspot = Dependency.get(HotspotController.class);
+        mBluetooth = Dependency.get(BluetoothController.class);
         mBluetooth.addCallback(this);
-        mNextAlarm = nextAlarm;
+        mNextAlarm = Dependency.get(NextAlarmController.class);
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-        mUserInfoController = userInfoController;
+        mUserInfoController = Dependency.get(UserInfoController.class);
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        mRotationLockController = rotationLockController;
-        mDataSaver = dataSaver;
+        mRotationLockController = Dependency.get(RotationLockController.class);
+        mDataSaver = Dependency.get(DataSaverController.class);
 
         mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
         mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 227ebdf..2f76cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -17,15 +17,11 @@
 package com.android.systemui.statusbar.phone;
 
 import android.app.ActivityManager;
-import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -33,8 +29,8 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.View;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.external.CustomTile;
@@ -52,25 +48,12 @@
 import com.android.systemui.qs.tiles.HotspotTile;
 import com.android.systemui.qs.tiles.IntentTile;
 import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.NfcTile;
 import com.android.systemui.qs.tiles.NightDisplayTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.UserTile;
 import com.android.systemui.qs.tiles.WifiTile;
 import com.android.systemui.qs.tiles.WorkModeTile;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
@@ -79,7 +62,6 @@
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 
 /** Platform implementation of the quick settings tile host **/
 public class QSTileHost implements QSTile.Host, Tunable {
@@ -92,81 +74,31 @@
     private final PhoneStatusBar mStatusBar;
     private final LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>();
     protected final ArrayList<String> mTileSpecs = new ArrayList<>();
-    private final BluetoothController mBluetooth;
-    private final LocationController mLocation;
-    private final RotationLockController mRotation;
-    private final NetworkController mNetwork;
-    private final ZenModeController mZen;
-    private final HotspotController mHotspot;
-    private final CastController mCast;
-    private final Looper mLooper;
-    private final FlashlightController mFlashlight;
-    private final UserSwitcherController mUserSwitcherController;
-    private final UserInfoController mUserInfoController;
-    private final KeyguardMonitor mKeyguard;
-    private final SecurityController mSecurity;
-    private final BatteryController mBattery;
-    private final StatusBarIconController mIconController;
     private final TileServices mServices;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
     private final AutoTileManager mAutoTiles;
-    private final ManagedProfileController mProfileController;
-    private final NextAlarmController mNextAlarmController;
-    private final HandlerThread mHandlerThread;
-    private View mHeader;
+    private final StatusBarIconController mIconController;
     private int mCurrentUser;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
-            BluetoothController bluetooth, LocationController location,
-            RotationLockController rotation, NetworkController network,
-            ZenModeController zen, HotspotController hotspot,
-            CastController cast, FlashlightController flashlight,
-            UserSwitcherController userSwitcher, UserInfoController userInfo,
-            KeyguardMonitor keyguard, SecurityController security,
-            BatteryController battery, StatusBarIconController iconController,
-            NextAlarmController nextAlarmController) {
+            StatusBarIconController iconController) {
+        mIconController = iconController;
         mContext = context;
         mStatusBar = statusBar;
-        mBluetooth = bluetooth;
-        mLocation = location;
-        mRotation = rotation;
-        mNetwork = network;
-        mZen = zen;
-        mHotspot = hotspot;
-        mCast = cast;
-        mFlashlight = flashlight;
-        mUserSwitcherController = userSwitcher;
-        mUserInfoController = userInfo;
-        mKeyguard = keyguard;
-        mSecurity = security;
-        mBattery = battery;
-        mIconController = iconController;
-        mNextAlarmController = nextAlarmController;
-        mProfileController = new ManagedProfileControllerImpl(this);
 
-        mHandlerThread = new HandlerThread(QSTileHost.class.getSimpleName(),
-                Process.THREAD_PRIORITY_BACKGROUND);
-        mHandlerThread.start();
-        mLooper = mHandlerThread.getLooper();
-
-        mServices = new TileServices(this, mLooper);
+        mServices = new TileServices(this, Dependency.get(Dependency.BG_LOOPER));
 
         TunerService.get(mContext).addTunable(this, TILES_SETTING);
         // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
         mAutoTiles = new AutoTileManager(context, this);
     }
 
-    public NextAlarmController getNextAlarmController() {
-        return mNextAlarmController;
-    }
-
-    public void setHeaderView(View view) {
-        mHeader = view;
+    public StatusBarIconController getIconController() {
+        return mIconController;
     }
 
     public void destroy() {
-        mHandlerThread.quitSafely();
         mTiles.values().forEach(tile -> tile.destroy());
         mAutoTiles.destroy();
         TunerService.get(mContext).removeTunable(this);
@@ -189,30 +121,10 @@
     }
 
     @Override
-    public void startActivityDismissingKeyguard(final Intent intent) {
-        mStatusBar.postStartActivityDismissingKeyguard(intent, 0);
-    }
-
-    @Override
-    public void startActivityDismissingKeyguard(PendingIntent intent) {
-        mStatusBar.postStartActivityDismissingKeyguard(intent);
-    }
-
-    @Override
-    public void startRunnableDismissingKeyguard(Runnable runnable) {
-        mStatusBar.postQSRunnableDismissingKeyguard(runnable);
-    }
-
-    @Override
     public void warn(String message, Throwable t) {
         // already logged
     }
 
-    public void animateToggleQSExpansion() {
-        // TODO: Better path to animated panel expansion.
-        mHeader.callOnClick();
-    }
-
     @Override
     public void collapsePanels() {
         mStatusBar.postAnimateCollapsePanels();
@@ -224,91 +136,15 @@
     }
 
     @Override
-    public Looper getLooper() {
-        return mLooper;
-    }
-
-    @Override
     public Context getContext() {
         return mContext;
     }
 
-    @Override
-    public BluetoothController getBluetoothController() {
-        return mBluetooth;
-    }
-
-    @Override
-    public LocationController getLocationController() {
-        return mLocation;
-    }
-
-    @Override
-    public RotationLockController getRotationLockController() {
-        return mRotation;
-    }
-
-    @Override
-    public NetworkController getNetworkController() {
-        return mNetwork;
-    }
-
-    @Override
-    public ZenModeController getZenModeController() {
-        return mZen;
-    }
-
-    @Override
-    public HotspotController getHotspotController() {
-        return mHotspot;
-    }
-
-    @Override
-    public CastController getCastController() {
-        return mCast;
-    }
-
-    @Override
-    public FlashlightController getFlashlightController() {
-        return mFlashlight;
-    }
-
-    @Override
-    public KeyguardMonitor getKeyguardMonitor() {
-        return mKeyguard;
-    }
-
-    @Override
-    public UserSwitcherController getUserSwitcherController() {
-        return mUserSwitcherController;
-    }
-
-    @Override
-    public UserInfoController getUserInfoController() {
-        return mUserInfoController;
-    }
-
-    @Override
-    public BatteryController getBatteryController() {
-        return mBattery;
-    }
-
-    public SecurityController getSecurityController() {
-        return mSecurity;
-    }
 
     public TileServices getTileServices() {
         return mServices;
     }
 
-    public StatusBarIconController getIconController() {
-        return mIconController;
-    }
-
-    public ManagedProfileController getManagedProfileController() {
-        return mProfileController;
-    }
-
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (!TILES_SETTING.equals(key)) {
@@ -440,6 +276,7 @@
         else if (tileSpec.equals("battery")) return new BatteryTile(this);
         else if (tileSpec.equals("saver")) return new DataSaverTile(this);
         else if (tileSpec.equals("night")) return new NightDisplayTile(this);
+        else if (tileSpec.equals("nfc")) return new NfcTile(this);
         // Intent tiles.
         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
         else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 08a91bb..c0e9653 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -21,10 +21,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.RippleDrawable;
+import android.icu.text.NumberFormat;
 import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
 import android.util.AttributeSet;
+import android.util.SparseBooleanArray;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
@@ -32,21 +37,27 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.keyguard.KeyguardStatusView;
+import com.android.settingslib.Utils;
+import com.android.systemui.ActivityStarter;
+import com.android.systemui.BatteryMeterView;
+import com.android.systemui.Dependency;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QS.ActivityStarter;
 import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader;
-import com.android.systemui.qs.QSPanel;
 import com.android.systemui.plugins.qs.QS.Callback;
+import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QuickQSPanel;
 import com.android.systemui.qs.TouchAnimator;
 import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.SignalClusterView;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -54,7 +65,8 @@
 import com.android.systemui.tuner.TunerService;
 
 public class QuickStatusBarHeader extends BaseStatusBarHeader implements
-        NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener {
+        NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener,
+        BatteryStateChangeCallback, SignalCallback {
 
     private static final String TAG = "QuickStatusBarHeader";
 
@@ -62,19 +74,20 @@
 
     private ActivityStarter mActivityStarter;
     private NextAlarmController mNextAlarmController;
+    private UserInfoController mUserInfoController;
     private SettingsButton mSettingsButton;
     protected View mSettingsContainer;
 
     private TextView mAlarmStatus;
     private View mAlarmStatusCollapsed;
+    private ViewGroup mDateTimeGroup;
+    private ViewGroup mDateTimeAlarmGroup;
 
     private QSPanel mQsPanel;
 
     private boolean mExpanded;
     private boolean mAlarmShowing;
 
-    private ViewGroup mDateTimeGroup;
-    private ViewGroup mDateTimeAlarmGroup;
     private TextView mEmergencyOnly;
 
     protected ExpandableIndicator mExpandIndicator;
@@ -95,6 +108,9 @@
     protected View mEdit;
     private boolean mShowFullAlarm;
     private float mDateTimeTranslation;
+    private TextView mBatteryLevel;
+    private SparseBooleanArray mRoamingsBySubId = new SparseBooleanArray();
+    private boolean mIsRoaming;
 
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -108,7 +124,8 @@
 
         mEdit = findViewById(android.R.id.edit);
         findViewById(android.R.id.edit).setOnClickListener(view ->
-                mHost.startRunnableDismissingKeyguard(() -> mQsPanel.showEdit(view)));
+                Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
+                        mQsPanel.showEdit(view)));
 
         mDateTimeAlarmGroup = (ViewGroup) findViewById(R.id.date_time_alarm_group);
         mDateTimeAlarmGroup.findViewById(R.id.empty_time_view).setVisibility(View.GONE);
@@ -130,6 +147,8 @@
         mAlarmStatus = (TextView) findViewById(R.id.alarm_status);
         mAlarmStatus.setOnClickListener(this);
 
+        mBatteryLevel = (TextView) findViewById(R.id.battery_level);
+
         mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
         mMultiUserAvatar = (ImageView) mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
 
@@ -139,6 +158,19 @@
         ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
 
         updateResources();
+
+        // Set the light/dark theming on the header status UI to match the current theme.
+        SignalClusterView cluster = (SignalClusterView) findViewById(R.id.signal_cluster);
+        int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
+        float intensity = colorForeground == Color.WHITE ? 0 : 1;
+        cluster.setIconTint(colorForeground, intensity, new Rect(0, 0, 0, 0));
+        BatteryMeterView battery = (BatteryMeterView) findViewById(R.id.battery);
+        int colorSecondary = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary);
+        battery.setRawColors(colorForeground, colorSecondary);
+
+        mNextAlarmController = Dependency.get(NextAlarmController.class);
+        mUserInfoController = Dependency.get(UserInfoController.class);
+        mActivityStarter = Dependency.get(ActivityStarter.class);
     }
 
     @Override
@@ -240,9 +272,6 @@
     @VisibleForTesting
     public void onDetachedFromWindow() {
         setListening(false);
-        mHost.getUserInfoController().removeCallback(this);
-        mHost.getNetworkController().removeEmergencyListener(this);
-        mHost.getUserInfoController().removeCallback(this);
         super.onDetachedFromWindow();
     }
 
@@ -276,7 +305,7 @@
     protected void updateVisibilities() {
         updateAlarmVisibilities();
         updateDateTimePosition();
-        mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly
+        mEmergencyOnly.setVisibility(mExpanded && (mShowEmergencyCallsOnly || mIsRoaming)
                 ? View.VISIBLE : View.INVISIBLE);
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
                 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
@@ -287,23 +316,26 @@
     }
 
     private void updateDateTimePosition() {
-        mDateTimeAlarmGroup.setTranslationY(mShowEmergencyCallsOnly
+        mDateTimeAlarmGroup.setTranslationY(mShowEmergencyCallsOnly || mIsRoaming
                 ? mExpansionAmount * mDateTimeTranslation : 0);
     }
 
     private void updateListeners() {
         if (mListening) {
             mNextAlarmController.addCallback(this);
+            mUserInfoController.addCallback(this);
+            if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
+                Dependency.get(NetworkController.class).addEmergencyListener(this);
+                Dependency.get(NetworkController.class).addCallback(this);
+            }
         } else {
             mNextAlarmController.removeCallback(this);
+            mUserInfoController.removeCallback(this);
+            Dependency.get(NetworkController.class).removeEmergencyListener(this);
+            Dependency.get(NetworkController.class).removeCallback(this);
         }
     }
 
-    @Override
-    public void setActivityStarter(ActivityStarter activityStarter) {
-        mActivityStarter = activityStarter;
-    }
-
     public void setQSPanel(final QSPanel qsPanel) {
         mQsPanel = qsPanel;
         setupHost(qsPanel.getHost());
@@ -314,17 +346,9 @@
 
     public void setupHost(final QSTileHost host) {
         mHost = host;
-        host.setHeaderView(mExpandIndicator);
+        //host.setHeaderView(mExpandIndicator);
         mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
         mHeaderQsPanel.setHost(host, null /* No customization in header */);
-        setUserInfoController(host.getUserInfoController());
-        setBatteryController(host.getBatteryController());
-        setNextAlarmController(host.getNextAlarmController());
-
-        final boolean isAPhone = mHost.getNetworkController().hasVoiceCallingFeature();
-        if (isAPhone) {
-            mHost.getNetworkController().addEmergencyListener(this);
-        }
     }
 
     @Override
@@ -334,7 +358,7 @@
                     mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
                             : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
             if (mSettingsButton.isTunerClick()) {
-                mHost.startRunnableDismissingKeyguard(() -> post(() -> {
+                Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
                     if (TunerService.isTunerEnabled(mContext)) {
                         TunerService.showResetRequest(mContext, () -> {
                             // Relaunch settings so that the tuner disappears.
@@ -347,7 +371,7 @@
                     }
                     startSettingsActivity();
 
-                }));
+                });
             } else {
                 startSettingsActivity();
             }
@@ -362,18 +386,6 @@
                 true /* dismissShade */);
     }
 
-    public void setNextAlarmController(NextAlarmController nextAlarmController) {
-        mNextAlarmController = nextAlarmController;
-    }
-
-    public void setBatteryController(BatteryController batteryController) {
-        // Don't care
-    }
-
-    public void setUserInfoController(UserInfoController userInfoController) {
-        userInfoController.addCallback(this);
-    }
-
     @Override
     public void setCallback(Callback qsPanelCallback) {
         mHeaderQsPanel.setCallback(qsPanelCallback);
@@ -391,6 +403,39 @@
     }
 
     @Override
+    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+        String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
+        mBatteryLevel.setText(percentage);
+    }
+
+    @Override
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        // Don't care.
+    }
+
+    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
+            int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
+            String description, boolean isWide, int subId, boolean roaming) {
+        mRoamingsBySubId.put(subId, roaming);
+        boolean isRoaming = calculateRoaming();
+        if (mIsRoaming != isRoaming) {
+            mIsRoaming = isRoaming;
+            mEmergencyOnly.setText(mIsRoaming ? R.string.accessibility_data_connection_roaming
+                    : com.android.internal.R.string.emergency_calls_only);
+            if (mExpanded) {
+                updateEverything();
+            }
+        }
+    }
+
+    private boolean calculateRoaming() {
+        for (int i = 0; i < mRoamingsBySubId.size(); i++) {
+            if (mRoamingsBySubId.valueAt(i)) return true;
+        }
+        return false;
+    }
+
+    @Override
     public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
         mMultiUserAvatar.setImageDrawable(picture);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 517551d..8fcbf38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -338,13 +338,13 @@
     private void setCurrentScrimAlpha(View scrim, float alpha) {
         if (scrim == mScrimBehind) {
             mCurrentBehindAlpha = alpha;
+            mLightBarController.setScrimAlpha(mCurrentBehindAlpha);
         } else if (scrim == mScrimInFront) {
             mCurrentInFrontAlpha = alpha;
         } else {
             alpha = Math.max(0.0f, Math.min(1.0f, alpha));
             mCurrentHeadsUpAlpha = alpha;
         }
-        mLightBarController.setScrimAlpha(mCurrentBehindAlpha);
     }
 
     protected void updateScrimColor(View scrim) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 2e279b2..7e5a7da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -491,7 +491,7 @@
     private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
         @Override
         public void run() {
-            mPhoneStatusBar.getNavigationBarView().setVisibility(View.VISIBLE);
+            mPhoneStatusBar.getNavigationBarView().getRootView().setVisibility(View.VISIBLE);
         }
     };
 
@@ -527,7 +527,7 @@
                     }
                 } else {
                     mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
-                    mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE);
+                    mPhoneStatusBar.getNavigationBarView().getRootView().setVisibility(View.GONE);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 1b73a3f..ccd6357 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -29,6 +29,7 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.media.session.MediaSessionLegacyHelper;
 import android.net.Uri;
 import android.os.Bundle;
@@ -223,7 +224,8 @@
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_UP:
                 if (mService.isDozing()) {
-                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, true);
+                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
+                            event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
                     return true;
                 }
                 break;
@@ -263,12 +265,9 @@
         if (mNotificationPanel.isFullyExpanded()
                 && mStackScrollLayout.getVisibility() == View.VISIBLE
                 && mService.getBarState() == StatusBarState.KEYGUARD
-                && !mService.isBouncerShowing()) {
+                && !mService.isBouncerShowing()
+                && !mService.isDozing()) {
             intercept = mDragDownHelper.onInterceptTouchEvent(ev);
-            // wake up on a touch down event, if dozing
-            if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                mService.wakeUpIfDozing(ev.getEventTime(), ev);
-            }
         }
         if (!intercept) {
             super.onInterceptTouchEvent(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 19dcf03..48ff1c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -17,12 +17,13 @@
 package com.android.systemui.statusbar.policy;
 
 import com.android.systemui.DemoMode;
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-public interface BatteryController extends DemoMode,
+public interface BatteryController extends DemoMode, Dumpable,
         CallbackController<BatteryStateChangeCallback> {
     /**
      * Prints the current state of the {@link BatteryController} to the given {@link PrintWriter}.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index 4c1c378..df30e20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -17,11 +17,12 @@
 package com.android.systemui.statusbar.policy;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.BluetoothController.Callback;
 
 import java.util.Collection;
 
-public interface BluetoothController extends CallbackController<Callback> {
+public interface BluetoothController extends CallbackController<Callback>, Dumpable {
     boolean isBluetoothSupported();
     boolean isBluetoothEnabled();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 3142ddf..5ab99e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -47,7 +47,7 @@
     private final ArrayList<SignalCallback> mSignalCallbacks = new ArrayList<>();
 
     public CallbackHandler() {
-        super();
+        super(Looper.getMainLooper());
     }
 
     @VisibleForTesting
@@ -125,14 +125,14 @@
     public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon,
             final int statusType, final int qsType,final boolean activityIn,
             final boolean activityOut, final String typeContentDescription,
-            final String description, final boolean isWide, final int subId) {
+            final String description, final boolean isWide, final int subId, boolean roaming) {
         post(new Runnable() {
             @Override
             public void run() {
                 for (SignalCallback signalCluster : mSignalCallbacks) {
                     signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
                             activityIn, activityOut, typeContentDescription, description, isWide,
-                            subId);
+                            subId, roaming);
                 }
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index 6988af7..97be6ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -16,11 +16,12 @@
 
 package com.android.systemui.statusbar.policy;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.CastController.Callback;
 
 import java.util.Set;
 
-public interface CastController extends CallbackController<Callback> {
+public interface CastController extends CallbackController<Callback>, Dumpable {
     void setDiscovering(boolean request);
     void setCurrentUserId(int currentUserId);
     Set<CastDevice> getCastDevices();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 1dbc664..9cc9749 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -38,8 +38,8 @@
 import android.widget.TextView;
 
 import com.android.systemui.DemoMode;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
@@ -107,7 +107,7 @@
             filter.addAction(Intent.ACTION_USER_SWITCHED);
 
             getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter,
-                    null, PhoneStatusBar.getTimeTickHandler(getContext()));
+                    null, Dependency.get(Dependency.TIME_TICK_HANDLER));
             TunerService.get(getContext()).addTunable(this, CLOCK_SECONDS,
                     StatusBarIconController.ICON_BLACKLIST);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index 5544c70..dc33633 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -23,12 +23,11 @@
 import android.content.res.TypedArray;
 import android.icu.text.DateFormat;
 import android.icu.text.DisplayContext;
-import android.os.Handler;
 import android.util.AttributeSet;
 import android.widget.TextView;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 import java.util.Date;
 import java.util.Locale;
@@ -87,7 +86,7 @@
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
         getContext().registerReceiver(mIntentReceiver, filter, null,
-                PhoneStatusBar.getTimeTickHandler(getContext()));
+                Dependency.get(Dependency.TIME_TICK_HANDLER));
 
         updateClock();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
new file mode 100644
index 0000000..aa4eaa7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.content.Context;
+
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+
+public interface DeviceProvisionedController extends CallbackController<DeviceProvisionedListener> {
+
+    boolean isDeviceProvisioned();
+    boolean isUserSetup(int currentUser);
+    int getCurrentUser();
+
+    interface DeviceProvisionedListener {
+        default void onDeviceProvisionedChanged() { }
+        default void onUserSwitched() { }
+        default void onUserSetupChanged() { }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
new file mode 100644
index 0000000..528fefe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings.Global;
+import android.provider.Settings.Secure;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.settings.CurrentUserTracker;
+
+import java.util.ArrayList;
+
+public class DeviceProvisionedControllerImpl extends CurrentUserTracker implements
+        DeviceProvisionedController {
+
+    private final ArrayList<DeviceProvisionedListener> mListeners = new ArrayList<>();
+    private final ContentResolver mContentResolver;
+    private final Context mContext;
+    private final Uri mDeviceProvisionedUri;
+    private final Uri mUserSetupUri;
+
+    public DeviceProvisionedControllerImpl(Context context) {
+        super(context);
+        mContext = context;
+        mContentResolver = context.getContentResolver();
+        mDeviceProvisionedUri = Global.getUriFor(Global.DEVICE_PROVISIONED);
+        mUserSetupUri = Secure.getUriFor(Secure.USER_SETUP_COMPLETE);
+    }
+
+    @Override
+    public boolean isDeviceProvisioned() {
+        return Global.getInt(mContentResolver, Global.DEVICE_PROVISIONED, 0) != 0;
+    }
+
+    @Override
+    public boolean isUserSetup(int currentUser) {
+        return Secure.getIntForUser(mContentResolver, Secure.USER_SETUP_COMPLETE, 0, currentUser)
+                != 0;
+    }
+
+    @Override
+    public int getCurrentUser() {
+        return ActivityManager.getCurrentUser();
+    }
+
+    @Override
+    public void addCallback(DeviceProvisionedListener listener) {
+        mListeners.add(listener);
+        if (mListeners.size() == 1) {
+            startListening(getCurrentUser());
+        }
+    }
+
+    @Override
+    public void removeCallback(DeviceProvisionedListener listener) {
+        mListeners.remove(listener);
+        if (mListeners.size() == 0) {
+            stopListening();
+        }
+    }
+
+    private void startListening(int user) {
+        mContentResolver.registerContentObserver(mDeviceProvisionedUri, true,
+                mSettingsObserver, 0);
+        mContentResolver.registerContentObserver(mUserSetupUri, true,
+                mSettingsObserver, user);
+        startTracking();
+    }
+
+    private void stopListening() {
+        stopTracking();
+        mContentResolver.unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
+    public void onUserSwitched(int newUserId) {
+        mContentResolver.unregisterContentObserver(mSettingsObserver);
+        mContentResolver.registerContentObserver(mDeviceProvisionedUri, true,
+                mSettingsObserver, 0);
+        mContentResolver.registerContentObserver(mUserSetupUri, true,
+                mSettingsObserver, newUserId);
+        notifyUserChanged();
+    }
+
+    private void notifyUserChanged() {
+        mListeners.forEach(c -> c.onUserSwitched());
+    }
+
+    private void notifySetupChanged() {
+        mListeners.forEach(c -> c.onUserSetupChanged());
+    }
+
+    private void notifyProvisionedChanged() {
+        mListeners.forEach(c -> c.onDeviceProvisionedChanged());
+    }
+
+    protected final ContentObserver mSettingsObserver = new ContentObserver(Dependency.get(
+            Dependency.MAIN_HANDLER)) {
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (mUserSetupUri.equals(uri)) {
+                notifySetupChanged();
+            } else {
+                notifyProvisionedChanged();
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index 6023f3e..e576f36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -14,9 +14,10 @@
 
 package com.android.systemui.statusbar.policy;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
 
-public interface FlashlightController extends CallbackController<FlashlightListener> {
+public interface FlashlightController extends CallbackController<FlashlightListener>, Dumpable {
 
     boolean hasFlashlight();
     void setFlashlight(boolean newState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index daf9d6b..0543678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -16,9 +16,10 @@
 
 package com.android.systemui.statusbar.policy;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
 
-public interface HotspotController extends CallbackController<Callback> {
+public interface HotspotController extends CallbackController<Callback>, Dumpable {
     boolean isHotspotEnabled();
     void setHotspotEnabled(boolean enabled);
     boolean isHotspotSupported();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 45cfbdc..882902e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -116,18 +116,18 @@
         mOnClickListener = onClickListener;
     }
 
-    public void loadAsync(String uri) {
-        new AsyncTask<String, Void, Drawable>() {
+    public void loadAsync(Icon icon) {
+        new AsyncTask<Icon, Void, Drawable>() {
             @Override
-            protected Drawable doInBackground(String... params) {
-                return Icon.createWithContentUri(params[0]).loadDrawable(mContext);
+            protected Drawable doInBackground(Icon... params) {
+                return params[0].loadDrawable(mContext);
             }
 
             @Override
             protected void onPostExecute(Drawable drawable) {
                 setImageDrawable(drawable);
             }
-        }.execute(uri);
+        }.execute(icon);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 1cf4050..4b283fed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -30,6 +30,7 @@
 import android.widget.FrameLayout;
 
 import com.android.settingslib.animation.AppearAnimationUtils;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.qs.tiles.UserDetailItemView;
@@ -56,10 +57,10 @@
     private boolean mAnimating;
 
     public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
-            KeyguardStatusBarView statusBarView, NotificationPanelView panelView,
-            UserSwitcherController userSwitcherController) {
+            KeyguardStatusBarView statusBarView, NotificationPanelView panelView) {
         boolean keyguardUserSwitcherEnabled =
                 context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON;
+        UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class);
         if (userSwitcherController != null && keyguardUserSwitcherEnabled) {
             mUserSwitcherContainer = (Container) userSwitcher.inflate();
             mBackground = new KeyguardUserSwitcherScrim(context);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 83463e2..03c46e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -226,10 +226,8 @@
         final boolean dataDisabled = mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
                 && mCurrentState.userSetup;
 
-        // Show icon in QS when we are connected or need to show roaming or data is disabled.
-        boolean showDataIcon = mCurrentState.dataConnected
-                || mCurrentState.iconGroup == TelephonyIcons.ROAMING
-                || dataDisabled;
+        // Show icon in QS when we are connected or data is disabled.
+        boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
         IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
                 getCurrentIconId(), contentDescription);
 
@@ -249,13 +247,11 @@
         boolean activityOut = mCurrentState.dataConnected
                         && !mCurrentState.carrierNetworkChangeMode
                         && mCurrentState.activityOut;
-        showDataIcon &= mCurrentState.isDefault
-                || mCurrentState.iconGroup == TelephonyIcons.ROAMING
-                || dataDisabled;
+        showDataIcon &= mCurrentState.isDefault || dataDisabled;
         int typeIcon = showDataIcon ? icons.mDataType : 0;
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
-                mSubscriptionInfo.getSubscriptionId());
+                mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);
     }
 
     @Override
@@ -405,10 +401,9 @@
         mCurrentState.dataConnected = mCurrentState.connected
                 && mDataState == TelephonyManager.DATA_CONNECTED;
 
+        mCurrentState.roaming = isRoaming();
         if (isCarrierNetworkChangeActive()) {
             mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
-        } else if (isRoaming()) {
-            mCurrentState.iconGroup = TelephonyIcons.ROAMING;
         } else if (isDataDisabled()) {
             mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
         }
@@ -541,6 +536,7 @@
         boolean carrierNetworkChangeMode;
         boolean isDefault;
         boolean userSetup;
+        boolean roaming;
 
         @Override
         public void copyFrom(State s) {
@@ -555,6 +551,7 @@
             airplaneMode = state.airplaneMode;
             carrierNetworkChangeMode = state.carrierNetworkChangeMode;
             userSetup = state.userSetup;
+            roaming = state.roaming;
         }
 
         @Override
@@ -565,6 +562,7 @@
             builder.append("networkName=").append(networkName).append(',');
             builder.append("networkNameData=").append(networkNameData).append(',');
             builder.append("dataConnected=").append(dataConnected).append(',');
+            builder.append("roaming=").append(roaming).append(',');
             builder.append("isDefault=").append(isDefault).append(',');
             builder.append("isEmergency=").append(isEmergency).append(',');
             builder.append("airplaneMode=").append(airplaneMode).append(',');
@@ -584,7 +582,8 @@
                     && ((MobileState) o).airplaneMode == airplaneMode
                     && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
                     && ((MobileState) o).userSetup == userSetup
-                    && ((MobileState) o).isDefault == isDefault;
+                    && ((MobileState) o).isDefault == isDefault
+                    && ((MobileState) o).roaming == roaming;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 082fe82..eb47a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -19,19 +19,22 @@
 import android.content.Context;
 import android.content.Intent;
 import android.telephony.SubscriptionInfo;
+import android.view.View;
+
 import com.android.settingslib.net.DataUsageController;
 import com.android.settingslib.wifi.AccessPoint;
+import com.android.systemui.DemoMode;
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 import java.util.List;
 
-public interface NetworkController extends CallbackController<SignalCallback> {
+public interface NetworkController extends CallbackController<SignalCallback>, DemoMode {
 
     boolean hasMobileDataFeature();
     void addCallback(SignalCallback cb);
     void removeCallback(SignalCallback cb);
     void setWifiEnabled(boolean enabled);
-    void onUserSwitched(int newUserId);
     AccessPointController getAccessPointController();
     DataUsageController getMobileDataController();
     DataSaverController getDataSaverController();
@@ -40,6 +43,9 @@
 
     void addEmergencyListener(EmergencyListener listener);
     void removeEmergencyListener(EmergencyListener listener);
+    void setUserSetupComplete(boolean userSetup);
+    boolean hasEmergencyCryptKeeperText();
+    boolean isRadioOn();
 
     public interface SignalCallback {
         default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
@@ -47,7 +53,7 @@
 
         default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
                 int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
-                String description, boolean isWide, int subId) {}
+                String description, boolean isWide, int subId, boolean roaming) {}
         default void setSubs(List<SubscriptionInfo> subs) {}
         default void setNoSims(boolean show) {}
 
@@ -64,15 +70,29 @@
 
     public static class IconState {
         public final boolean visible;
+
         public final int icon;
+
+        /**
+         * Optional iconOverlay resource id.
+         *
+         * <p>Set to -1 if not present.
+         */
+        public final int iconOverlay;
+
         public final String contentDescription;
 
-        public IconState(boolean visible, int icon, String contentDescription) {
+        public IconState(boolean visible, int icon, int iconOverlay, String contentDescription) {
             this.visible = visible;
             this.icon = icon;
+            this.iconOverlay = iconOverlay;
             this.contentDescription = contentDescription;
         }
 
+        public IconState(boolean visible, int icon, String contentDescription) {
+            this(visible, icon, -1 /* iconOverlay */, contentDescription);
+        }
+
         public IconState(boolean visible, int icon, int contentDescription,
                 Context context) {
             this(visible, icon, context.getString(contentDescription));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index a7fab41..d7c919d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -20,9 +20,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
+import android.net.NetworkScoreManager;
 import android.net.wifi.WifiManager;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -42,8 +44,12 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.ConfigurationChangedReceiver;
 import com.android.systemui.DemoMode;
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -60,7 +66,8 @@
 
 /** Platform implementation of the network controller. **/
 public class NetworkControllerImpl extends BroadcastReceiver
-        implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider {
+        implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider,
+        ConfigurationChangedReceiver, Dumpable {
     // debug
     static final String TAG = "NetworkController";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -80,7 +87,9 @@
     private final boolean mHasMobileDataFeature;
     private final SubscriptionDefaults mSubDefaults;
     private final DataSaverController mDataSaverController;
+    private final CurrentUserTracker mUserTracker;
     private Config mConfig;
+    private final NetworkScoreManager mNetworkScoreManager;
 
     // Subcontrollers.
     @VisibleForTesting
@@ -135,26 +144,36 @@
     /**
      * Construct this controller object and register for updates.
      */
-    public NetworkControllerImpl(Context context, Looper bgLooper) {
+    public NetworkControllerImpl(Context context, Looper bgLooper,
+            DeviceProvisionedController deviceProvisionedController) {
         this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
+                context.getSystemService(NetworkScoreManager.class),
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
                 (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
-                SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
+                SubscriptionManager.from(context),
+                Config.readConfig(context),
+                bgLooper,
                 new CallbackHandler(),
                 new AccessPointControllerImpl(context, bgLooper),
                 new DataUsageController(context),
-                new SubscriptionDefaults());
+                new SubscriptionDefaults(),
+                deviceProvisionedController);
         mReceiverHandler.post(mRegisterListeners);
     }
 
     @VisibleForTesting
     NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
-            TelephonyManager telephonyManager, WifiManager wifiManager,
-            SubscriptionManager subManager, Config config, Looper bgLooper,
+            NetworkScoreManager networkScoreManager,
+            TelephonyManager telephonyManager,
+            WifiManager wifiManager,
+            SubscriptionManager subManager,
+            Config config,
+            Looper bgLooper,
             CallbackHandler callbackHandler,
             AccessPointControllerImpl accessPointController,
             DataUsageController dataUsageController,
-            SubscriptionDefaults defaultsHandler) {
+            SubscriptionDefaults defaultsHandler,
+            DeviceProvisionedController deviceProvisionedController) {
         mContext = context;
         mConfig = config;
         mReceiverHandler = new Handler(bgLooper);
@@ -172,6 +191,7 @@
 
         // wifi
         mWifiManager = wifiManager;
+        mNetworkScoreManager = networkScoreManager;
 
         mLocale = mContext.getResources().getConfiguration().locale;
         mAccessPoints = accessPointController;
@@ -185,12 +205,26 @@
             }
         });
         mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
-                mCallbackHandler, this);
+                mCallbackHandler, this, mNetworkScoreManager);
 
         mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);
 
         // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
         updateAirplaneMode(true /* force callback */);
+        mUserTracker = new CurrentUserTracker(mContext) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                NetworkControllerImpl.this.onUserSwitched(newUserId);
+            }
+        };
+        mUserTracker.startTracking();
+        deviceProvisionedController.addCallback(new DeviceProvisionedListener() {
+            @Override
+            public void onUserSetupChanged() {
+                setUserSetupComplete(deviceProvisionedController.isUserSetup(
+                        deviceProvisionedController.getCurrentUser()));
+            }
+        });
     }
 
     public DataSaverController getDataSaverController() {
@@ -358,8 +392,7 @@
         }.execute();
     }
 
-    @Override
-    public void onUserSwitched(int newUserId) {
+    private void onUserSwitched(int newUserId) {
         mCurrentUserId = newUserId;
         mAccessPoints.onUserSwitched(newUserId);
         updateConnectivity();
@@ -413,7 +446,7 @@
         }
     }
 
-    public void onConfigurationChanged() {
+    public void onConfigurationChanged(Configuration newConfig) {
         mConfig = Config.readConfig(mContext);
         mReceiverHandler.post(new Runnable() {
             @Override
@@ -776,6 +809,8 @@
                 MobileSignalController controller = mMobileSignalControllers
                         .values().toArray(new MobileSignalController[0])[slot];
                 controller.getState().dataSim = datatype != null;
+                controller.getState().isDefault = datatype != null;
+                controller.getState().dataConnected = datatype != null;
                 if (datatype != null) {
                     controller.getState().iconGroup =
                             datatype.equals("1x") ? TelephonyIcons.ONE_X :
@@ -787,9 +822,12 @@
                             datatype.equals("h") ? TelephonyIcons.H :
                             datatype.equals("lte") ? TelephonyIcons.LTE :
                             datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS :
-                            datatype.equals("roam") ? TelephonyIcons.ROAMING :
+                            datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED :
                             TelephonyIcons.UNKNOWN;
                 }
+                if (args.containsKey("roam")) {
+                    controller.getState().roaming = "show".equals(args.getString("roam"));
+                }
                 int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
                 String level = args.getString("level");
                 if (level != null) {
@@ -797,6 +835,10 @@
                             : Math.min(Integer.parseInt(level), icons[0].length - 1);
                     controller.getState().connected = controller.getState().level >= 0;
                 }
+                String activity = args.getString("activity");
+                if (activity != null) {
+                    controller.setActivity(Integer.parseInt(activity));
+                }
                 controller.getState().enabled = show;
                 controller.notifyListeners();
             }
@@ -813,9 +855,11 @@
     private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
                 null, 0, 0, "");
-        mMobileSignalControllers.put(id, new MobileSignalController(mContext,
+        MobileSignalController controller = new MobileSignalController(mContext,
                 mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info,
-                mSubDefaults, mReceiverHandler.getLooper()));
+                mSubDefaults, mReceiverHandler.getLooper());
+        mMobileSignalControllers.put(id, controller);
+        controller.getState().userSetup = true;
         return info;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
index e5b0c03..366a752 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmController.java
@@ -16,9 +16,10 @@
 
 import android.app.AlarmManager;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 
-public interface NextAlarmController extends CallbackController<NextAlarmChangeCallback> {
+public interface NextAlarmController extends CallbackController<NextAlarmChangeCallback>, Dumpable {
 
     public interface NextAlarmChangeCallback {
         void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 44ec283..784f25e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -56,6 +56,7 @@
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.stack.ScrollContainer;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
@@ -90,6 +91,7 @@
     private int mRevealR;
 
     private boolean mResetting;
+    private NotificationViewWrapper mWrapper;
 
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -210,11 +212,17 @@
                     @Override
                     public void onAnimationEnd(Animator animation) {
                         setVisibility(INVISIBLE);
+                        if (mWrapper != null) {
+                            mWrapper.setRemoteInputVisible(false);
+                        }
                     }
                 });
                 reveal.start();
             } else {
                 setVisibility(INVISIBLE);
+                if (mWrapper != null) {
+                    mWrapper.setRemoteInputVisible(false);
+                }
             }
         }
         MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
@@ -267,6 +275,9 @@
                 mEntry.notification.getPackageName());
 
         setVisibility(VISIBLE);
+        if (mWrapper != null) {
+            mWrapper.setRemoteInputVisible(true);
+        }
         mController.addRemoteInput(mEntry, mToken);
         mEditText.setInnerFocusable(true);
         mEditText.mShowImeOnInputConnection = true;
@@ -283,6 +294,10 @@
             // Update came in after we sent the reply, time to reset.
             reset();
         }
+
+        if (isActive() && mWrapper != null) {
+            mWrapper.setRemoteInputVisible(true);
+        }
     }
 
     private void reset() {
@@ -452,6 +467,10 @@
         super.dispatchFinishTemporaryDetach();
     }
 
+    public void setWrapper(NotificationViewWrapper wrapper) {
+        mWrapper = wrapper;
+    }
+
     /**
      * An EditText that changes appearance based on whether it's focusable and becomes
      * un-focusable whenever the user navigates away from it or it becomes invisible.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 3142228..3f8e41a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -15,9 +15,11 @@
  */
 package com.android.systemui.statusbar.policy;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
 
-public interface SecurityController extends CallbackController<SecurityControllerCallback> {
+public interface SecurityController extends CallbackController<SecurityControllerCallback>,
+        Dumpable {
     /** Whether the device has device owner, even if not on this user. */
     boolean isDeviceManaged();
     boolean hasProfileOwner();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index df959bd..19ced23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -39,12 +39,13 @@
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
 import com.android.systemui.R;
+import com.android.systemui.settings.CurrentUserTracker;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
-public class SecurityControllerImpl implements SecurityController {
+public class SecurityControllerImpl extends CurrentUserTracker implements SecurityController {
 
     private static final String TAG = "SecurityController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -73,6 +74,7 @@
     private int mVpnUserId;
 
     public SecurityControllerImpl(Context context) {
+        super(context);
         mContext = context;
         mDevicePolicyManager = (DevicePolicyManager)
                 context.getSystemService(Context.DEVICE_POLICY_SERVICE);
@@ -87,6 +89,7 @@
         // TODO: re-register network callback on user change.
         mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
         onUserSwitched(ActivityManager.getCurrentUser());
+        startTracking();
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java
deleted file mode 100644
index dce889f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.policy;
-
-import android.telephony.SubscriptionInfo;
-
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-
-import java.util.List;
-
-
-/**
- * Provides empty implementations of SignalCallback for those that only want some of
- * the callbacks.
- */
-public class SignalCallbackAdapter implements SignalCallback {
-
-    @Override
-    public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-            boolean activityIn, boolean activityOut, String description) {
-    }
-
-    @Override
-    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-            int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
-            String description, boolean isWide, int subId) {
-    }
-
-    @Override
-    public void setSubs(List<SubscriptionInfo> subs) {
-    }
-
-    @Override
-    public void setNoSims(boolean show) {
-    }
-
-    @Override
-    public void setEthernetIndicators(IconState icon) {
-    }
-
-    @Override
-    public void setIsAirplaneMode(IconState icon) {
-    }
-
-    @Override
-    public void setMobileDataEnabled(boolean enabled) {
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index ed8c7ff..6b2361e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -95,8 +95,6 @@
           R.drawable.ic_qs_signal_carrier_network_change_animation }
     };
 
-    static final int QS_DATA_R = R.drawable.ic_qs_signal_r;
-
     //***** Data connection icons
 
     //GSM/UMTS
@@ -211,7 +209,7 @@
     static final int QS_DATA_LTE_PLUS = R.drawable.ic_qs_signal_lte_plus;
 
     static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode;
-    static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam;
+    static final int ROAMING_ICON = R.drawable.stat_sys_roaming;
     static final int ICON_LTE = R.drawable.stat_sys_data_fully_connected_lte;
     static final int ICON_LTE_PLUS = R.drawable.stat_sys_data_fully_connected_lte_plus;
     static final int ICON_G = R.drawable.stat_sys_data_fully_connected_g;
@@ -410,21 +408,6 @@
             TelephonyIcons.QS_DATA_LTE_PLUS
             );
 
-    static final MobileIconGroup ROAMING = new MobileIconGroup(
-            "Roaming",
-            TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING,
-            TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH,
-            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
-            TelephonyIcons.TELEPHONY_NO_NETWORK,
-            TelephonyIcons.QS_TELEPHONY_NO_NETWORK,
-            AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
-            R.string.accessibility_data_connection_roaming,
-            TelephonyIcons.ROAMING_ICON,
-            false,
-            TelephonyIcons.QS_DATA_R
-            );
-
     static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
             "DataDisabled",
             TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 4785ba9..fd71f43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -52,14 +52,16 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Dependency;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUISecondaryUserService;
 import com.android.systemui.plugins.qs.QS.DetailAdapter;
 import com.android.systemui.qs.tiles.UserDetailView;
-import com.android.systemui.plugins.qs.QS.ActivityStarter;
+import com.android.systemui.ActivityStarter;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.NotificationChannels;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -560,7 +562,7 @@
                     0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM);
             Notification.Builder builder = new Notification.Builder(mContext)
                     .setVisibility(Notification.VISIBILITY_SECRET)
-                    .setPriority(Notification.PRIORITY_MIN)
+                    .setChannel(NotificationChannels.USER)
                     .setSmallIcon(R.drawable.ic_person)
                     .setContentTitle(mContext.getString(R.string.user_logout_notification_title))
                     .setContentText(mContext.getString(R.string.user_logout_notification_text))
@@ -584,7 +586,7 @@
 
         Notification.Builder builder = new Notification.Builder(mContext)
                 .setVisibility(Notification.VISIBILITY_SECRET)
-                .setPriority(Notification.PRIORITY_MIN)
+                .setChannel(NotificationChannels.USER)
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle(mContext.getString(R.string.guest_notification_title))
                 .setContentText(mContext.getString(R.string.guest_notification_text))
@@ -645,11 +647,6 @@
     }
 
     @VisibleForTesting
-    public KeyguardMonitor getKeyguardMonitor() {
-        return mKeyguardMonitor;
-    }
-
-    @VisibleForTesting
     public ArrayList<UserRecord> getUsers() {
         return mUsers;
     }
@@ -657,17 +654,19 @@
     public static abstract class BaseUserAdapter extends BaseAdapter {
 
         final UserSwitcherController mController;
+        private final KeyguardMonitor mKeyguardMonitor;
 
         protected BaseUserAdapter(UserSwitcherController controller) {
             mController = controller;
+            mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
             controller.addAdapter(new WeakReference<>(this));
         }
 
         @Override
         public int getCount() {
-            boolean secureKeyguardShowing = mController.getKeyguardMonitor().isShowing()
-                    && mController.getKeyguardMonitor().isSecure()
-                    && !mController.getKeyguardMonitor().canSkipBouncer();
+            boolean secureKeyguardShowing = mKeyguardMonitor.isShowing()
+                    && mKeyguardMonitor.isSecure()
+                    && !mKeyguardMonitor.canSkipBouncer();
             if (!secureKeyguardShowing) {
                 return mController.getUsers().size();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index b1bc2f0..42c20ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -15,18 +15,27 @@
  */
 package com.android.systemui.statusbar.policy;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.database.ContentObserver;
 import android.net.NetworkCapabilities;
+import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
+import android.net.ScoredNetwork;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.WifiNetworkScoreCache.CacheListener;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.provider.Settings;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.AsyncChannel;
+import com.android.settingslib.Utils;
 import com.android.settingslib.wifi.WifiStatusTracker;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
@@ -34,17 +43,24 @@
 import com.android.systemui.R;
 
 import java.util.Objects;
+import java.util.List;
 
 
 public class WifiSignalController extends
         SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
+
     private final WifiManager mWifiManager;
     private final AsyncChannel mWifiChannel;
     private final boolean mHasMobileData;
+    private final NetworkScoreManager mNetworkScoreManager;
+    private final WifiNetworkScoreCache mScoreCache;
     private final WifiStatusTracker mWifiTracker;
 
+    private boolean mScoringEnabled = false;
+
     public WifiSignalController(Context context, boolean hasMobileData,
-            CallbackHandler callbackHandler, NetworkControllerImpl networkController) {
+            CallbackHandler callbackHandler, NetworkControllerImpl networkController,
+            NetworkScoreManager networkScoreManager) {
         super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
                 callbackHandler, networkController);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
@@ -68,6 +84,44 @@
                 WifiIcons.QS_WIFI_NO_NETWORK,
                 AccessibilityContentDescriptions.WIFI_NO_CONNECTION
                 );
+
+        mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(handler) {
+            @Override
+            public void networkCacheUpdated(List<ScoredNetwork> networks) {
+                mCurrentState.badgeEnum = getWifiBadgeEnum();
+                notifyListenersIfNecessary();
+            }
+        });
+
+        // Setup scoring
+        mNetworkScoreManager = networkScoreManager;
+        ContentObserver observer = new ContentObserver(new Handler(Looper.getMainLooper())) {
+            @Override
+            public void onChange(boolean selfChange) {
+                mScoringEnabled =
+                        Settings.Global.getInt(
+                                mContext.getContentResolver(),
+                                Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1;
+                if (!mScoringEnabled) {
+                    mScoreCache.clearScores();
+                }
+            }
+        };
+        ContentResolver cr = mContext.getContentResolver();
+        cr.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED),
+                false /* notifyForDescendants */,
+                observer);
+        observer.onChange(false /* selfChange */);
+        registerScoreCache();
+    }
+
+    private void registerScoreCache() {
+        Log.d(mTag, "Registered score cache");
+        mNetworkScoreManager.registerNetworkScoreCache(
+                NetworkKey.TYPE_WIFI,
+                mScoreCache,
+                NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK);
     }
 
     @Override
@@ -88,27 +142,74 @@
                     ("," + mContext.getString(R.string.accessibility_quick_settings_no_internet));
         }
 
-        IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
-        IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(),
-                contentDescription);
+        IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(),
+                Utils.getWifiBadgeResource(mCurrentState.badgeEnum), contentDescription);
+        IconState qsIcon = new IconState(
+                mCurrentState.connected, getQsCurrentIconId(),
+                Utils.getWifiBadgeResource(mCurrentState.badgeEnum), contentDescription);
         callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
                 ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
                 wifiDesc);
     }
 
+    @Override
+    public int getCurrentIconId() {
+        if (mCurrentState.badgeEnum != ScoredNetwork.BADGING_NONE) {
+            return Utils.WIFI_PIE_FOR_BADGING[mCurrentState.level];
+        }
+        return super.getCurrentIconId();
+    }
+
     /**
      * Extract wifi state directly from broadcasts about changes in wifi state.
      */
     public void handleBroadcast(Intent intent) {
+        // Update the WifiStatusTracker with the new information and update the score cache.
+        NetworkKey previousNetworkKey = mWifiTracker.networkKey;
         mWifiTracker.handleBroadcast(intent);
+        updateScoreCacheIfNecessary(previousNetworkKey);
+
         mCurrentState.enabled = mWifiTracker.enabled;
         mCurrentState.connected = mWifiTracker.connected;
         mCurrentState.ssid = mWifiTracker.ssid;
         mCurrentState.rssi = mWifiTracker.rssi;
         mCurrentState.level = mWifiTracker.level;
+        mCurrentState.badgeEnum = getWifiBadgeEnum();
         notifyListenersIfNecessary();
     }
 
+    /**
+     * Clears old scores out of the cache and requests new scores if the network key has changed.
+     *
+     * <p>New scores are requested asynchronously.
+     */
+    private void updateScoreCacheIfNecessary(NetworkKey previousNetworkKey) {
+        if (mWifiTracker.networkKey == null) {
+            return;
+        }
+        if ((previousNetworkKey == null) || !mWifiTracker.networkKey.equals(previousNetworkKey)) {
+            mScoreCache.clearScores();
+            mNetworkScoreManager.requestScores(new NetworkKey[]{mWifiTracker.networkKey});
+        }
+    }
+
+    /**
+     * Returns the wifi badge enum for the current {@link #mWifiTracker} state.
+     *
+     * <p>{@link #updateScoreCacheIfNecessary} should be called prior to this method.
+     */
+    private int getWifiBadgeEnum() {
+        if (!mScoringEnabled || mWifiTracker.networkKey == null) {
+            return ScoredNetwork.BADGING_NONE;
+        }
+        ScoredNetwork score = mScoreCache.getScoredNetwork(mWifiTracker.networkKey);
+
+        if (score != null) {
+            return score.calculateBadge(mWifiTracker.rssi);
+        }
+        return ScoredNetwork.BADGING_NONE;
+    }
+
     @VisibleForTesting
     void setActivity(int wifiActivity) {
         mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
@@ -149,12 +250,14 @@
 
     static class WifiState extends SignalController.State {
         String ssid;
+        int badgeEnum;
 
         @Override
         public void copyFrom(State s) {
             super.copyFrom(s);
             WifiState state = (WifiState) s;
             ssid = state.ssid;
+            badgeEnum = state.badgeEnum;
         }
 
         @Override
@@ -166,7 +269,8 @@
         @Override
         public boolean equals(Object o) {
             return super.equals(o)
-                    && Objects.equals(((WifiState) o).ssid, ssid);
+                    && Objects.equals(((WifiState) o).ssid, ssid)
+                    && (((WifiState) o).badgeEnum == badgeEnum);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index bcdb62d..f195f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -30,7 +30,6 @@
     ZenRule getManualRule();
     ZenModeConfig getConfig();
     long getNextAlarm();
-    void setUserId(int userId);
     boolean isZenAvailable();
     ComponentName getEffectsSuppressor();
     boolean isCountdownConditionSupported();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 96efea1..e80d3b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -40,13 +40,14 @@
 import android.util.Slog;
 
 import com.android.systemui.qs.GlobalSetting;
+import com.android.systemui.settings.CurrentUserTracker;
 
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.Objects;
 
 /** Platform implementation of the zen mode controller. **/
-public class ZenModeControllerImpl implements ZenModeController {
+public class ZenModeControllerImpl extends CurrentUserTracker implements ZenModeController {
     private static final String TAG = "ZenModeController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -66,6 +67,7 @@
     private ZenModeConfig mConfig;
 
     public ZenModeControllerImpl(Context context, Handler handler) {
+        super(context);
         mContext = context;
         mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
             @Override
@@ -87,6 +89,7 @@
         mSetupObserver = new SetupObserver(handler);
         mSetupObserver.register();
         mUserManager = context.getSystemService(UserManager.class);
+        startTracking();
     }
 
     @Override
@@ -137,7 +140,7 @@
     }
 
     @Override
-    public void setUserId(int userId) {
+    public void onUserSwitched(int userId) {
         mUserId = userId;
         if (mRegistered) {
             mContext.unregisterReceiver(mReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 1a2d778..e6a3add 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -78,6 +78,7 @@
     private NotificationHeaderUtil mHeaderUtil;
     private ViewState mHeaderViewState;
     private int mClipBottomAmount;
+    private boolean mIsLowPriority;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -246,10 +247,13 @@
         return mChildren.size();
     }
 
-    public void recreateNotificationHeader(OnClickListener listener, StatusBarNotification notification) {
+    public void recreateNotificationHeader(OnClickListener listener,
+            StatusBarNotification notification) {
         final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
                 mNotificationParent.getStatusBarNotification().getNotification());
-        final RemoteViews header = builder.makeNotificationHeader();
+        final RemoteViews header = mIsLowPriority
+                ? builder.makeLowPriorityContentView(true /* useRegularSubtext */)
+                : builder.makeNotificationHeader();
         if (mNotificationHeader == null) {
             mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
             final View expandButton = mNotificationHeader.findViewById(
@@ -262,7 +266,7 @@
             invalidate();
         } else {
             header.reapply(getContext(), mNotificationHeader);
-            mNotificationHeaderWrapper.notifyContentUpdated(notification);
+            mNotificationHeaderWrapper.notifyContentUpdated(notification, mIsLowPriority);
         }
         updateChildrenHeaderAppearance();
     }
@@ -367,6 +371,9 @@
      * @return the intrinsic size of this children container, i.e the natural fully expanded state
      */
     public int getIntrinsicHeight() {
+        if (mIsLowPriority && !mChildrenExpanded) {
+            return mNotificationHeader.getHeight();
+        }
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
         return getIntrinsicHeight(maxAllowedVisibleChildren);
     }
@@ -480,7 +487,7 @@
             childState.clipTopAmount = 0;
             childState.alpha = 0;
             if (i < firstOverflowIndex) {
-                childState.alpha = 1;
+                childState.alpha = mIsLowPriority && !mChildrenExpanded ? expandFactor : 1.0f;
             } else if (expandFactor == 1.0f && i <= lastVisibleIndex) {
                 childState.alpha = (mActualHeight - childState.yTranslation) / childState.height;
                 childState.alpha = Math.max(0.0f, Math.min(1.0f, childState.alpha));
@@ -828,6 +835,9 @@
     }
 
     private int getMinHeight(int maxAllowedVisibleChildren) {
+        if (mIsLowPriority && !mChildrenExpanded) {
+            return mNotificationHeader.getHeight();
+        }
         int minExpandHeight = mNotificationHeaderMargin;
         int visibleChildren = 0;
         boolean firstChild = true;
@@ -922,4 +932,8 @@
         mClipBottomAmount = clipBottomAmount;
         updateChildrenClipping();
     }
+
+    public void setIsLowPriority(boolean isLowPriority) {
+        mIsLowPriority = isLowPriority;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 06cd769..5d48df9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -63,14 +63,15 @@
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.DismissView;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.NotificationGuts;
-import com.android.systemui.statusbar.NotificationSettingsIconRow;
-import com.android.systemui.statusbar.NotificationSettingsIconRow.SettingsIconRowListener;
+import com.android.systemui.statusbar.NotificationMenuRow;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StackScrollerDecorView;
 import com.android.systemui.statusbar.StatusBarState;
@@ -94,7 +95,8 @@
 public class NotificationStackScrollLayout extends ViewGroup
         implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
         ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
-        SettingsIconRowListener, ScrollContainer, VisibilityLocationProvider {
+        NotificationMenuRowProvider.OnMenuClickListener, ScrollContainer,
+        VisibilityLocationProvider {
 
     public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
     private static final String TAG = "StackScroller";
@@ -224,7 +226,7 @@
     private int mMaxScrollAfterExpand;
     private SwipeHelper.LongPressListener mLongPressListener;
 
-    private NotificationSettingsIconRow mCurrIconRow;
+    private NotificationMenuRow mCurrIconRow;
     private View mTranslatingParentView;
     private View mGearExposedView;
 
@@ -359,6 +361,7 @@
     private boolean mInHeadsUpPinnedMode;
     private boolean mHeadsUpAnimatingAway;
     private int mStatusBarState;
+    private int mCachedBackgroundColor;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -398,16 +401,20 @@
     }
 
     @Override
-    public void onGearTouched(ExpandableNotificationRow row, int x, int y) {
-        if (mLongPressListener != null) {
+    public void onMenuClicked(View view, int x, int y, MenuItem item) {
+        if (mLongPressListener == null) {
+            return;
+        }
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
             MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR,
                     row.getStatusBarNotification().getPackageName());
-            mLongPressListener.onLongPress(row, x, y);
         }
+        mLongPressListener.onLongPress(view, x, y, item);
     }
 
     @Override
-    public void onSettingsIconRowReset(ExpandableNotificationRow row) {
+    public void onMenuReset(View row) {
         if (mTranslatingParentView != null && row == mTranslatingParentView) {
             mSwipeHelper.setSnappedToGear(false);
             mGearExposedView = null;
@@ -424,7 +431,7 @@
         if (DEBUG) {
             int y = mTopPadding;
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-            y = (int) getLayoutHeight();
+            y = getLayoutHeight();
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
             y = getHeight() - getEmptyBottomMargin();
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -445,8 +452,11 @@
                         + alphaInv * Color.green(scrimColor)),
                 (int) (mBackgroundFadeAmount * Color.blue(mBgColor)
                         + alphaInv * Color.blue(scrimColor)));
-        mBackgroundPaint.setColor(color);
-        invalidate();
+        if (mCachedBackgroundColor != color) {
+            mCachedBackgroundColor = color;
+            mBackgroundPaint.setColor(color);
+            invalidate();
+        }
     }
 
     private void initView(Context context) {
@@ -602,9 +612,9 @@
             ExpandableView child = (ExpandableView) getChildAt(i);
             if (mChildrenToAddAnimated.contains(child)) {
                 int startingPosition = getPositionInLinearLayout(child);
-                int padding = child.getIncreasedPaddingAmount() == 1.0f
-                        ? mIncreasedPaddingBetweenElements :
-                        mPaddingBetweenElements;
+                float increasedPaddingAmount = child.getIncreasedPaddingAmount();
+                int padding = increasedPaddingAmount == 1.0f ? mIncreasedPaddingBetweenElements
+                        : increasedPaddingAmount == -1.0f ? 0 : mPaddingBetweenElements;
                 int childHeight = getIntrinsicHeight(child) + padding;
                 if (startingPosition < mOwnScrollY) {
                     // This child starts off screen, so let's keep it offscreen to keep the others visible
@@ -907,7 +917,7 @@
             mDragAnimPendingChildren.remove(animView);
         }
         if (mCurrIconRow != null && targetLeft == 0) {
-            mCurrIconRow.resetState();
+            mCurrIconRow.resetState(true /* notify */);
             mCurrIconRow = null;
         }
     }
@@ -1876,26 +1886,53 @@
 
     private void updateContentHeight() {
         int height = 0;
-        float previousIncreasedAmount = 0.0f;
+        float previousPaddingRequest = mPaddingBetweenElements;
+        float previousPaddingAmount = 0.0f;
         int numShownItems = 0;
         boolean finish = false;
+        int maxDisplayedNotifications = mAmbientState.isDark()
+                ? (mPulsing ? 1 : 0)
+                : mMaxDisplayedNotifications;
+
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView expandableView = (ExpandableView) getChildAt(i);
             if (expandableView.getVisibility() != View.GONE
                     && !expandableView.hasNoContentHeight()) {
-                if (mMaxDisplayedNotifications != -1
-                        && numShownItems >= mMaxDisplayedNotifications) {
+                if (maxDisplayedNotifications != -1
+                        && numShownItems >= maxDisplayedNotifications) {
                     expandableView = mShelf;
                     finish = true;
                 }
                 float increasedPaddingAmount = expandableView.getIncreasedPaddingAmount();
-                if (height != 0) {
-                    height += (int) NotificationUtils.interpolate(
+                float padding;
+                if (increasedPaddingAmount >= 0.0f) {
+                    padding = (int) NotificationUtils.interpolate(
+                            previousPaddingRequest,
+                            mIncreasedPaddingBetweenElements,
+                            increasedPaddingAmount);
+                    previousPaddingRequest = (int) NotificationUtils.interpolate(
                             mPaddingBetweenElements,
                             mIncreasedPaddingBetweenElements,
-                            Math.max(previousIncreasedAmount, increasedPaddingAmount));
+                            increasedPaddingAmount);
+                } else {
+                    int ownPadding = (int) NotificationUtils.interpolate(
+                            0,
+                            mPaddingBetweenElements,
+                            1.0f + increasedPaddingAmount);
+                    if (previousPaddingAmount > 0.0f) {
+                        padding = (int) NotificationUtils.interpolate(
+                                ownPadding,
+                                mIncreasedPaddingBetweenElements,
+                                previousPaddingAmount);
+                    } else {
+                        padding = ownPadding;
+                    }
+                    previousPaddingRequest = ownPadding;
                 }
-                previousIncreasedAmount = increasedPaddingAmount;
+                if (height != 0) {
+                    height += padding;
+                }
+                previousPaddingAmount = increasedPaddingAmount;
                 height += expandableView.getIntrinsicHeight();
                 numShownItems++;
                 if (finish) {
@@ -2092,9 +2129,14 @@
      * Update the background bounds to the new desired bounds
      */
     private void updateBackgroundBounds() {
-        getLocationInWindow(mTempInt2);
-        mBackgroundBounds.left = mTempInt2[0];
-        mBackgroundBounds.right = mTempInt2[0] + getWidth();
+        if (mAmbientState.isPanelFullWidth()) {
+            mBackgroundBounds.left = 0;
+            mBackgroundBounds.right = getWidth();
+        } else {
+            getLocationInWindow(mTempInt2);
+            mBackgroundBounds.left = mTempInt2[0];
+            mBackgroundBounds.right = mTempInt2[0] + getWidth();
+        }
         if (!mIsExpanded) {
             mBackgroundBounds.top = 0;
             mBackgroundBounds.bottom = 0;
@@ -2553,10 +2595,19 @@
      */
     private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
         int startingPosition = getPositionInLinearLayout(removedChild);
-        int padding = (int) NotificationUtils.interpolate(
-                mPaddingBetweenElements,
-                mIncreasedPaddingBetweenElements,
-                removedChild.getIncreasedPaddingAmount());
+        float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
+        int padding;
+        if (increasedPaddingAmount >= 0) {
+            padding = (int) NotificationUtils.interpolate(
+                    mPaddingBetweenElements,
+                    mIncreasedPaddingBetweenElements,
+                    increasedPaddingAmount);
+        } else {
+            padding = (int) NotificationUtils.interpolate(
+                    0,
+                    mPaddingBetweenElements,
+                    1.0f + increasedPaddingAmount);
+        }
         int childHeight = getIntrinsicHeight(removedChild) + padding;
         int endPosition = startingPosition + childHeight;
         if (endPosition <= mOwnScrollY) {
@@ -2588,19 +2639,42 @@
             requestedView = requestedRow = childInGroup.getNotificationParent();
         }
         int position = 0;
-        float previousIncreasedAmount = 0.0f;
+        float previousPaddingRequest = mPaddingBetweenElements;
+        float previousPaddingAmount = 0.0f;
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView child = (ExpandableView) getChildAt(i);
             boolean notGone = child.getVisibility() != View.GONE;
             if (notGone && !child.hasNoContentHeight()) {
                 float increasedPaddingAmount = child.getIncreasedPaddingAmount();
-                if (position != 0) {
-                    position += (int) NotificationUtils.interpolate(
+                float padding;
+                if (increasedPaddingAmount >= 0.0f) {
+                    padding = (int) NotificationUtils.interpolate(
+                            previousPaddingRequest,
+                            mIncreasedPaddingBetweenElements,
+                            increasedPaddingAmount);
+                    previousPaddingRequest = (int) NotificationUtils.interpolate(
                             mPaddingBetweenElements,
                             mIncreasedPaddingBetweenElements,
-                            Math.max(previousIncreasedAmount, increasedPaddingAmount));
+                            increasedPaddingAmount);
+                } else {
+                    int ownPadding = (int) NotificationUtils.interpolate(
+                            0,
+                            mPaddingBetweenElements,
+                            1.0f + increasedPaddingAmount);
+                    if (previousPaddingAmount > 0.0f) {
+                        padding = (int) NotificationUtils.interpolate(
+                                ownPadding,
+                                mIncreasedPaddingBetweenElements,
+                                previousPaddingAmount);
+                    } else {
+                        padding = ownPadding;
+                    }
+                    previousPaddingRequest = ownPadding;
                 }
-                previousIncreasedAmount = increasedPaddingAmount;
+                if (position != 0) {
+                    position += padding;
+                }
+                previousPaddingAmount = increasedPaddingAmount;
             }
             if (child == requestedView) {
                 if (requestedRow != null) {
@@ -3477,6 +3551,8 @@
             updateBackground();
             setWillNotDraw(false);
         }
+        updateContentHeight();
+        notifyHeightChangeListener(mShelf);
     }
 
     private void setBackgroundFadeAmount(float fadeAmount) {
@@ -3912,6 +3988,8 @@
     public void setPulsing(boolean pulsing) {
         mPulsing = pulsing;
         updateNotificationAnimationStates();
+        updateContentHeight();
+        notifyHeightChangeListener(mShelf);
     }
 
     public void setFadingOut(boolean fadingOut) {
@@ -4104,7 +4182,7 @@
             if (currView instanceof ExpandableNotificationRow) {
                 // Set the listener for the current row's gear
                 mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow();
-                mCurrIconRow.setGearListener(NotificationStackScrollLayout.this);
+                mCurrIconRow.setMenuClickListener(NotificationStackScrollLayout.this);
             }
         }
 
@@ -4116,9 +4194,9 @@
                 mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
 
                 // If the gear is visible and the movement is towards it it's not a location change.
-                boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isIconOnLeft();
+                boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isMenuOnLeft();
                 boolean locationChange = isTowardsGear(translation, onLeft)
-                        ? false : mCurrIconRow.isIconLocationChange(translation);
+                        ? false : mCurrIconRow.isMenuLocationChange(translation);
                 if (locationChange) {
                     // Don't consider it "snapped" if location has changed.
                     setSnappedToGear(false);
@@ -4129,8 +4207,8 @@
                         mCheckForDrag = null;
                     } else {
                         // Check scheduled, reset alpha and update location; check will fade it in
-                        mCurrIconRow.setGearAlpha(0f);
-                        mCurrIconRow.setIconLocation(translation > 0 /* onLeft */);
+                        mCurrIconRow.setMenuAlpha(0f);
+                        mCurrIconRow.setMenuLocation((int) translation);
                     }
                 }
             }
@@ -4181,14 +4259,14 @@
                 return false; // Let SwipeHelper handle it.
             }
 
-            boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isIconOnLeft());
+            boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isMenuOnLeft());
             boolean gestureFastEnough = Math.abs(velocity) > getEscapeVelocity();
             final double timeForGesture = ev.getEventTime() - ev.getDownTime();
             final boolean showGearForSlowOnGoing = !canChildBeDismissed(animView)
                 && timeForGesture >= SWIPE_GEAR_TIMING;
 
             if (mGearSnappedTo && mCurrIconRow.isVisible()) {
-                if (mGearSnappedOnLeft == mCurrIconRow.isIconOnLeft()) {
+                if (mGearSnappedOnLeft == mCurrIconRow.isMenuOnLeft()) {
                     boolean coveringGear =
                             Math.abs(getTranslation(animView)) <= getSpaceForGear(animView) * 0.6f;
                     if (gestureTowardsGear || coveringGear) {
@@ -4232,7 +4310,7 @@
 
         private void snapToGear(View animView, float velocity) {
             final float snapBackThreshold = getSpaceForGear(animView);
-            final float target = mCurrIconRow.isIconOnLeft() ? snapBackThreshold
+            final float target = mCurrIconRow.isMenuOnLeft() ? snapBackThreshold
                     : -snapBackThreshold;
             mGearExposedView = mTranslatingParentView;
             if (animView instanceof ExpandableNotificationRow) {
@@ -4263,7 +4341,7 @@
             final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
             final float snapBackThreshold = getSpaceForGear(animView) * multiplier;
             final float translation = getTranslation(animView);
-            return !swipedFarEnough() && mCurrIconRow.isVisible() && (mCurrIconRow.isIconOnLeft()
+            return !swipedFarEnough() && mCurrIconRow.isVisible() && (mCurrIconRow.isMenuOnLeft()
                     ? translation > snapBackThreshold
                     : translation < -snapBackThreshold);
         }
@@ -4332,7 +4410,7 @@
          * Indicates the the gear has been snapped to.
          */
         private void setSnappedToGear(boolean snapped) {
-            mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isIconOnLeft() : false;
+            mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isMenuOnLeft() : false;
             mGearSnappedTo = snapped && mCurrIconRow != null;
         }
 
@@ -4372,11 +4450,11 @@
                 final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView);
                 final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
                 if ((mCurrIconRow != null && (!mCurrIconRow.isVisible()
-                        || mCurrIconRow.isIconLocationChange(translation)))
+                        || mCurrIconRow.isMenuLocationChange(translation)))
                         && absTransX >= bounceBackToGearWidth * 0.4
                         && absTransX < notiThreshold) {
                     // Fade in the gear
-                    mCurrIconRow.fadeInSettings(translation > 0 /* fromLeft */, translation,
+                    mCurrIconRow.fadeInMenu(translation > 0 /* fromLeft */, translation,
                             notiThreshold);
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 7ff1000..ba91ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -27,7 +27,6 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.StackScrollerDecorView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 
 import java.util.ArrayList;
@@ -244,7 +243,7 @@
         int childCount = hostView.getChildCount();
         state.visibleChildren.clear();
         state.visibleChildren.ensureCapacity(childCount);
-        state.increasedPaddingMap.clear();
+        state.paddingMap.clear();
         int notGoneIndex = 0;
         ExpandableView lastView = null;
         for (int i = 0; i < childCount; i++) {
@@ -254,16 +253,31 @@
                     continue;
                 }
                 notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
-                float increasedPadding = v.getIncreasedPaddingAmount();
+                float increasedPadding = v.getIncreasedPaddingAmount();;
                 if (increasedPadding != 0.0f) {
-                    state.increasedPaddingMap.put(v, increasedPadding);
+                    state.paddingMap.put(v, increasedPadding);
                     if (lastView != null) {
-                        Float prevValue = state.increasedPaddingMap.get(lastView);
-                        float newValue = prevValue != null
-                                ? Math.max(prevValue, increasedPadding)
-                                : increasedPadding;
-                        state.increasedPaddingMap.put(lastView, newValue);
+                        Float prevValue = state.paddingMap.get(lastView);
+                        float newValue = getPaddingForValue(increasedPadding);
+                        if (prevValue != null) {
+                            float prevPadding = getPaddingForValue(prevValue);
+                            if (increasedPadding > 0) {
+                                newValue = NotificationUtils.interpolate(
+                                        prevPadding,
+                                        newValue,
+                                        increasedPadding);
+                            } else if (prevValue > 0) {
+                                newValue = NotificationUtils.interpolate(
+                                        newValue,
+                                        prevPadding,
+                                        prevValue);
+                            }
+                        }
+                        state.paddingMap.put(lastView, newValue);
                     }
+                } else if (lastView != null) {
+                    float newValue = getPaddingForValue(state.paddingMap.get(lastView));
+                    state.paddingMap.put(lastView, newValue);
                 }
                 if (v instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -287,6 +301,22 @@
         }
     }
 
+    private float getPaddingForValue(Float increasedPadding) {
+        if (increasedPadding == null) {
+            return mPaddingBetweenElements;
+        } else if (increasedPadding >= 0.0f) {
+            return NotificationUtils.interpolate(
+                    mPaddingBetweenElements,
+                    mIncreasedPaddingBetweenElements,
+                    increasedPadding);
+        } else {
+            return NotificationUtils.interpolate(
+                    0,
+                    mPaddingBetweenElements,
+                    1.0f + increasedPadding);
+        }
+    }
+
     private int updateNotGoneIndex(StackScrollState resultState,
             StackScrollAlgorithmState state, int notGoneIndex,
             ExpandableView v) {
@@ -527,18 +557,17 @@
         public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
 
         /**
-         * The children from the host that need an increased padding after them. A value of 0 means
-         * no increased padding, a value of 1 means full padding.
+         * The padding after each child measured in pixels.
          */
-        public final HashMap<ExpandableView, Float> increasedPaddingMap = new HashMap<>();
+        public final HashMap<ExpandableView, Float> paddingMap = new HashMap<>();
 
         public int getPaddingAfterChild(ExpandableView child) {
-            Float paddingValue = increasedPaddingMap.get(child);
-            return paddingValue == null
-                    ? mPaddingBetweenElements
-                    : (int) NotificationUtils.interpolate(mPaddingBetweenElements,
-                            mIncreasedPaddingBetweenElements,
-                            paddingValue);
+            Float padding = paddingMap.get(child);
+            if (padding == null) {
+                // Should only happen for the last view
+                return mPaddingBetweenElements;
+            }
+            return (int) padding.floatValue();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BetterListPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BetterListPreference.java
new file mode 100644
index 0000000..c9c780a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BetterListPreference.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.tuner;
+
+import android.content.Context;
+import android.support.v7.preference.ListPreference;
+import android.util.AttributeSet;
+
+public class BetterListPreference extends ListPreference {
+
+    private CharSequence mSummary;
+
+    public BetterListPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void setSummary(CharSequence summary) {
+        super.setSummary(summary);
+        mSummary = summary;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        return mSummary;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
new file mode 100644
index 0000000..41786b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.tuner;
+
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Process;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
+import com.android.systemui.statusbar.phone.ExpandableIndicator;
+import com.android.systemui.tuner.ShortcutParser.Shortcut;
+import com.android.systemui.tuner.TunerService.Tunable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class LockscreenFragment extends PreferenceFragment {
+
+    private static final String KEY_LEFT = "left";
+    private static final String KEY_RIGHT = "right";
+    private static final String KEY_CUSTOMIZE = "customize";
+    private static final String KEY_SHORTCUT = "shortcut";
+
+    public static final String LOCKSCREEN_LEFT_BUTTON = "sysui_keyguard_left";
+    public static final String LOCKSCREEN_LEFT_UNLOCK = "sysui_keyguard_left_unlock";
+    public static final String LOCKSCREEN_RIGHT_BUTTON = "sysui_keyguard_right";
+    public static final String LOCKSCREEN_RIGHT_UNLOCK = "sysui_keyguard_right_unlock";
+
+    private final ArrayList<Tunable> mTunables = new ArrayList<>();
+    private TunerService mTunerService;
+    private Handler mHandler;
+
+    @Override
+    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+        mTunerService = TunerService.get(getContext());
+        mHandler = new Handler();
+        addPreferencesFromResource(R.xml.lockscreen_settings);
+        setupGroup((PreferenceGroup) findPreference(KEY_LEFT), LOCKSCREEN_LEFT_BUTTON,
+                LOCKSCREEN_LEFT_UNLOCK);
+        setupGroup((PreferenceGroup) findPreference(KEY_RIGHT), LOCKSCREEN_RIGHT_BUTTON,
+                LOCKSCREEN_RIGHT_UNLOCK);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mTunables.forEach(t -> mTunerService.removeTunable(t));
+    }
+
+    private void setupGroup(PreferenceGroup group, String buttonSetting, String unlockKey) {
+        SwitchPreference customize = (SwitchPreference) group.findPreference(KEY_CUSTOMIZE);
+        Preference shortcut = group.findPreference(KEY_SHORTCUT);
+        SwitchPreference unlock = (SwitchPreference) group.findPreference(unlockKey);
+        addTunable((k, v) -> {
+            boolean visible = v != null;
+            customize.setChecked(visible);
+            shortcut.setVisible(visible);
+            unlock.setVisible(visible);
+            if (visible) {
+                setSummary(shortcut, v);
+            }
+        }, buttonSetting);
+        customize.setOnPreferenceChangeListener((preference, newValue) -> {
+            boolean hasSetting = mTunerService.getValue(buttonSetting) != null;
+            if (hasSetting != (boolean) newValue) {
+                mHandler.post(() -> mTunerService.setValue(buttonSetting, hasSetting ? null : ""));
+            }
+            return true;
+        });
+        shortcut.setOnPreferenceClickListener(preference -> {
+            showSelectDialog(buttonSetting);
+            return true;
+        });
+    }
+
+    private void showSelectDialog(String buttonSetting) {
+        RecyclerView v = (RecyclerView) LayoutInflater.from(getContext())
+                .inflate(R.layout.tuner_shortcut_list, null);
+        v.setLayoutManager(new LinearLayoutManager(getContext()));
+        AlertDialog dialog = new Builder(getContext())
+                .setView(v)
+                .show();
+        Adapter adapter = new Adapter(getContext(), item -> {
+            mTunerService.setValue(buttonSetting, item.getSettingValue());
+            dialog.dismiss();
+        });
+        LauncherApps apps = getContext().getSystemService(LauncherApps.class);
+        List<LauncherActivityInfo> activities = apps.getActivityList(null,
+                Process.myUserHandle());
+
+        activities.forEach(info -> {
+            App app = new App(getContext(), info);
+            try {
+                new ShortcutParser(getContext(), info.getComponentName()).getShortcuts().forEach(
+                        shortcut -> app.addChild(new StaticShortcut(getContext(), shortcut)));
+            } catch (NameNotFoundException e) {
+            }
+            adapter.addItem(app);
+        });
+
+        v.setAdapter(adapter);
+    }
+
+    private void setSummary(Preference shortcut, String value) {
+        if (value.contains("::")) {
+            Shortcut info = getShortcutInfo(getContext(), value);
+            shortcut.setSummary(info != null ? info.label : null);
+        } else if (value.contains("/")) {
+            ActivityInfo info = getActivityinfo(getContext(), value);
+            shortcut.setSummary(info != null ? info.loadLabel(getContext().getPackageManager())
+                    : null);
+        } else {
+            shortcut.setSummary(null);
+        }
+    }
+
+    private void addTunable(Tunable t, String... keys) {
+        mTunables.add(t);
+        mTunerService.addTunable(t, keys);
+    }
+
+    public static ActivityInfo getActivityinfo(Context context, String value) {
+        ComponentName component = ComponentName.unflattenFromString(value);
+        try {
+            return context.getPackageManager().getActivityInfo(component, 0);
+        } catch (NameNotFoundException e) {
+            return null;
+        }
+    }
+
+    public static Shortcut getShortcutInfo(Context context, String value) {
+        return Shortcut.create(context, value);
+    }
+
+    public static class Holder extends ViewHolder {
+        public final ImageView icon;
+        public final TextView title;
+        public final ExpandableIndicator expand;
+
+        public Holder(View itemView) {
+            super(itemView);
+            icon = (ImageView) itemView.findViewById(android.R.id.icon);
+            title = (TextView) itemView.findViewById(android.R.id.title);
+            expand = (ExpandableIndicator) itemView.findViewById(R.id.expand);
+        }
+    }
+
+    private static class StaticShortcut extends Item {
+
+        private final Context mContext;
+        private final Shortcut mShortcut;
+
+
+        public StaticShortcut(Context context, Shortcut shortcut) {
+            mContext = context;
+            mShortcut = shortcut;
+        }
+
+        @Override
+        public Drawable getDrawable() {
+            return mShortcut.icon.loadDrawable(mContext);
+        }
+
+        @Override
+        public String getLabel() {
+            return mShortcut.label;
+        }
+
+        @Override
+        public String getSettingValue() {
+            return mShortcut.toString();
+        }
+
+        @Override
+        public Boolean getExpando() {
+            return null;
+        }
+    }
+
+    private static class App extends Item {
+
+        private final Context mContext;
+        private final LauncherActivityInfo mInfo;
+        private final ArrayList<Item> mChildren = new ArrayList<>();
+        private boolean mExpanded;
+
+        public App(Context context, LauncherActivityInfo info) {
+            mContext = context;
+            mInfo = info;
+            mExpanded = false;
+        }
+
+        public void addChild(Item child) {
+            mChildren.add(child);
+        }
+
+        @Override
+        public Drawable getDrawable() {
+            return mInfo.getBadgedIcon(mContext.getResources().getConfiguration().densityDpi);
+        }
+
+        @Override
+        public String getLabel() {
+            return mInfo.getLabel().toString();
+        }
+
+        @Override
+        public String getSettingValue() {
+            return mInfo.getComponentName().flattenToString();
+        }
+
+        @Override
+        public Boolean getExpando() {
+            return mChildren.size() != 0 ? mExpanded : null;
+        }
+
+        @Override
+        public void toggleExpando(Adapter adapter) {
+            mExpanded = !mExpanded;
+            if (mExpanded) {
+                mChildren.forEach(child -> adapter.addItem(this, child));
+            } else {
+                mChildren.forEach(child -> adapter.remItem(child));
+            }
+        }
+    }
+
+    private abstract static class Item {
+        public abstract Drawable getDrawable();
+
+        public abstract String getLabel();
+
+        public abstract String getSettingValue();
+
+        public abstract Boolean getExpando();
+
+        public void toggleExpando(Adapter adapter) {
+        }
+    }
+
+    public static class Adapter extends RecyclerView.Adapter<Holder> {
+        private ArrayList<Item> mItems = new ArrayList<>();
+        private final Context mContext;
+        private final Consumer<Item> mCallback;
+
+        public Adapter(Context context, Consumer<Item> callback) {
+            mContext = context;
+            mCallback = callback;
+        }
+
+        @Override
+        public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new Holder(LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.tuner_shortcut_item, parent, false));
+        }
+
+        @Override
+        public void onBindViewHolder(Holder holder, int position) {
+            Item item = mItems.get(position);
+            holder.icon.setImageDrawable(item.getDrawable());
+            holder.title.setText(item.getLabel());
+            holder.itemView.setOnClickListener(
+                    v -> mCallback.accept(mItems.get(holder.getAdapterPosition())));
+            Boolean expando = item.getExpando();
+            if (expando != null) {
+                holder.expand.setVisibility(View.VISIBLE);
+                holder.expand.setExpanded(expando);
+                holder.expand.setOnClickListener(
+                        v -> mItems.get(holder.getAdapterPosition()).toggleExpando(Adapter.this));
+            } else {
+                holder.expand.setVisibility(View.GONE);
+            }
+        }
+
+        @Override
+        public int getItemCount() {
+            return mItems.size();
+        }
+
+        public void addItem(Item item) {
+            mItems.add(item);
+            notifyDataSetChanged();
+        }
+
+        public void remItem(Item item) {
+            int index = mItems.indexOf(item);
+            mItems.remove(item);
+            notifyItemRemoved(index);
+        }
+
+        public void addItem(Item parent, Item child) {
+            int index = mItems.indexOf(parent);
+            mItems.add(index + 1, child);
+            notifyItemInserted(index + 1);
+        }
+    }
+
+    public static IntentButton getIntentButton(Context context, String buttonStr,
+            IntentButton plugin, IntentButton def) {
+        // Plugin wins.
+        if (plugin != null) return plugin;
+        // Then tuner options.
+        if (!TextUtils.isEmpty(buttonStr)) {
+            if (buttonStr.contains("::")) {
+                Shortcut shortcut = getShortcutInfo(context, buttonStr);
+                if (shortcut != null) {
+                    return new ShortcutButton(context, shortcut);
+                }
+            } else if (buttonStr.contains("/")) {
+                ActivityInfo info = getActivityinfo(context, buttonStr);
+                if (info != null) {
+                    return new ActivityButton(context, info);
+                }
+            }
+        }
+        // Then default.
+        return def;
+    }
+
+    private static class ShortcutButton implements IntentButton {
+        private final Shortcut mShortcut;
+        private final IconState mIconState;
+
+        public ShortcutButton(Context context, Shortcut shortcut) {
+            mShortcut = shortcut;
+            mIconState = new IconState();
+            mIconState.isVisible = true;
+            mIconState.drawable = shortcut.icon.loadDrawable(context);
+            mIconState.contentDescription = mShortcut.label;
+        }
+
+        @Override
+        public IconState getIcon() {
+            return mIconState;
+        }
+
+        @Override
+        public Intent getIntent() {
+            return mShortcut.intent;
+        }
+    }
+
+    private static class ActivityButton implements IntentButton {
+        private final Intent mIntent;
+        private final IconState mIconState;
+
+        public ActivityButton(Context context, ActivityInfo info) {
+            mIntent = new Intent().setComponent(new ComponentName(info.packageName, info.name));
+            mIconState = new IconState();
+            mIconState.isVisible = true;
+            mIconState.drawable = info.loadIcon(context.getPackageManager());
+            mIconState.contentDescription = info.loadLabel(context.getPackageManager());
+        }
+
+        @Override
+        public IconState getIcon() {
+            return mIconState;
+        }
+
+        @Override
+        public Intent getIntent() {
+            return mIntent;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
index ad42459..9593c45 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NavBarTuner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of the License at
@@ -14,590 +14,227 @@
 
 package com.android.systemui.tuner;
 
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Fragment;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
-import android.util.TypedValue;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.BACK;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.BUTTON_SEPARATOR;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.CLIPBOARD;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.GRAVITY_SEPARATOR;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.HOME;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_END;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_CODE_START;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.KEY_IMAGE_DELIM;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.MENU_IME;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAVSPACE;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_LEFT;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_RIGHT;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.RECENT;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.SIZE_MOD_END;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.SIZE_MOD_START;
 import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractButton;
-import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractSize;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractImage;
+import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.extractKeycode;
 
-public class NavBarTuner extends Fragment implements TunerService.Tunable {
+import android.annotation.Nullable;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.DropDownPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.Preference.OnPreferenceChangeListener;
+import android.support.v7.preference.Preference.OnPreferenceClickListener;
+import android.support.v7.preference.PreferenceCategory;
+import android.text.SpannableStringBuilder;
+import android.text.style.ImageSpan;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.widget.EditText;
 
-    private static final int SAVE = Menu.FIRST + 1;
-    private static final int RESET = Menu.FIRST + 2;
-    private static final int READ_REQUEST = 42;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.NavigationBarInflaterView;
+import com.android.systemui.tuner.TunerService.Tunable;
 
-    private static final float PREVIEW_SCALE = .95f;
-    private static final float PREVIEW_SCALE_LANDSCAPE = .75f;
+import java.util.ArrayList;
 
-    private NavBarAdapter mNavBarAdapter;
-    private PreviewNavInflater mPreview;
+public class NavBarTuner extends PreferenceFragment {
+
+    private static final String LAYOUT = "layout";
+    private static final String LEFT = "left";
+    private static final String RIGHT = "right";
+
+    private static final String TYPE = "type";
+    private static final String KEYCODE = "keycode";
+    private static final String ICON = "icon";
+
+    private static final int[] ICONS = new int[]{
+            R.drawable.ic_qs_circle,
+            R.drawable.ic_add,
+            R.drawable.ic_remove,
+            R.drawable.ic_left,
+            R.drawable.ic_right,
+            R.drawable.ic_menu,
+    };
+
+    private final ArrayList<Tunable> mTunables = new ArrayList<>();
+    private Handler mHandler;
 
     @Override
-    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-            Bundle savedInstanceState) {
-        final View view = inflater.inflate(R.layout.nav_bar_tuner, container, false);
-        inflatePreview((ViewGroup) view.findViewById(R.id.nav_preview_frame));
-        return view;
-    }
-
-    private void inflatePreview(ViewGroup view) {
-        Display display = getActivity().getWindowManager().getDefaultDisplay();
-        boolean isRotated = display.getRotation() == Surface.ROTATION_90
-                || display.getRotation() == Surface.ROTATION_270;
-
-        Configuration config = new Configuration(getContext().getResources().getConfiguration());
-        boolean isPhoneLandscape = isRotated && (config.smallestScreenWidthDp < 600);
-        final float scale = isPhoneLandscape ? PREVIEW_SCALE_LANDSCAPE : PREVIEW_SCALE;
-        config.densityDpi = (int) (config.densityDpi * scale);
-
-        mPreview = (PreviewNavInflater) LayoutInflater.from(getContext().createConfigurationContext(
-                config)).inflate(R.layout.nav_bar_tuner_inflater, view, false);
-        final ViewGroup.LayoutParams layoutParams = mPreview.getLayoutParams();
-        layoutParams.width = (int) ((isPhoneLandscape ? display.getHeight() : display.getWidth())
-                * scale);
-        // Not sure why, but the height dimen is not being scaled with the dp, set it manually
-        // for now.
-        layoutParams.height = (int) (layoutParams.height * scale);
-        if (isPhoneLandscape) {
-            int width = layoutParams.width;
-            layoutParams.width = layoutParams.height;
-            layoutParams.height = width;
-        }
-        view.addView(mPreview);
-
-        if (isRotated) {
-            mPreview.findViewById(R.id.rot0).setVisibility(View.GONE);
-            final View rot90 = mPreview.findViewById(R.id.rot90);
-        } else {
-            mPreview.findViewById(R.id.rot90).setVisibility(View.GONE);
-            final View rot0 = mPreview.findViewById(R.id.rot0);
-        }
-    }
-
-    private void notifyChanged() {
-        mPreview.onTuningChanged(NAV_BAR_VIEWS, mNavBarAdapter.getNavString());
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        mHandler = new Handler();
+        super.onCreate(savedInstanceState);
     }
 
     @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        RecyclerView recyclerView = (RecyclerView) view.findViewById(android.R.id.list);
-        final Context context = getContext();
-        recyclerView.setLayoutManager(new LinearLayoutManager(context));
-        mNavBarAdapter = new NavBarAdapter(context);
-        recyclerView.setAdapter(mNavBarAdapter);
-        recyclerView.addItemDecoration(new Dividers(context));
-        final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mNavBarAdapter.mCallbacks);
-        mNavBarAdapter.setTouchHelper(itemTouchHelper);
-        itemTouchHelper.attachToRecyclerView(recyclerView);
-
-        TunerService.get(getContext()).addTunable(this, NAV_BAR_VIEWS);
+    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
     }
 
     @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        TunerService.get(getContext()).removeTunable(this);
+    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+        addPreferencesFromResource(R.xml.nav_bar_tuner);
+        bindLayout((ListPreference) findPreference(LAYOUT));
+        bindButton((PreferenceCategory) findPreference(LEFT),
+                NAV_BAR_LEFT, NAVSPACE);
+        bindButton((PreferenceCategory) findPreference(RIGHT),
+                NAV_BAR_RIGHT, MENU_IME);
     }
 
     @Override
-    public void onTuningChanged(String key, String navLayout) {
-        if (!NAV_BAR_VIEWS.equals(key)) return;
-        Context context = getContext();
-        if (navLayout == null) {
-            navLayout = context.getString(R.string.config_navBarLayout);
-        }
-        String[] views = navLayout.split(GRAVITY_SEPARATOR);
-        String[] groups = new String[] { NavBarAdapter.START, NavBarAdapter.CENTER,
-                NavBarAdapter.END};
-        CharSequence[] groupLabels = new String[] { getString(R.string.start),
-                getString(R.string.center), getString(R.string.end) };
-        mNavBarAdapter.clear();
-        for (int i = 0; i < 3; i++) {
-            mNavBarAdapter.addButton(groups[i], groupLabels[i]);
-            for (String button : views[i].split(BUTTON_SEPARATOR)) {
-                mNavBarAdapter.addButton(button, getLabel(button, context));
+    public void onDestroy() {
+        super.onDestroy();
+        mTunables.forEach(t -> TunerService.get(getContext()).removeTunable(t));
+    }
+
+    private void addTunable(Tunable tunable, String... keys) {
+        mTunables.add(tunable);
+        TunerService.get(getContext()).addTunable(tunable, keys);
+    }
+
+    private void bindLayout(ListPreference preference) {
+        addTunable((key, newValue) -> mHandler.post(() -> {
+            String val = newValue;
+            if (val == null) {
+                val = "default";
             }
-        }
-        mNavBarAdapter.addButton(NavBarAdapter.ADD, getString(R.string.add_button));
-        setHasOptionsMenu(true);
+            preference.setValue(val);
+        }), NAV_BAR_VIEWS);
+        preference.setOnPreferenceChangeListener((preference1, newValue) -> {
+            String val = (String) newValue;
+            if ("default".equals(val)) val = null;
+            TunerService.get(getContext()).setValue(NAV_BAR_VIEWS, val);
+            return true;
+        });
     }
 
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        super.onCreateOptionsMenu(menu, inflater);
-        // TODO: Show save button conditionally, only when there are changes.
-        menu.add(Menu.NONE, SAVE, Menu.NONE, getString(R.string.save))
-                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-        menu.add(Menu.NONE, RESET, Menu.NONE, getString(R.string.reset));
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item.getItemId() == SAVE) {
-            if (!mNavBarAdapter.hasHomeButton()) {
-                new AlertDialog.Builder(getContext())
-                        .setTitle(R.string.no_home_title)
-                        .setMessage(R.string.no_home_message)
-                        .setPositiveButton(android.R.string.ok, null)
-                        .show();
+    private void bindButton(PreferenceCategory parent, String setting, String def) {
+        String k = parent.getKey();
+        DropDownPreference type = (DropDownPreference) findPreference(TYPE + "_" + k);
+        Preference keycode = findPreference(KEYCODE + "_" + k);
+        ListPreference icon = (ListPreference) findPreference(ICON + "_" + k);
+        setupIcons(icon);
+        addTunable((key, newValue) -> mHandler.post(() -> {
+            String val = newValue;
+            if (val == null) {
+                val = def;
+            }
+            String button = extractButton(val);
+            if (button.startsWith(KEY)) {
+                type.setValue(KEY);
+                String uri = extractImage(button);
+                int code = extractKeycode(button);
+                icon.setValue(uri);
+                updateSummary(icon);
+                keycode.setSummary(code + "");
+                keycode.setVisible(true);
+                icon.setVisible(true);
             } else {
-                Settings.Secure.putString(getContext().getContentResolver(),
-                        NAV_BAR_VIEWS, mNavBarAdapter.getNavString());
+                type.setValue(button);
+                keycode.setVisible(false);
+                icon.setVisible(false);
             }
-            return true;
-        } else if (item.getItemId() == RESET) {
-            Settings.Secure.putString(getContext().getContentResolver(),
-                    NAV_BAR_VIEWS, null);
-            return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    private static CharSequence getLabel(String button, Context context) {
-        if (button.startsWith(HOME)) {
-            return context.getString(R.string.accessibility_home);
-        } else if (button.startsWith(BACK)) {
-            return context.getString(R.string.accessibility_back);
-        } else if (button.startsWith(RECENT)) {
-            return context.getString(R.string.accessibility_recent);
-        } else if (button.startsWith(NAVSPACE)) {
-            return context.getString(R.string.space);
-        } else if (button.startsWith(MENU_IME)) {
-            return context.getString(R.string.menu_ime);
-        } else if (button.startsWith(CLIPBOARD)) {
-            return context.getString(R.string.clipboard);
-        } else if (button.startsWith(KEY)) {
-            return context.getString(R.string.keycode);
-        }
-        return button;
-    }
-
-    private static class Holder extends RecyclerView.ViewHolder {
-        private TextView title;
-
-        public Holder(View itemView) {
-            super(itemView);
-            title = (TextView) itemView.findViewById(android.R.id.title);
-        }
-    }
-
-    private static class Dividers extends RecyclerView.ItemDecoration {
-        private final Drawable mDivider;
-
-        public Dividers(Context context) {
-            TypedValue value = new TypedValue();
-            context.getTheme().resolveAttribute(android.R.attr.listDivider, value, true);
-            mDivider = context.getDrawable(value.resourceId);
-        }
-
-        @Override
-        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
-            super.onDraw(c, parent, state);
-            final int left = parent.getPaddingLeft();
-            final int right = parent.getWidth() - parent.getPaddingRight();
-
-            final int childCount = parent.getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = parent.getChildAt(i);
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
-                        .getLayoutParams();
-                final int top = child.getBottom() + params.bottomMargin;
-                final int bottom = top + mDivider.getIntrinsicHeight();
-                mDivider.setBounds(left, top, right, bottom);
-                mDivider.draw(c);
-            }
-        }
-    }
-
-    private void selectImage() {
-        startActivityForResult(KeycodeSelectionHelper.getSelectImageIntent(), READ_REQUEST);
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == READ_REQUEST && resultCode == Activity.RESULT_OK && data != null) {
-            final Uri uri = data.getData();
-            final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);
-            getContext().getContentResolver().takePersistableUriPermission(uri, takeFlags);
-            mNavBarAdapter.onImageSelected(uri);
-        } else {
-            super.onActivityResult(requestCode, resultCode, data);
-        }
-    }
-
-    private class NavBarAdapter extends RecyclerView.Adapter<Holder>
-            implements View.OnClickListener {
-
-        private static final String START = "start";
-        private static final String CENTER = "center";
-        private static final String END = "end";
-        private static final String ADD = "add";
-
-        private static final int ADD_ID = 0;
-        private static final int BUTTON_ID = 1;
-        private static final int CATEGORY_ID = 2;
-
-        private List<String> mButtons = new ArrayList<>();
-        private List<CharSequence> mLabels = new ArrayList<>();
-        private int mCategoryLayout;
-        private int mButtonLayout;
-        private ItemTouchHelper mTouchHelper;
-
-        // Stored keycode while we wait for image selection on a KEY.
-        private int mKeycode;
-
-        public NavBarAdapter(Context context) {
-            TypedArray attrs = context.getTheme().obtainStyledAttributes(null,
-                    android.R.styleable.Preference, android.R.attr.preferenceStyle, 0);
-            mButtonLayout = attrs.getResourceId(android.R.styleable.Preference_layout, 0);
-            attrs = context.getTheme().obtainStyledAttributes(null,
-                    android.R.styleable.Preference, android.R.attr.preferenceCategoryStyle, 0);
-            mCategoryLayout = attrs.getResourceId(android.R.styleable.Preference_layout, 0);
-        }
-
-        public void setTouchHelper(ItemTouchHelper itemTouchHelper) {
-            mTouchHelper = itemTouchHelper;
-        }
-
-        public void clear() {
-            mButtons.clear();
-            mLabels.clear();
-            notifyDataSetChanged();
-        }
-
-        public void addButton(String button, CharSequence label) {
-            mButtons.add(button);
-            mLabels.add(label);
-            notifyItemInserted(mLabels.size() - 1);
-            notifyChanged();
-        }
-
-        public boolean hasHomeButton() {
-            final int N = mButtons.size();
-            for (int i = 0; i < N; i++) {
-                if (mButtons.get(i).startsWith(HOME)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public String getNavString() {
-            StringBuilder builder = new StringBuilder();
-            for (int i = 1; i < mButtons.size() - 1; i++) {
-                String button = mButtons.get(i);
-                if (button.equals(CENTER) || button.equals(END)) {
-                    if (builder.length() == 0 || builder.toString().endsWith(GRAVITY_SEPARATOR)) {
-                        // No start or center buttons, fill with a space.
-                        builder.append(NAVSPACE);
-                    }
-                    builder.append(GRAVITY_SEPARATOR);
-                    continue;
-                } else if (builder.length() != 0 && !builder.toString().endsWith(
-                        GRAVITY_SEPARATOR)) {
-                    builder.append(BUTTON_SEPARATOR);
-                }
-                builder.append(button);
-            }
-            if (builder.toString().endsWith(GRAVITY_SEPARATOR)) {
-                // No end buttons, fill with space.
-                builder.append(NAVSPACE);
-            }
-            return builder.toString();
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            String button = mButtons.get(position);
-            if (button.equals(START) || button.equals(CENTER) || button.equals(END)) {
-                return CATEGORY_ID;
-            }
-            if (button.equals(ADD)) {
-                return ADD_ID;
-            }
-            return BUTTON_ID;
-        }
-
-        @Override
-        public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
-            final Context context = parent.getContext();
-            final LayoutInflater inflater = LayoutInflater.from(context);
-            final View view = inflater.inflate(getLayoutId(viewType), parent, false);
-            if (viewType == BUTTON_ID) {
-                inflater.inflate(R.layout.nav_control_widget,
-                        (ViewGroup) view.findViewById(android.R.id.widget_frame));
-            }
-            return new Holder(view);
-        }
-
-        private int getLayoutId(int viewType) {
-            if (viewType == CATEGORY_ID) {
-                return mCategoryLayout;
-            }
-            return mButtonLayout;
-        }
-
-        @Override
-        public void onBindViewHolder(Holder holder, int position) {
-            holder.title.setText(mLabels.get(position));
-            if (holder.getItemViewType() == BUTTON_ID) {
-                bindButton(holder, position);
-            } else if (holder.getItemViewType() == ADD_ID) {
-                bindAdd(holder);
-            }
-        }
-
-        private void bindAdd(Holder holder) {
-            TypedValue value = new TypedValue();
-            final Context context = holder.itemView.getContext();
-            context.getTheme().resolveAttribute(android.R.attr.colorAccent, value, true);
-            final ImageView icon = (ImageView) holder.itemView.findViewById(android.R.id.icon);
-            icon.setImageResource(R.drawable.ic_add);
-            icon.setImageTintList(ColorStateList.valueOf(context.getColor(value.resourceId)));
-            holder.itemView.findViewById(android.R.id.summary).setVisibility(View.GONE);
-            holder.itemView.setClickable(true);
-            holder.itemView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    showAddDialog(v.getContext());
-                }
+        }), setting);
+        OnPreferenceChangeListener listener = (preference, newValue) -> {
+            mHandler.post(() -> {
+                setValue(setting, type, keycode, icon);
+                updateSummary(icon);
             });
-        }
-
-        private void bindButton(final Holder holder, int position) {
-            holder.itemView.findViewById(android.R.id.icon_frame).setVisibility(View.GONE);
-            holder.itemView.findViewById(android.R.id.summary).setVisibility(View.GONE);
-            bindClick(holder.itemView.findViewById(R.id.close), holder);
-            bindClick(holder.itemView.findViewById(R.id.width), holder);
-            holder.itemView.findViewById(R.id.drag).setOnTouchListener(new View.OnTouchListener() {
-                @Override
-                public boolean onTouch(View v, MotionEvent event) {
-                    mTouchHelper.startDrag(holder);
-                    return true;
-                }
-            });
-        }
-
-        private void showAddDialog(final Context context) {
-            final String[] options = new String[] {
-                    BACK, HOME, RECENT, MENU_IME, NAVSPACE, CLIPBOARD, KEY,
-            };
-            final CharSequence[] labels = new CharSequence[options.length];
-            for (int i = 0; i < options.length; i++) {
-                labels[i] = getLabel(options[i], context);
-            }
-            new AlertDialog.Builder(context)
-                    .setTitle(R.string.select_button)
-                    .setItems(labels, new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            if (KEY.equals(options[which])) {
-                                showKeyDialogs(context);
-                            } else {
-                                int index = mButtons.size() - 1;
-                                showAddedMessage(context, options[which]);
-                                mButtons.add(index, options[which]);
-                                mLabels.add(index, labels[which]);
-
-                                notifyItemInserted(index);
-                                notifyChanged();
-                            }
-                        }
-                    }).setNegativeButton(android.R.string.cancel, null)
-                    .show();
-        }
-
-        private void onImageSelected(Uri uri) {
-            int index = mButtons.size() - 1;
-            mButtons.add(index, KEY + KEY_CODE_START + mKeycode + KEY_IMAGE_DELIM + uri.toString()
-                    + KEY_CODE_END);
-            mLabels.add(index, getLabel(KEY, getContext()));
-
-            notifyItemInserted(index);
-            notifyChanged();
-        }
-
-        private void showKeyDialogs(final Context context) {
-            final KeycodeSelectionHelper.OnSelectionComplete listener =
-                    new KeycodeSelectionHelper.OnSelectionComplete() {
-                        @Override
-                        public void onSelectionComplete(int code) {
-                            mKeycode = code;
-                            selectImage();
-                        }
-                    };
-            new AlertDialog.Builder(context)
-                    .setTitle(R.string.keycode)
-                    .setMessage(R.string.keycode_description)
-                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            KeycodeSelectionHelper.showKeycodeSelect(context, listener);
-                        }
-                    }).show();
-        }
-
-        private void showAddedMessage(Context context, String button) {
-            if (CLIPBOARD.equals(button)) {
-                new AlertDialog.Builder(context)
-                        .setTitle(R.string.clipboard)
-                        .setMessage(R.string.clipboard_description)
-                        .setPositiveButton(android.R.string.ok, null)
-                        .show();
-            }
-        }
-
-        private void bindClick(View view, Holder holder) {
-            view.setOnClickListener(this);
-            view.setTag(holder);
-        }
-
-        @Override
-        public void onClick(View v) {
-            Holder holder = (Holder) v.getTag();
-            if (v.getId() == R.id.width) {
-                showWidthDialog(holder, v.getContext());
-            } else if (v.getId() == R.id.close) {
-                int position = holder.getAdapterPosition();
-                mButtons.remove(position);
-                mLabels.remove(position);
-                notifyItemRemoved(position);
-                notifyChanged();
-            }
-        }
-
-        private void showWidthDialog(final Holder holder, Context context) {
-            final String buttonSpec = mButtons.get(holder.getAdapterPosition());
-            float amount = extractSize(buttonSpec);
-            final AlertDialog dialog = new AlertDialog.Builder(context)
-                    .setTitle(R.string.adjust_button_width)
-                    .setView(R.layout.nav_width_view)
-                    .setNegativeButton(android.R.string.cancel, null).create();
-            dialog.setButton(DialogInterface.BUTTON_POSITIVE,
-                    context.getString(android.R.string.ok),
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface d, int which) {
-                            final String button = extractButton(buttonSpec);
-                            SeekBar seekBar = (SeekBar) dialog.findViewById(R.id.seekbar);
-                            if (seekBar.getProgress() == 75) {
-                                mButtons.set(holder.getAdapterPosition(), button);
-                            } else {
-                                float amount = (seekBar.getProgress() + 25) / 100f;
-                                mButtons.set(holder.getAdapterPosition(), button
-                                        + SIZE_MOD_START + amount + SIZE_MOD_END);
-                            }
-                            notifyChanged();
-                        }
-                    });
-            dialog.show();
-            SeekBar seekBar = (SeekBar) dialog.findViewById(R.id.seekbar);
-            // Range is .25 - 1.75.
-            seekBar.setMax(150);
-            seekBar.setProgress((int) ((amount - .25f) * 100));
-        }
-
-        @Override
-        public int getItemCount() {
-            return mButtons.size();
-        }
-
-        private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
-            @Override
-            public boolean isLongPressDragEnabled() {
-                return false;
-            }
-
-            @Override
-            public boolean isItemViewSwipeEnabled() {
-                return false;
-            }
-
-            @Override
-            public int getMovementFlags(RecyclerView recyclerView,
-                    RecyclerView.ViewHolder viewHolder) {
-                if (viewHolder.getItemViewType() != BUTTON_ID) {
-                    return makeMovementFlags(0, 0);
-                }
-                int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
-                return makeMovementFlags(dragFlags, 0);
-            }
-
-            @Override
-            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
-                    RecyclerView.ViewHolder target) {
-                int from = viewHolder.getAdapterPosition();
-                int to = target.getAdapterPosition();
-                if (to == 0) {
-                    // Can't go above the top.
-                    return false;
-                }
-                move(from, to, mButtons);
-                move(from, to, mLabels);
-                notifyChanged();
-                notifyItemMoved(from, to);
-                return true;
-            }
-
-            private <T> void move(int from, int to, List<T> list) {
-                list.add(from > to ? to : to + 1, list.get(from));
-                list.remove(from > to ? from + 1 : from);
-            }
-
-            @Override
-            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
-                // Don't care.
-            }
+            return true;
         };
+        type.setOnPreferenceChangeListener(listener);
+        icon.setOnPreferenceChangeListener(listener);
+        keycode.setOnPreferenceClickListener(preference -> {
+            EditText editText = new EditText(getContext());
+            new AlertDialog.Builder(getContext())
+                    .setTitle(preference.getTitle())
+                    .setView(editText)
+                    .setNegativeButton(android.R.string.cancel, null)
+                    .setPositiveButton(android.R.string.ok, (dialog, which) -> {
+                        int code = KeyEvent.KEYCODE_ENTER;
+                        try {
+                            code = Integer.parseInt(editText.getText().toString());
+                        } catch (Exception e) {
+                        }
+                        keycode.setSummary(code + "");
+                        setValue(setting, type, keycode, icon);
+                    }).show();
+            return true;
+        });
+    }
+
+    private void updateSummary(ListPreference icon) {
+        try {
+            int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14,
+                    getContext().getResources().getDisplayMetrics());
+            String pkg = icon.getValue().split("/")[0];
+            int id = Integer.parseInt(icon.getValue().split("/")[1]);
+            SpannableStringBuilder builder = new SpannableStringBuilder();
+            Drawable d = Icon.createWithResource(pkg, id)
+                    .loadDrawable(getContext());
+            d.setTint(Color.BLACK);
+            d.setBounds(0, 0, size, size);
+            ImageSpan span = new ImageSpan(d);
+            builder.append("  ", span, 0);
+            icon.setSummary(builder);
+        } catch (Exception e) {
+            Log.d("NavButton", "Problem with summary", e);
+            icon.setSummary(null);
+        }
+    }
+
+    private void setValue(String setting, DropDownPreference type, Preference keycode,
+            ListPreference icon) {
+        String button = type.getValue();
+        if (KEY.equals(button)) {
+            String uri = icon.getValue();
+            int code = KeyEvent.KEYCODE_ENTER;
+            try {
+                code = Integer.parseInt(keycode.getSummary().toString());
+            } catch (Exception e) {
+            }
+            button = button + KEY_CODE_START + code + KEY_IMAGE_DELIM + uri + KEY_CODE_END;
+        }
+        TunerService.get(getContext()).setValue(setting, button);
+    }
+
+    private void setupIcons(ListPreference icon) {
+        CharSequence[] labels = new CharSequence[ICONS.length];
+        CharSequence[] values = new CharSequence[ICONS.length];
+        int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14,
+                getContext().getResources().getDisplayMetrics());
+        for (int i = 0; i < ICONS.length; i++) {
+            SpannableStringBuilder builder = new SpannableStringBuilder();
+            Drawable d = Icon.createWithResource(getContext().getPackageName(), ICONS[i])
+                    .loadDrawable(getContext());
+            d.setTint(Color.BLACK);
+            d.setBounds(0, 0, size, size);
+            ImageSpan span = new ImageSpan(d);
+            builder.append("  ", span, 0);
+            labels[i] = builder;
+            values[i] = getContext().getPackageName() + "/" + ICONS[i];
+        }
+        icon.setEntries(labels);
+        icon.setEntryValues(values);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index f6b8891..266f053 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -23,6 +23,7 @@
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.support.v14.preference.PreferenceFragment;
 import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.PreferenceCategory;
@@ -30,9 +31,9 @@
 import android.support.v7.preference.PreferenceViewHolder;
 import android.view.View;
 
+import com.android.systemui.R;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.PluginPrefs;
-import com.android.systemui.R;
 
 import java.util.List;
 import java.util.Set;
@@ -147,6 +148,12 @@
                                     result.activityInfo.name)));
                 }
             });
+            holder.itemView.setOnLongClickListener(v -> {
+                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+                intent.setData(Uri.fromParts("package", mComponent.getPackageName(), null));
+                getContext().startActivity(intent);
+                return true;
+            });
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ShortcutParser.java b/packages/SystemUI/src/com/android/systemui/tuner/ShortcutParser.java
new file mode 100644
index 0000000..2aa51b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ShortcutParser.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.tuner;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Icon;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ShortcutParser {
+    private static final String SHORTCUTS = "android.app.shortcuts";
+    private static final String SHORTCUT = "shortcut";
+    private static final String INTENT = "intent";
+
+    private final Context mContext;
+    private final String mPkg;
+    private final int mResId;
+    private final String mName;
+    private Resources mResources;
+    private AttributeSet mAttrs;
+
+    public ShortcutParser(Context context, ComponentName component) throws NameNotFoundException {
+        this(context, component.getPackageName(), component.getClassName(),
+                getResId(context, component));
+    }
+
+    private static int getResId(Context context, ComponentName component)
+            throws NameNotFoundException {
+        ActivityInfo i = context.getPackageManager().getActivityInfo(
+                component, PackageManager.GET_META_DATA);
+        int resId = 0;
+        if (i.metaData != null && i.metaData.containsKey(SHORTCUTS)) {
+            resId = i.metaData.getInt(SHORTCUTS);
+        }
+        return resId;
+    }
+
+    public ShortcutParser(Context context, String pkg, String name, int resId) {
+        mContext = context;
+        mPkg = pkg;
+        mResId = resId;
+        mName = name;
+    }
+
+    public List<Shortcut> getShortcuts() {
+        List<Shortcut> list = new ArrayList<>();
+        if (mResId != 0) {
+            try {
+                mResources = mContext.getPackageManager().getResourcesForApplication(mPkg);
+                XmlResourceParser parser = mResources.getXml(mResId);
+                mAttrs = Xml.asAttributeSet(parser);
+                int type;
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                    if (type != XmlPullParser.START_TAG) {
+                        continue;
+                    }
+                    if (parser.getName().equals(SHORTCUT)) {
+                        Shortcut c = parseShortcut(parser);
+                        if (c != null) {
+                            list.add(c);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        return list;
+    }
+
+    private Shortcut parseShortcut(XmlResourceParser parser)
+            throws IOException, XmlPullParserException {
+        final TypedArray sa = mResources.obtainAttributes(mAttrs, R.styleable.Shortcut);
+        Shortcut c = new Shortcut();
+
+        final boolean enabled = sa.getBoolean(R.styleable.Shortcut_enabled, true);
+        if (!enabled) return null;
+        final String id = sa.getString(R.styleable.Shortcut_shortcutId);
+        final int iconResId = sa.getResourceId(R.styleable.Shortcut_icon, 0);
+        final int titleResId = sa.getResourceId(R.styleable.Shortcut_shortcutShortLabel, 0);
+
+        c.pkg = mPkg;
+        c.icon = Icon.createWithResource(mPkg, iconResId);
+        c.id = id;
+        c.label = mResources.getString(titleResId);
+        c.name = mName;
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_TAG) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (parser.getName().equals(INTENT)) {
+                c.intent = Intent.parseIntent(mResources, parser, mAttrs);
+            }
+        }
+        return c.intent != null ? c : null;
+    }
+
+    public static class Shortcut {
+        public Intent intent;
+        public String label;
+        public Icon icon;
+        public String pkg;
+        public String id;
+        public String name;
+
+        public static Shortcut create(Context context, String value) {
+            String[] sp = value.split("::");
+            try {
+                for (Shortcut shortcut : new ShortcutParser(context,
+                        new ComponentName(sp[0], sp[1])).getShortcuts()) {
+                    if (shortcut.id.equals(sp[2])) {
+                        return shortcut;
+                    }
+                }
+            } catch (NameNotFoundException e) {
+            }
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append(pkg);
+            builder.append("::");
+            builder.append(name);
+            builder.append("::");
+            builder.append(id);
+            return builder.toString();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ThemePreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ThemePreference.java
deleted file mode 100644
index a068172..0000000
--- a/packages/SystemUI/src/com/android/systemui/tuner/ThemePreference.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.tuner;
-
-import android.app.AlertDialog;
-import android.app.UiModeManager;
-import android.content.Context;
-import android.os.SystemProperties;
-import android.support.v7.preference.ListPreference;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-
-import com.android.systemui.R;
-
-import libcore.util.Objects;
-
-import com.google.android.collect.Lists;
-
-import java.io.File;
-import java.util.ArrayList;
-
-public class ThemePreference extends ListPreference {
-
-    public ThemePreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public void onAttached() {
-        super.onAttached();
-        String def = SystemProperties.get("ro.boot.vendor.overlay.theme");
-        if (TextUtils.isEmpty(def)) {
-            def = getContext().getString(R.string.default_theme);
-        }
-        String[] fileList = new File("/vendor/overlay").list();
-        ArrayList<String> options = fileList != null
-                ? Lists.newArrayList(fileList) : new ArrayList<>();
-        if (!options.contains(def)) {
-            options.add(0, def);
-        }
-        String[] list = options.toArray(new String[options.size()]);
-        setVisible(options.size() > 1);
-        setEntries(list);
-        setEntryValues(list);
-        updateValue();
-    }
-
-    private void updateValue() {
-        setValue(getContext().getSystemService(UiModeManager.class).getTheme());
-    }
-
-    @Override
-    protected void notifyChanged() {
-        super.notifyChanged();
-        if (!Objects.equal(getValue(),
-                getContext().getSystemService(UiModeManager.class).getTheme())) {
-            new AlertDialog.Builder(getContext())
-                    .setTitle(R.string.change_theme_reboot)
-                    .setPositiveButton(com.android.internal.R.string.global_action_restart, (d, i)
-                            -> getContext().getSystemService(UiModeManager.class)
-                            .setTheme(getValue()))
-                    .setNegativeButton(android.R.string.cancel, (d, i) -> updateValue())
-                    .show();
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 565ac08..fb94061 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -33,6 +33,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -126,6 +127,12 @@
         return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
     }
 
+    public String getValue(String setting, String def) {
+        String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
+        if (ret == null) return def;
+        return ret;
+    }
+
     public void setValue(String setting, int value) {
          Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 2c90e62..9a16d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -43,6 +43,7 @@
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.SystemUI;
+import com.android.systemui.util.NotificationChannels;
 
 import java.util.List;
 
@@ -206,6 +207,7 @@
                         .setStyle(new Notification.BigTextStyle().bigText(text))
                         .setVisibility(Notification.VISIBILITY_PUBLIC)
                         .setLocalOnly(true)
+                        .setChannel(NotificationChannels.STORAGE)
                         .setCategory(Notification.CATEGORY_SYSTEM)
                         .setDeleteIntent(buildSnoozeIntent(fsUuid));
                 SystemUI.overrideNotificationAppName(mContext, builder);
@@ -225,6 +227,7 @@
                     R.string.ext_media_unsupported_notification_message, disk.getDescription());
 
             Notification.Builder builder = new Notification.Builder(mContext)
+                    .setChannel(NotificationChannels.STORAGE)
                     .setSmallIcon(getSmallIcon(disk, VolumeInfo.STATE_UNMOUNTABLE))
                     .setColor(mContext.getColor(R.color.system_notification_accent_color))
                     .setContentTitle(title)
@@ -331,7 +334,6 @@
 
         return buildNotificationBuilder(vol, title, text)
                 .setCategory(Notification.CATEGORY_PROGRESS)
-                .setPriority(Notification.PRIORITY_LOW)
                 .setOngoing(true)
                 .build();
     }
@@ -360,7 +362,6 @@
                             buildUnmountPendingIntent(vol)))
                     .setContentIntent(initIntent)
                     .setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()))
-                    .setCategory(Notification.CATEGORY_SYSTEM)
                     .build();
 
         } else {
@@ -377,8 +378,7 @@
                             mContext.getString(R.string.ext_media_unmount_action),
                             buildUnmountPendingIntent(vol)))
                     .setContentIntent(browseIntent)
-                    .setCategory(Notification.CATEGORY_SYSTEM)
-                    .setPriority(Notification.PRIORITY_LOW);
+                    .setCategory(Notification.CATEGORY_SYSTEM);
             // Non-adoptable disks can't be snoozed.
             if (disk.isAdoptable()) {
                 builder.setDeleteIntent(buildSnoozeIntent(vol.getFsUuid()));
@@ -402,7 +402,6 @@
 
         return buildNotificationBuilder(vol, title, text)
                 .setCategory(Notification.CATEGORY_PROGRESS)
-                .setPriority(Notification.PRIORITY_LOW)
                 .setOngoing(true)
                 .build();
     }
@@ -485,8 +484,8 @@
                 .setStyle(new Notification.BigTextStyle().bigText(text))
                 .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setLocalOnly(true)
+                .setChannel(NotificationChannels.STORAGE)
                 .setCategory(Notification.CATEGORY_PROGRESS)
-                .setPriority(Notification.PRIORITY_LOW)
                 .setProgress(100, status, false)
                 .setOngoing(true);
         SystemUI.overrideNotificationAppName(mContext, builder);
@@ -537,7 +536,7 @@
                 .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setLocalOnly(true)
                 .setCategory(Notification.CATEGORY_SYSTEM)
-                .setPriority(Notification.PRIORITY_LOW)
+                .setChannel(NotificationChannels.STORAGE)
                 .setAutoCancel(true);
         SystemUI.overrideNotificationAppName(mContext, builder);
 
@@ -564,6 +563,7 @@
     private Notification.Builder buildNotificationBuilder(VolumeInfo vol, CharSequence title,
             CharSequence text) {
         Notification.Builder builder = new Notification.Builder(mContext)
+                .setChannel(NotificationChannels.STORAGE)
                 .setSmallIcon(getSmallIcon(vol.getDisk(), vol.getState()))
                 .setColor(mContext.getColor(R.color.system_notification_accent_color))
                 .setContentTitle(title)
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
new file mode 100644
index 0000000..6bb8aea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+
+import android.content.Context;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+import java.util.Arrays;
+
+public class NotificationChannels extends SystemUI {
+    public static String ALERTS      = "ALR";
+    public static String SCREENSHOTS = "SCN";
+    public static String SECURITY    = "SEC";
+    public static String USER        = "USR";
+    public static String STORAGE     = "DSK";
+
+    @VisibleForTesting
+    static void createAll(Context context) {
+        final NotificationManager nm = context.getSystemService(NotificationManager.class);
+        nm.createNotificationChannels(Arrays.asList(
+                new NotificationChannel(
+                        ALERTS,
+                        context.getString(R.string.notification_channel_alerts),
+                        NotificationManager.IMPORTANCE_HIGH),
+                new NotificationChannel(
+                        SCREENSHOTS,
+                        context.getString(R.string.notification_channel_screenshot),
+                        NotificationManager.IMPORTANCE_DEFAULT),
+                new NotificationChannel(
+                        SECURITY,
+                        context.getString(R.string.notification_channel_security),
+                        NotificationManager.IMPORTANCE_HIGH),
+                new NotificationChannel(
+                        USER,
+                        context.getString(R.string.notification_channel_user_status),
+                        NotificationManager.IMPORTANCE_MIN),
+                new NotificationChannel(
+                        STORAGE,
+                        context.getString(R.string.notification_channel_storage),
+                        NotificationManager.IMPORTANCE_LOW)
+                ));
+    }
+
+    @Override
+    public void start() {
+        createAll(mContext);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
index 1f0ee57..36c673c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
@@ -25,7 +25,6 @@
 import java.io.PrintWriter;
 
 public interface VolumeComponent extends DemoMode {
-    ZenModeController getZenController();
     void dismissNow();
     void onConfigurationChanged(Configuration newConfig);
     void dump(FileDescriptor fd, PrintWriter pw, String[] args);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index d23ebc1..d057d863 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -127,6 +127,7 @@
 
     private boolean mShowing;
     private boolean mExpanded;
+    private boolean mShowA11yStream;
 
     private int mActiveStream;
     private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
@@ -244,7 +245,6 @@
             if (!AudioSystem.isSingleVolume(mContext)) {
                 addRow(AudioManager.STREAM_RING,
                         R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true);
-
                 addRow(AudioManager.STREAM_ALARM,
                         R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, false);
                 addRow(AudioManager.STREAM_VOICE_CALL,
@@ -253,6 +253,8 @@
                         R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false);
                 addRow(AudioManager.STREAM_SYSTEM,
                         R.drawable.ic_volume_system, R.drawable.ic_volume_system_mute, false);
+                addRow(AudioManager.STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
+                        R.drawable.ic_volume_accessibility, true);
             }
         } else {
             addExistingRows();
@@ -307,10 +309,24 @@
     }
 
     private void addRow(int stream, int iconRes, int iconMuteRes, boolean important) {
+        addRow(stream, iconRes, iconMuteRes, important, false);
+    }
+
+    private void addRow(int stream, int iconRes, int iconMuteRes, boolean important,
+            boolean dynamic) {
         VolumeRow row = new VolumeRow();
         initRow(row, stream, iconRes, iconMuteRes, important);
-        mDialogRowsView.addView(row.view);
-        mRows.add(row);
+        int rowSize;
+        int viewSize;
+        if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1
+                && (viewSize = mDialogRowsView.getChildCount()) > 1) {
+            // A11y Stream should be the last in the list
+            mDialogRowsView.addView(row.view, viewSize - 2);
+            mRows.add(rowSize - 2, row);
+        } else {
+            mDialogRowsView.addView(row.view);
+            mRows.add(row);
+        }
     }
 
     private void addExistingRows() {
@@ -592,6 +608,9 @@
     }
 
     private boolean shouldBeVisibleH(VolumeRow row, boolean isActive) {
+        if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
+            return mShowA11yStream;
+        }
         return mExpanded && row.view.getVisibility() == View.VISIBLE
                 || (mExpanded && (row.important || isActive))
                 || !mExpanded && isActive;
@@ -644,7 +663,8 @@
             if (!ss.dynamic) continue;
             mDynamic.put(stream, true);
             if (findRow(stream) == null) {
-                addRow(stream, R.drawable.ic_volume_remote, R.drawable.ic_volume_remote_mute, true);
+                addRow(stream, R.drawable.ic_volume_remote, R.drawable.ic_volume_remote_mute, true,
+                        true);
             }
         }
 
@@ -1009,6 +1029,14 @@
         public void onShowSafetyWarning(int flags) {
             showSafetyWarningH(flags);
         }
+
+        @Override
+        public void onAccessibilityModeChanged(Boolean showA11yStream) {
+            boolean show = showA11yStream == null ? false : showA11yStream;
+            mShowA11yStream = show;
+            updateRowsH(getActiveRow());
+
+        }
     };
 
     private final ZenModePanel.Callback mZenPanelCallback = new ZenModePanel.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index f195a0b..137a12f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -16,20 +16,17 @@
 
 package com.android.systemui.volume;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.media.AudioManager;
 import android.media.VolumePolicy;
 import android.os.Bundle;
 import android.os.Handler;
-import android.provider.Settings;
-import android.util.Log;
 import android.view.WindowManager;
 
-import com.android.systemui.R;
+import com.android.systemui.ActivityStarter;
+import com.android.systemui.Dependency;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -37,11 +34,9 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.volume.car.CarVolumeDialogController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.reflect.Constructor;
 
 /**
  * Implementation of VolumeComponent backed by the new volume dialog.
@@ -69,15 +64,14 @@
             400    // vibrateToSilentDebounce
     );
 
-    public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler,
-            ZenModeController zen) {
+    public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) {
         mSysui = sysui;
         mContext = context;
         mController = SystemUIFactory.getInstance().createVolumeDialogController(context, null);
         mController.setUserActivityListener(this);
-        mZenModeController = zen;
+        mZenModeController = Dependency.get(ZenModeController.class);
         mDialog = new VolumeDialog(context, WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY,
-                mController, zen, mVolumeDialogCallback);
+                mController, mZenModeController, mVolumeDialogCallback);
         applyConfiguration();
         TunerService.get(mContext).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
                 VOLUME_SILENT_DO_NOT_DISTURB);
@@ -134,11 +128,6 @@
     }
 
     @Override
-    public ZenModeController getZenController() {
-        return mZenModeController;
-    }
-
-    @Override
     public void onConfigurationChanged(Configuration newConfig) {
         // noop
     }
@@ -166,7 +155,7 @@
     }
 
     private void startSettings(Intent intent) {
-        mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(intent,
+        Dependency.get(ActivityStarter.class).startActivity(intent,
                 true /* onlyProvisioned */, true /* dismissShade */);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index 0e5ff43..276b7c3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -75,13 +75,13 @@
         STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
         STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf);
         STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music);
+        STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
         STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification);
         STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring);
         STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system);
         STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced);
         STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts);
         STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call);
-        STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
     }
 
     private final HandlerThread mWorkerThread;
@@ -98,6 +98,7 @@
     private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
     private final Vibrator mVibrator;
     private final boolean mHasVibrator;
+    private boolean mShowA11yStream;
 
     private boolean mDestroyed;
     private VolumePolicy mVolumePolicy;
@@ -204,6 +205,7 @@
         pw.print("  mHasVibrator: "); pw.println(mHasVibrator);
         pw.print("  mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
                 .values());
+        pw.print("  mShowA11yStream: "); pw.println(mShowA11yStream);
         pw.println();
         mMediaSessions.dump(pw);
     }
@@ -301,6 +303,10 @@
         mCallbacks.onShowSafetyWarning(flags);
     }
 
+    private void onAccessibilityModeChanged(Boolean showA11yStream) {
+        mCallbacks.onAccessibilityModeChanged(showA11yStream);
+    }
+
     private boolean checkRoutedToBluetoothW(int stream) {
         boolean changed = false;
         if (stream == AudioManager.STREAM_MUSIC) {
@@ -570,13 +576,16 @@
             switch (mode) {
                 case VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME:
                     // "legacy" mode
+                    mShowA11yStream = false;
                     break;
                 case VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME:
+                    mShowA11yStream = true;
                     break;
                 default:
                     Log.e(TAG, "Invalid accessibility mode " + mode);
                     break;
             }
+            mWorker.obtainMessage(W.ACCESSIBILITY_MODE_CHANGED, mShowA11yStream).sendToTarget();
         }
     }
 
@@ -595,6 +604,7 @@
         private static final int NOTIFY_VISIBLE = 12;
         private static final int USER_ACTIVITY = 13;
         private static final int SHOW_SAFETY_WARNING = 14;
+        private static final int ACCESSIBILITY_MODE_CHANGED = 15;
 
         W(Looper looper) {
             super(looper);
@@ -617,6 +627,7 @@
                 case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
                 case USER_ACTIVITY: onUserActivityW(); break;
                 case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
+                case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);
             }
         }
     }
@@ -743,6 +754,19 @@
                 });
             }
         }
+
+        @Override
+        public void onAccessibilityModeChanged(Boolean showA11yStream) {
+            boolean show = showA11yStream == null ? false : showA11yStream;
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onAccessibilityModeChanged(show);
+                    }
+                });
+            }
+        }
     }
 
 
@@ -1004,6 +1028,7 @@
                         .append('[').append(ss.levelMin).append("..").append(ss.levelMax)
                         .append(']');
                 if (ss.muted) sb.append(" [MUTED]");
+                if (ss.dynamic) sb.append(" [DYNAMIC]");
             }
             sep(sb, indent); sb.append("ringerModeExternal:").append(ringerModeExternal);
             sep(sb, indent); sb.append("ringerModeInternal:").append(ringerModeInternal);
@@ -1037,6 +1062,7 @@
         void onShowSilentHint();
         void onScreenOff();
         void onShowSafetyWarning(int flags);
+        void onAccessibilityModeChanged(Boolean showA11yStream);
     }
 
     public interface UserActivityListener {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 73d9ea7..02969e4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -42,8 +42,7 @@
     public void start() {
         mEnabled = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
         if (!mEnabled) return;
-        final ZenModeController zenController = new ZenModeControllerImpl(mContext, mHandler);
-        mVolumeComponent = new VolumeDialogComponent(this, mContext, null, zenController);
+        mVolumeComponent = new VolumeDialogComponent(this, mContext, null);
         putComponent(VolumeComponent.class, getVolumeComponent());
         setDefaultVolumeController();
     }
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index e96ea19..c627e22 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -46,7 +46,8 @@
     android-support-test \
     mockito-updated-target-minus-junit4 \
     SystemUI-proto \
-    SystemUI-tags
+    SystemUI-tags \
+    legacy-android-test
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
 
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index dec8ba6..90e9321 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -17,6 +17,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.systemui.tests">
 
+    <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
@@ -28,6 +29,10 @@
     <uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
     <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
new file mode 100644
index 0000000..fb4b6bd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.os.Looper;
+
+import com.android.systemui.statusbar.policy.FlashlightController;
+
+import org.junit.Test;
+
+import java.io.PrintWriter;
+
+public class DependencyTest extends SysuiTestCase {
+
+    @Test
+    public void testClassDependency() {
+        FlashlightController f = mock(FlashlightController.class);
+        injectTestDependency(FlashlightController.class, f);
+        assertEquals(f, Dependency.get(FlashlightController.class));
+    }
+
+    @Test
+    public void testStringDependency() {
+        Looper l = Looper.getMainLooper();
+        injectTestDependency(Dependency.BG_LOOPER, l);
+        assertEquals(l, Dependency.get(Dependency.BG_LOOPER));
+    }
+
+    @Test
+    public void testDump() {
+        Dumpable d = mock(Dumpable.class);
+        injectTestDependency("test", d);
+        Dependency.get("test");
+        mDependency.dump(null, mock(PrintWriter.class), null);
+        verify(d).dump(eq(null), any(), eq(null));
+    }
+
+    @Test
+    public void testConfigurationChanged() {
+        ConfigurationChangedReceiver d = mock(ConfigurationChangedReceiver.class);
+        injectTestDependency("test", d);
+        Dependency.get("test");
+        mDependency.onConfigurationChanged(null);
+        verify(d).onConfigurationChanged(eq(null));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index d1d7520..f258e5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -15,11 +15,14 @@
  */
 package com.android.systemui;
 
+import static org.mockito.Mockito.mock;
+
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
+import android.util.ArrayMap;
 
 import com.android.systemui.utils.TestableContext;
 import com.android.systemui.utils.leaks.Tracker;
@@ -34,11 +37,16 @@
 
     private Handler mHandler;
     protected TestableContext mContext;
+    protected TestDependency mDependency;
 
     @Before
     public void SysuiSetup() throws Exception {
         System.setProperty("dexmaker.share_classloader", "true");
         mContext = new TestableContext(InstrumentationRegistry.getTargetContext(), this);
+        SystemUIFactory.createFromConfig(mContext);
+        mDependency = new TestDependency();
+        mDependency.mContext = mContext;
+        mDependency.start();
     }
 
     @After
@@ -78,11 +86,37 @@
         return null;
     }
 
+    public void injectMockDependency(Class<?> cls) {
+        mDependency.injectTestDependency(cls.getName(), mock(cls));
+    }
+
+    public void injectTestDependency(Class<?> cls, Object obj) {
+        mDependency.injectTestDependency(cls.getName(), obj);
+    }
+
+    public void injectTestDependency(String key, Object obj) {
+        mDependency.injectTestDependency(key, obj);
+    }
+
     public static final class EmptyRunnable implements Runnable {
         public void run() {
         }
     }
 
+    public static class TestDependency extends Dependency {
+        private final ArrayMap<String, Object> mObjs = new ArrayMap<>();
+
+        private void injectTestDependency(String key, Object obj) {
+            mObjs.put(key, obj);
+        }
+
+        @Override
+        protected <T> T createDependency(String cls) {
+            if (mObjs.containsKey(cls)) return (T) mObjs.get(cls);
+            return super.createDependency(cls);
+        }
+    }
+
     public static final class Idler implements MessageQueue.IdleHandler {
         private final Runnable mCallback;
         private boolean mIdle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
index be6290b..76bb6c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
@@ -150,4 +150,28 @@
         mVisualStabilityManager.onReorderingFinished();
         assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
     }
+
+    @Test
+    public void testPulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+        mVisualStabilityManager.setPulsing(false);
+        assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+    }
+
+    @Test
+    public void testReorderingAllowedChanges_Pulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+        mVisualStabilityManager.setPulsing(false);
+        assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+    }
+
+    @Test
+    public void testCallBackCalled_Pulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+        mVisualStabilityManager.setPulsing(false);
+        verify(mCallback).onReorderingAllowed();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
index d529ee1..3715df2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
@@ -113,8 +113,7 @@
         waitForIdleSync(mPluginInstanceManager.mPluginHandler);
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
 
-        verify(mMockListener, Mockito.never()).onPluginConnected(
-                ArgumentCaptor.forClass(Plugin.class).capture());
+        verify(mMockListener, Mockito.never()).onPluginConnected(any(), any());
     }
 
     @Test
@@ -124,7 +123,7 @@
         // Verify startup lifecycle
         verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
                 ArgumentCaptor.forClass(Context.class).capture());
-        verify(mMockListener).onPluginConnected(ArgumentCaptor.forClass(Plugin.class).capture());
+        verify(mMockListener).onPluginConnected(any(), any());
     }
 
     @Test
@@ -154,8 +153,7 @@
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
 
         // Plugin shouldn't be connected because it is the wrong version.
-        verify(mMockListener, Mockito.never()).onPluginConnected(
-                ArgumentCaptor.forClass(Plugin.class).capture());
+        verify(mMockListener, Mockito.never()).onPluginConnected(any(), any());
         verify(nm).notifyAsUser(eq(TestPlugin.class.getName()), eq(SystemMessage.NOTE_PLUGIN),
                 any(), eq(UserHandle.ALL));
     }
@@ -176,8 +174,7 @@
         verify(sMockPlugin, Mockito.times(2)).onCreate(
                 ArgumentCaptor.forClass(Context.class).capture(),
                 ArgumentCaptor.forClass(Context.class).capture());
-        verify(mMockListener, Mockito.times(2)).onPluginConnected(
-                ArgumentCaptor.forClass(Plugin.class).capture());
+        verify(mMockListener, Mockito.times(2)).onPluginConnected(any(), any());
     }
 
     @Test
@@ -193,8 +190,7 @@
         waitForIdleSync(mPluginInstanceManager.mMainHandler);;
 
         // Non-debuggable build should receive no plugins.
-        verify(mMockListener, Mockito.never()).onPluginConnected(
-                ArgumentCaptor.forClass(Plugin.class).capture());
+        verify(mMockListener, Mockito.never()).onPluginConnected(any(), any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
index 8acd6ba..2f6487b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
@@ -29,6 +29,7 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.SecurityController;
@@ -56,6 +57,8 @@
 
     @Before
     public void setUp() {
+        injectTestDependency(SecurityController.class, mSecurityController);
+        injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
         mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
                 new LayoutInflaterBuilder(mContext)
                         .replace("ImageView", TestableImageView.class)
@@ -67,7 +70,7 @@
         mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
         mFooterIcon = (TestableImageView) mRootView.findViewById(R.id.footer_icon);
         mFooterIcon2 = (TestableImageView) mRootView.findViewById(R.id.footer_icon2);
-        mFooter.setHostEnvironment(null, mSecurityController, Looper.getMainLooper());
+        mFooter.setHostEnvironment(null);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index c0d5bbd..5345031 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -18,11 +18,12 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Handler;
+import android.os.Looper;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.FragmentTestCase;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.QSTileHost;
 import com.android.systemui.statusbar.phone.QuickStatusBarHeader;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -42,6 +43,7 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -54,33 +56,25 @@
         super(QSFragment.class);
     }
 
+    @Before
+    public void addLeakCheckDependencies() {
+        injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+        injectMockDependency(UserSwitcherController.class);
+        injectLeakCheckedDependencies(BluetoothController.class, LocationController.class,
+                RotationLockController.class, NetworkController.class, ZenModeController.class,
+                HotspotController.class, CastController.class, FlashlightController.class,
+                UserInfoController.class, KeyguardMonitor.class, SecurityController.class,
+                BatteryController.class, NextAlarmController.class);
+    }
+
     @Test
     public void testListening() {
         QSFragment qs = (QSFragment) mFragment;
         postAndWait(() -> mFragments.dispatchResume());
-        UserSwitcherController userSwitcher = mock(UserSwitcherController.class);
-        KeyguardMonitor keyguardMonitor = getLeakChecker(KeyguardMonitor.class);
-        when(userSwitcher.getKeyguardMonitor()).thenReturn(keyguardMonitor);
-        when(userSwitcher.getUsers()).thenReturn(new ArrayList<>());
-        QSTileHost host = new QSTileHost(mContext,
-                null,
-                getLeakChecker(BluetoothController.class),
-                getLeakChecker(LocationController.class),
-                getLeakChecker(RotationLockController.class),
-                getLeakChecker(NetworkController.class),
-                getLeakChecker(ZenModeController.class),
-                getLeakChecker(HotspotController.class),
-                getLeakChecker(CastController.class),
-                getLeakChecker(FlashlightController.class),
-                userSwitcher,
-                getLeakChecker(UserInfoController.class),
-                keyguardMonitor,
-                getLeakChecker(SecurityController.class),
-                getLeakChecker(BatteryController.class),
-                mock(StatusBarIconController.class),
-                getLeakChecker(NextAlarmController.class));
+        QSTileHost host = new QSTileHost(mContext, null,
+                mock(StatusBarIconController.class));
         qs.setHost(host);
-        Handler h = new Handler(host.getLooper());
+        Handler h = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
 
         qs.setListening(true);
         waitForIdleSync(h);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 3ee1372..4146cb81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -47,13 +47,7 @@
     @Before
     public void setUp() throws Exception {
         mManagers = new ArrayList<>();
-        final NetworkController networkController = Mockito.mock(NetworkController.class);
-        Mockito.when(networkController.getDataSaverController()).thenReturn(
-                Mockito.mock(DataSaverController.class));
-        QSTileHost host = new QSTileHost(mContext, null, null, null, null,
-                networkController, null,
-                Mockito.mock(HotspotController.class), null,
-                null, null, null, null, null, null, null, null);
+        QSTileHost host = new QSTileHost(mContext, null, null);
         mTileService = new TestTileServices(host, Looper.getMainLooper());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
new file mode 100644
index 0000000..3bb9f5f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationContentViewTest {
+
+    NotificationContentView mView;
+    Context mContext;
+
+    @Before
+    public void setup() {
+        ExpandableNotificationRow rowMock = mock(ExpandableNotificationRow.class);
+        when(rowMock.getIntrinsicHeight()).thenReturn(10);
+
+        mContext = InstrumentationRegistry.getTargetContext();
+        mView = new NotificationContentView(mContext, null);
+        mView.setContainingNotification(rowMock);
+        mView.setHeights(10, 20, 30, 40);
+
+        mView.setContractedChild(createViewWithHeight(10));
+        mView.setExpandedChild(createViewWithHeight(20));
+        mView.setHeadsUpChild(createViewWithHeight(30));
+        mView.setAmbientChild(createViewWithHeight(40));
+
+        mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+    }
+
+    private View createViewWithHeight(int height) {
+        View view = new View(mContext, null);
+        view.setMinimumHeight(height);
+        return view;
+    }
+
+    @Test
+    @UiThreadTest
+    public void animationStartType_getsClearedAfterUpdatingVisibilitiesWithoutAnimation() {
+        mView.setHeadsUp(true);
+        mView.setDark(true, false, 0);
+        mView.setDark(false, true, 0);
+        mView.setHeadsUpAnimatingAway(true);
+        Assert.assertFalse(mView.isAnimatingVisibleType());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java
index c65f7150..166fcb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java
@@ -63,7 +63,7 @@
     private static final String TEST_CHANNEL = "test_channel";
     private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
 
-    private NotificationGuts mNotificationGuts;
+    private NotificationInfo mNotificationInfo;
     private final INotificationManager mMockINotificationManager = mock(INotificationManager.class);
     private final PackageManager mMockPackageManager = mock(PackageManager.class);
     private NotificationChannel mNotificationChannel;
@@ -76,7 +76,7 @@
         // Inflate the layout
         final LayoutInflater layoutInflater =
                 LayoutInflater.from(InstrumentationRegistry.getTargetContext());
-        mNotificationGuts = (NotificationGuts) layoutInflater.inflate(R.layout.notification_guts,
+        mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
                 null);
 
         // PackageManager must return a packageInfo and applicationInfo.
@@ -91,7 +91,6 @@
         // mMockStatusBarNotification with a test channel.
         mNotificationChannel = new NotificationChannel(
                 TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
-        when(mMockStatusBarNotification.getNotificationChannel()).thenReturn(mNotificationChannel);
         when(mMockStatusBarNotification.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
     }
 
@@ -99,18 +98,18 @@
     @UiThreadTest
     public void testBindNotification_SetsTextApplicationName() throws Exception {
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
-        final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.pkgname);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
+        final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
     }
 
     @Test
     @UiThreadTest
     public void testBindNotification_SetsTextChannelName() throws Exception {
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
-        final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.channel_name);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
+        final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
 
@@ -118,12 +117,12 @@
     @UiThreadTest
     public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, (View v, int appUid) -> { latch.countDown(); },
-                null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel,
+                (View v, int appUid) -> { latch.countDown(); }, null, null);
 
-        final TextView settingsButton =
-                (TextView) mNotificationGuts.findViewById(R.id.more_settings);
+        final TextView settingsButton = 
+                (TextView) mNotificationInfo.findViewById(R.id.more_settings);
         settingsButton.performClick();
         // Verify that listener was triggered.
         assertEquals(0, latch.getCount());
@@ -133,12 +132,12 @@
     @UiThreadTest
     public void testBindNotification_SetsOnClickListenerForDone() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null,
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null,
                 (View v) -> { latch.countDown(); },
                 null);
 
-        final TextView doneButton = (TextView) mNotificationGuts.findViewById(R.id.done);
+        final TextView doneButton = (TextView) mNotificationInfo.findViewById(R.id.done);
         doneButton.performClick();
         // Verify that listener was triggered.
         assertEquals(0, latch.getCount());
@@ -147,39 +146,39 @@
     @Test
     @UiThreadTest
     public void testHasImportanceChanged_DefaultsToFalse() throws Exception {
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
-        assertFalse(mNotificationGuts.hasImportanceChanged());
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
+        assertFalse(mNotificationInfo.hasImportanceChanged());
     }
 
     @Test
     @UiThreadTest
     public void testHasImportanceChanged_ReturnsTrueAfterButtonChecked() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
         // Find the high button and check it.
-        RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
+        RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance);
         highButton.setChecked(true);
-        assertTrue(mNotificationGuts.hasImportanceChanged());
+        assertTrue(mNotificationInfo.hasImportanceChanged());
     }
 
     @Test
     @UiThreadTest
     public void testImportanceButtonCheckedBasedOnInitialImportance() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
+        RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance);
         assertTrue(highButton.isChecked());
     }
 
     @Test
     @UiThreadTest
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
     }
@@ -188,10 +187,10 @@
     @UiThreadTest
     public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
+        RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance);
         highButton.setChecked(true);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
@@ -200,10 +199,10 @@
     @Test
     @UiThreadTest
     public void testCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception {
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        mNotificationGuts.closeControls(-1, -1, true);
+        mNotificationInfo.closeControls();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
     }
@@ -212,10 +211,10 @@
     @UiThreadTest
     public void testCloseControls_DoesNotUpdateNotificationChannelIfUnspecified() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        mNotificationGuts.closeControls(-1, -1, true);
+        mNotificationInfo.closeControls();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
     }
@@ -224,12 +223,12 @@
     @UiThreadTest
     public void testCloseControls_CallsUpdateNotificationChannelIfChanged() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
+        RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance);
         highButton.setChecked(true);
-        mNotificationGuts.closeControls(-1, -1, true);
+        mNotificationInfo.closeControls();
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
         assertEquals(NotificationManager.IMPORTANCE_HIGH, mNotificationChannel.getImportance());
@@ -239,12 +238,12 @@
     @UiThreadTest
     public void testCloseControls_DoesNotUpdateNotificationChannelIfSaveFalse() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
+        RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance);
         highButton.setChecked(true);
-        mNotificationGuts.closeControls(-1, -1, false);
+        mNotificationInfo.closeControls();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
     }
@@ -253,10 +252,10 @@
     @UiThreadTest
     public void testEnabledSwitchOnByDefault() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
+        Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         assertTrue(enabledSwitch.isChecked());
     }
 
@@ -264,10 +263,10 @@
     @UiThreadTest
     public void testEnabledSwitchVisibleByDefault() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
+        Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         assertEquals(View.VISIBLE, enabledSwitch.getVisibility());
     }
 
@@ -275,10 +274,11 @@
     @UiThreadTest
     public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, Collections.singleton(TEST_PACKAGE_NAME));
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null,
+                Collections.singleton(TEST_PACKAGE_NAME));
 
-        Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
+        Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
     }
 
@@ -286,12 +286,13 @@
     @UiThreadTest
     public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, Collections.singleton(TEST_PACKAGE_NAME));
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null,
+                Collections.singleton(TEST_PACKAGE_NAME));
 
-        Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
+        Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
         enabledSwitch.setChecked(false);
-        mNotificationGuts.closeControls(-1, -1, true);
+        mNotificationInfo.closeControls();
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
     }
@@ -300,14 +301,14 @@
     @UiThreadTest
     public void testEnabledSwitchOverridesOtherButtons() throws Exception {
         mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
-        mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, null, null, null);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
 
-        Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
-        RadioButton lowButton = (RadioButton) mNotificationGuts.findViewById(R.id.low_importance);
+        Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch);
+        RadioButton lowButton = (RadioButton) mNotificationInfo.findViewById(R.id.low_importance);
         lowButton.setChecked(true);
         enabledSwitch.setChecked(false);
-        mNotificationGuts.closeControls(-1, -1, true);
+        mNotificationInfo.closeControls();
         assertEquals(NotificationManager.IMPORTANCE_NONE, mNotificationChannel.getImportance());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
new file mode 100644
index 0000000..7d9e073
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.graphics.drawable.Icon;
+import android.os.Debug;
+import android.os.UserHandle;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class StatusBarIconViewTest extends SysuiTestCase {
+
+    private StatusBarIconView mIconView;
+    private StatusBarIcon mStatusBarIcon = mock(StatusBarIcon.class);
+
+    @Before
+    public void setUp() {
+        mIconView = new StatusBarIconView(getContext(), "slot", null);
+        mStatusBarIcon = new StatusBarIcon(UserHandle.ALL, getContext().getPackageName(),
+                Icon.createWithResource(getContext(), R.drawable.ic_android), 0, 0, "");
+    }
+
+    @Test
+    public void testSetClearsGrayscale() {
+        mIconView.setTag(R.id.icon_is_grayscale, true);
+        mIconView.set(mStatusBarIcon);
+        assertNull(mIconView.getTag(R.id.icon_is_grayscale));
+    }
+
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 34743ff..525a361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -16,12 +16,17 @@
 
 import static org.mockito.Mockito.mock;
 
+import android.content.Context;
+import android.view.WindowManager;
+
 import com.android.systemui.FragmentTestCase;
+import com.android.systemui.assist.AssistManager;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
+import org.junit.Test;
 
 public class NavigationBarFragmentTest extends FragmentTestCase {
 
@@ -35,6 +40,16 @@
         mContext.putComponent(PhoneStatusBar.class, mock(PhoneStatusBar.class));
         mContext.putComponent(Recents.class, mock(Recents.class));
         mContext.putComponent(Divider.class, mock(Divider.class));
+        mContext.addMockSystemService(Context.WINDOW_SERVICE, mock(WindowManager.class));
+    }
+
+    @Test
+    public void testHomeLongPress() {
+        mContext.addMockSystemService(Context.WINDOW_SERVICE, mock(WindowManager.class));
+        NavigationBarFragment navigationBarFragment = (NavigationBarFragment) mFragment;
+
+        postAndWait(() -> mFragments.dispatchResume());
+        navigationBarFragment.onHomeLongClick(navigationBarFragment.getView());
     }
 
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTest.java
new file mode 100644
index 0000000..d82566f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.BaseStatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PhoneStatusBarTest extends SysuiTestCase {
+
+    StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    PhoneStatusBar mPhoneStatusBar;
+
+    @Before
+    public void setup() {
+        mStatusBarKeyguardViewManager = mock(StatusBarKeyguardViewManager.class);
+        mPhoneStatusBar = new TestablePhoneStatusBar(mStatusBarKeyguardViewManager);
+
+        doAnswer(invocation -> {
+            OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
+            onDismissAction.onDismiss();
+            return null;
+        }).when(mStatusBarKeyguardViewManager).dismissWithAction(any(), any(), anyBoolean());
+
+        doAnswer(invocation -> {
+            Runnable runnable = (Runnable) invocation.getArguments()[0];
+            runnable.run();
+            return null;
+        }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
+    }
+
+    @Test
+    public void executeRunnableDismissingKeyguard_nullRunnable_showingAndOccluded() {
+        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
+
+        mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+    }
+
+    @Test
+    public void executeRunnableDismissingKeyguard_nullRunnable_showing() {
+        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+
+        mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+    }
+
+    @Test
+    public void executeRunnableDismissingKeyguard_nullRunnable_notShowing() {
+        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+        when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
+
+        mPhoneStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+    }
+
+    static class TestablePhoneStatusBar extends PhoneStatusBar {
+        public TestablePhoneStatusBar(StatusBarKeyguardViewManager man) {
+            mStatusBarKeyguardViewManager = man;
+        }
+
+        @Override
+        protected BaseStatusBar.H createHandler() {
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 7b56ea3..b544d9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -18,7 +18,6 @@
 import android.os.HandlerThread;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.SubscriptionInfo;
-import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
@@ -37,6 +36,8 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.assertEquals;
 
+import static org.mockito.Matchers.eq;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class CallbackHandlerTest {
@@ -110,8 +111,9 @@
         int qsType = R.drawable.ic_qs_signal_1x;
         boolean wide = true;
         int subId = 5;
+        boolean roaming = true;
         mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription,
-                description, wide, subId);
+                description, wide, subId, roaming);
         waitForCallbacks();
 
         ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
@@ -127,7 +129,7 @@
         Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(),
                 qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(),
                 outArg.capture(), typeContentArg.capture(), descArg.capture(), wideArg.capture(),
-                subIdArg.capture());
+                subIdArg.capture(), eq(roaming));
         assertEquals(status, statusArg.getValue());
         assertEquals(qs, qsArg.getValue());
         assertEquals(type, (int) typeIconArg.getValue());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 6fe7768..0e5f513 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -19,6 +19,7 @@
 import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
+import android.net.NetworkScoreManager;
 import android.net.wifi.WifiManager;
 import android.os.Looper;
 import android.telephony.PhoneStateListener;
@@ -54,6 +55,7 @@
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -79,6 +81,7 @@
     protected Config mConfig;
     protected CallbackHandler mCallbackHandler;
     protected SubscriptionDefaults mMockSubDefaults;
+    protected NetworkScoreManager mMockNetworkScoreManager;
 
     protected int mSubId;
 
@@ -105,6 +108,8 @@
         mMockCm = mock(ConnectivityManager.class);
         mMockSubDefaults = mock(SubscriptionDefaults.class);
         mNetCapabilities = new NetworkCapabilities();
+        mMockNetworkScoreManager = mock(NetworkScoreManager.class);
+
         when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
         when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn(
                 new NetworkCapabilities[] { mNetCapabilities });
@@ -115,10 +120,11 @@
         mConfig = new Config();
         mConfig.hspaDataDistinguishable = true;
         mCallbackHandler = mock(CallbackHandler.class);
-        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
+        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
+                mMockTm, mMockWm, mMockSm,
                 mConfig, Looper.getMainLooper(), mCallbackHandler,
                 mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
-                mMockSubDefaults);
+                mMockSubDefaults, mock(DeviceProvisionedController.class));
         setupNetworkController();
 
         // Trigger blank callbacks to always get the current state (some tests don't trigger
@@ -157,10 +163,11 @@
     protected NetworkControllerImpl setUpNoMobileData() {
       when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
       NetworkControllerImpl networkControllerNoMobile
-              = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
-                        mConfig, mContext.getMainLooper(), mCallbackHandler,
+              = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager, mMockTm,
+                        mMockWm, mMockSm, mConfig, mContext.getMainLooper(), mCallbackHandler,
                         mock(AccessPointControllerImpl.class),
-                        mock(DataUsageController.class), mMockSubDefaults);
+                        mock(DataUsageController.class), mMockSubDefaults,
+                        mock(DeviceProvisionedController.class));
 
       setupNetworkController();
 
@@ -291,7 +298,7 @@
                     iconArg.capture(),
                     anyInt(),
                     typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
-                    anyString(), anyString(), anyBoolean(), anyInt());
+                    anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean());
         IconState iconState = iconArg.getValue();
         assertEquals("Visibility in, quick settings", visible, iconState.visible);
         assertEquals("Signal icon in, quick settings", icon, iconState.icon);
@@ -303,6 +310,11 @@
     }
 
     protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon) {
+        verifyLastMobileDataIndicators(visible, icon, typeIcon, false);
+    }
+
+    protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
+            boolean roaming) {
         ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
         ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
 
@@ -312,9 +324,10 @@
                 any(),
                 typeIconArg.capture(),
                 anyInt(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyBoolean(),
-                anyInt());
+                anyInt(), eq(roaming));
         IconState iconState = iconArg.getValue();
 
+        assertEquals("Signal icon in status bar", icon, iconState.icon);
         assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue());
         assertEquals("Visibility in status bar", visible, iconState.visible);
     }
@@ -335,7 +348,7 @@
                 qsTypeIconArg.capture(),
                 dataInArg.capture(),
                 dataOutArg.capture(),
-                anyString(), anyString(), anyBoolean(), anyInt());
+                anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean());
 
         IconState iconState = iconArg.getValue();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 4f961ab..d7f961c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -1,5 +1,7 @@
 package com.android.systemui.statusbar.policy;
 
+import static org.mockito.Mockito.mock;
+
 import android.net.NetworkCapabilities;
 import android.os.Looper;
 import android.support.test.runner.AndroidJUnit4;
@@ -23,19 +25,6 @@
     }
 
     @Test
-    public void testRoamingDataIcon() {
-        setupDefaultSignal();
-        setGsmRoaming(true);
-
-        verifyLastMobileDataIndicators(true,
-                TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][DEFAULT_LEVEL],
-                TelephonyIcons.ROAMING_ICON,
-                true,
-                TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[1][DEFAULT_LEVEL],
-                TelephonyIcons.QS_DATA_R, false, false);
-    }
-
-    @Test
     public void test2gDataIcon() {
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
@@ -98,10 +87,12 @@
     public void test4gDataIcon() {
         // Switch to showing 4g icon and re-initialize the NetworkController.
         mConfig.show4gForLte = true;
-        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
+        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
+                mMockTm, mMockWm, mMockSm,
                 mConfig, Looper.getMainLooper(), mCallbackHandler,
-                Mockito.mock(AccessPointControllerImpl.class),
-                Mockito.mock(DataUsageController.class), mMockSubDefaults);
+                mock(AccessPointControllerImpl.class),
+                mock(DataUsageController.class), mMockSubDefaults,
+                mock(DeviceProvisionedController.class));
         setupNetworkController();
 
         setupDefaultSignal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 4560aa7..2c0f9c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -53,10 +53,11 @@
         // Turn off mobile network support.
         Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
         // Create a new NetworkController as this is currently handled in constructor.
-        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
+        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
+                mMockTm, mMockWm, mMockSm,
                 mConfig, Looper.getMainLooper(), mCallbackHandler,
                 mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
-                mMockSubDefaults);
+                mMockSubDefaults, mock(DeviceProvisionedController.class));
         setupNetworkController();
 
         verifyLastMobileDataIndicators(false, 0, 0);
@@ -107,10 +108,11 @@
         // Turn off mobile network support.
         Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
         // Create a new NetworkController as this is currently handled in constructor.
-        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
+        mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
+                mMockTm, mMockWm, mMockSm,
                 mConfig, Looper.getMainLooper(), mCallbackHandler,
                 mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
-                mMockSubDefaults);
+                mMockSubDefaults, mock(DeviceProvisionedController.class));
         setupNetworkController();
 
         // No Subscriptions.
@@ -156,13 +158,12 @@
         for (int testStrength = SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                 testStrength <= SignalStrength.SIGNAL_STRENGTH_GREAT; testStrength++) {
             setupDefaultSignal();
-            setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
             setGsmRoaming(true);
             setLevel(testStrength);
 
             verifyLastMobileDataIndicators(true,
                     TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][testStrength],
-                    TelephonyIcons.ROAMING_ICON);
+                    DEFAULT_ICON, true);
         }
     }
 
@@ -177,7 +178,7 @@
 
             verifyLastMobileDataIndicators(true,
                     TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][testStrength],
-                    TelephonyIcons.ROAMING_ICON);
+                    TelephonyIcons.DATA_1X[1][0 /* No direction */], true);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index ed32f65..9110e0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -1,24 +1,50 @@
 package com.android.systemui.statusbar.policy;
 
 import android.content.Intent;
+import android.graphics.drawable.Drawable;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkKey;
+import android.net.RssiCurve;
+import android.net.ScoredNetwork;
+import android.net.WifiKey;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkScoreCache;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Matchers;
 import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -27,6 +53,15 @@
     private static final int MIN_RSSI = -100;
     private static final int MAX_RSSI = -55;
 
+    private static final int LATCH_TIMEOUT = 2000;
+    private static final String TEST_SSID = "\"Test SSID\"";
+    private static final String TEST_BSSID = "00:00:00:00:00:00";
+
+    private final List<NetworkKey> mRequestedKeys = new ArrayList<>();
+    private CountDownLatch mRequestScoresLatch;
+
+    private SettingOverrider mSettingsOverrider;
+
     @Test
     public void testWifiIcon() {
         String testSsid = "Test SSID";
@@ -47,6 +82,77 @@
     }
 
     @Test
+    public void testBadgedWifiIcon() throws Exception {
+        int testLevel = 1;
+        RssiCurve mockBadgeCurve = mock(RssiCurve.class);
+        Bundle attr = new Bundle();
+        attr.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve);
+        ScoredNetwork score =
+                new ScoredNetwork(
+                        new NetworkKey(new WifiKey(TEST_SSID, TEST_BSSID)),
+                        null,
+                        false /* meteredHint */,
+                        attr);
+
+        // Enable scoring
+        mSettingsOverrider = mContext.getSettingsProvider().acquireOverridesBuilder(this)
+                .addSetting("global", Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, "1")
+                .build();
+
+        setupNetworkScoreManager();
+        mRequestScoresLatch = new CountDownLatch(1);
+        setWifiEnabled(true);
+        setWifiState(true, TEST_SSID, TEST_BSSID);
+        mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+
+        when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) ScoredNetwork.BADGING_SD);
+
+        ArgumentCaptor<WifiNetworkScoreCache> scoreCacheCaptor =
+                ArgumentCaptor.forClass(WifiNetworkScoreCache.class);
+        verify(mMockNetworkScoreManager).registerNetworkScoreCache(
+                anyInt(),
+                scoreCacheCaptor.capture(),
+                Matchers.anyInt());
+        scoreCacheCaptor.getValue().updateScores(Arrays.asList(score));
+
+        setWifiLevel(testLevel);
+        NetworkController.SignalCallback mockCallback =
+                mock(NetworkController.SignalCallback.class);
+        mNetworkController.addCallback(mockCallback);
+
+        ArgumentCaptor<IconState> iconState = ArgumentCaptor.forClass(IconState.class);
+        Mockito.verify(mockCallback).setWifiIndicators(
+                anyBoolean(), iconState.capture(), any(), anyBoolean(), anyBoolean(), any());
+
+        assertEquals("Badged Wifi Resource is set",
+                Utils.WIFI_PIE_FOR_BADGING[testLevel],
+                iconState.getValue().icon);
+        assertEquals("SD Badge is set",
+                Utils.getWifiBadgeResource(ScoredNetwork.BADGING_SD),
+                iconState.getValue().iconOverlay);
+
+        mSettingsOverrider.release();
+    }
+
+    private void setupNetworkScoreManager() {
+        // Capture requested keys and count down latch if present
+        doAnswer(
+                new Answer<Boolean>() {
+                    @Override
+                    public Boolean answer(InvocationOnMock input) {
+                        if (mRequestScoresLatch != null) {
+                            mRequestScoresLatch.countDown();
+                        }
+                        NetworkKey[] keys = (NetworkKey[]) input.getArguments()[0];
+                        for (NetworkKey key : keys) {
+                            mRequestedKeys.add(key);
+                        }
+                        return true;
+                    }
+                }).when(mMockNetworkScoreManager).requestScores(Matchers.<NetworkKey[]>any());
+    }
+
+    @Test
     public void testQsWifiIcon() {
         String testSsid = "Test SSID";
 
@@ -97,7 +203,7 @@
     @Test
     public void testRoamingIconDuringWifi() {
         // Setup normal connection
-        String testSsid = "Test SSID";
+        String testSsid = "\"Test SSID\"";
         int testLevel = 2;
         setWifiEnabled(true);
         setWifiState(true, testSsid);
@@ -109,9 +215,10 @@
         setGsmRoaming(true);
         // Still be on wifi though.
         setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+        setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
         verifyLastMobileDataIndicators(true,
                 TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][DEFAULT_LEVEL],
-                TelephonyIcons.ROAMING_ICON);
+                0, true);
     }
 
     protected void setWifiActivity(int activity) {
@@ -137,12 +244,19 @@
     }
 
     protected void setWifiState(boolean connected, String ssid) {
+        setWifiState(connected, ssid, null);
+    }
+
+    protected void setWifiState(boolean connected, String ssid, String bssid) {
         Intent i = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         NetworkInfo networkInfo = Mockito.mock(NetworkInfo.class);
         Mockito.when(networkInfo.isConnected()).thenReturn(connected);
 
         WifiInfo wifiInfo = Mockito.mock(WifiInfo.class);
         Mockito.when(wifiInfo.getSSID()).thenReturn(ssid);
+        if (bssid != null) {
+            Mockito.when(wifiInfo.getBSSID()).thenReturn(bssid);
+        }
 
         i.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         i.putExtra(WifiManager.EXTRA_WIFI_INFO, wifiInfo);
@@ -166,8 +280,8 @@
         ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                enabledArg.capture(), any(), iconArg.capture(), anyBoolean(), anyBoolean(),
-                descArg.capture());
+                enabledArg.capture(), any(), iconArg.capture(), anyBoolean(),
+                anyBoolean(),  descArg.capture());
         IconState iconState = iconArg.getValue();
         assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue());
         assertEquals("WiFi connected, in quick settings", connected, iconState.visible);
@@ -179,7 +293,8 @@
         ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(), any());
+                anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(),
+                any());
         IconState iconState = iconArg.getValue();
         assertEquals("WiFi visible, in status bar", visible, iconState.visible);
         assertEquals("WiFi signal, in status bar", icon, iconState.icon);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
new file mode 100644
index 0000000..8949598
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.NotificationChannels;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ChannelsTest extends SysuiTestCase {
+    private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
+
+    @Before
+    public void setup() throws Exception {
+        mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, mMockNotificationManager);
+    }
+
+    @Test
+    public void testChannelSetup() {
+        Set<String> ALL_CHANNELS = new ArraySet<>(Arrays.asList(
+                NotificationChannels.ALERTS,
+                NotificationChannels.SCREENSHOTS,
+                NotificationChannels.SECURITY,
+                NotificationChannels.USER,
+                NotificationChannels.STORAGE
+        ));
+        NotificationChannels.createAll(mContext);
+        ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+        verify(mMockNotificationManager).createNotificationChannels(captor.capture());
+        final List<NotificationChannel> list = captor.getValue();
+        assertEquals(ALL_CHANNELS.size(), list.size());
+        list.forEach((chan) -> assertTrue(ALL_CHANNELS.contains(chan.getId())));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
index 0238bf7..b118fdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
@@ -14,9 +14,13 @@
 
 package com.android.systemui.utils.leaks;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.CallbackController;
 
-public class BaseLeakChecker<T> implements CallbackController<T> {
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class BaseLeakChecker<T> implements CallbackController<T>, Dumpable {
 
     private final Tracker mTracker;
 
@@ -37,4 +41,9 @@
     public void removeCallback(T listener) {
         mTracker.getLeakInfo(listener).clearAllocations();
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index fcfe9aa..5497686 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -14,11 +14,16 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.os.Bundle;
+
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
         implements NetworkController {
 
@@ -42,6 +47,21 @@
     }
 
     @Override
+    public void setUserSetupComplete(boolean userSetup) {
+
+    }
+
+    @Override
+    public boolean hasEmergencyCryptKeeperText() {
+        return false;
+    }
+
+    @Override
+    public boolean isRadioOn() {
+        return false;
+    }
+
+    @Override
     public DataSaverController getDataSaverController() {
         return mDataSaverController;
     }
@@ -57,11 +77,6 @@
     }
 
     @Override
-    public void onUserSwitched(int newUserId) {
-
-    }
-
-    @Override
     public AccessPointController getAccessPointController() {
         return null;
     }
@@ -75,4 +90,9 @@
     public boolean hasVoiceCallingFeature() {
         return false;
     }
+
+    @Override
+    public void dispatchDemoCommand(String command, Bundle args) {
+
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
index 13ea385..7581363 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
@@ -53,11 +53,6 @@
     }
 
     @Override
-    public void setUserId(int userId) {
-
-    }
-
-    @Override
     public boolean isZenAvailable() {
         return false;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index 728ed60..c182493 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -118,6 +118,12 @@
         mTrackers.values().forEach(Tracker::verify);
     }
 
+    public void injectLeakCheckedDependencies(Class<?>... cls) {
+        for (Class<?> c : cls) {
+            injectTestDependency(c, getLeakChecker(c));
+        }
+    }
+
     public <T extends CallbackController> T addListening(T mock, Class<T> cls, String tag) {
         doAnswer(new Answer<Void>() {
             @Override
diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk
index 7128b0d..1dea798 100644
--- a/packages/WAPPushManager/tests/Android.mk
+++ b/packages/WAPPushManager/tests/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
index 09b41fd..d8fb7a4 100644
--- a/packages/WallpaperCropper/Android.mk
+++ b/packages/WallpaperCropper/Android.mk
@@ -6,7 +6,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 junit
 
 LOCAL_PACKAGE_NAME := WallpaperCropper
 LOCAL_CERTIFICATE := platform
diff --git a/preloaded-classes b/preloaded-classes
index 86cbb69..2fad5dd 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -823,11 +823,6 @@
 android.graphics.EmbossMaskFilter
 android.graphics.FontFamily
 android.graphics.FontListParser
-android.graphics.FontListParser$Alias
-android.graphics.FontListParser$Axis
-android.graphics.FontListParser$Config
-android.graphics.FontListParser$Family
-android.graphics.FontListParser$Font
 android.graphics.Insets
 android.graphics.Interpolator
 android.graphics.Interpolator$Result
@@ -1001,10 +996,6 @@
 android.hardware.input.InputDeviceIdentifier$1
 android.hardware.input.InputManager
 android.hardware.input.InputManager$InputDevicesChangedListener
-# These cannot be preloaded and need to be refactored into system server. b/17791590, b/21935130
-# android.hardware.location.ActivityRecognitionHardware
-# android.hardware.location.IActivityRecognitionHardware
-# android.hardware.location.IActivityRecognitionHardware$Stub
 android.hardware.location.ContextHubManager
 android.hardware.location.IContextHubService
 android.hardware.location.IContextHubService$Stub
@@ -1847,6 +1838,11 @@
 android.text.DynamicLayout$ChangeWatcher
 android.text.Editable
 android.text.Editable$Factory
+android.text.FontConfig
+android.text.FontConfig$Alias
+android.text.FontConfig$Axis
+android.text.FontConfig$Family
+android.text.FontConfig$Font
 android.text.GetChars
 android.text.GraphicsOperations
 android.text.Html
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index dc92f56..952f851 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3173,9 +3173,9 @@
 
     // These values should never appear in log outputs - they are reserved for
     // internal Tron use.
-    RESERVED_FOR_LOGBUILDER_VIEW = 757;
-    RESERVED_FOR_LOGBUILDER_CATEGORY = 758;
-    RESERVED_FOR_LOGBUILDER_TYPE = 759;
+    RESERVED_FOR_LOGBUILDER_CATEGORY = 757;
+    RESERVED_FOR_LOGBUILDER_TYPE = 758;
+    RESERVED_FOR_LOGBUILDER_SUBTYPE = 759;
 
     // ACTION: "Do not show again" was enabled in the support disclaimer and the
     // user accepted
@@ -3191,6 +3191,161 @@
     // ACTION: Clicking on any search result in Settings.
     ACTION_CLICK_SETTINGS_SEARCH_RESULT = 763;
 
+    // ACTION: Allow Battery optimization for an app
+    APP_SPECIAL_PERMISSION_BATTERY_ALLOW = 764;
+
+    // ACTION: Deny Battery optimization for an app
+    APP_SPECIAL_PERMISSION_BATTERY_DENY = 765;
+
+    // ACTION: Enable Device Admin app
+    APP_SPECIAL_PERMISSION_ADMIN_ALLOW = 766;
+
+    // ACTION: Disable Device Admin app
+    APP_SPECIAL_PERMISSION_ADMIN_DENY = 767;
+
+    // ACTION: Allow "Do Not Disturb access" for an app
+    APP_SPECIAL_PERMISSION_DND_ALLOW = 768;
+
+    // ACTION: Deny "Do Not Disturb access" for an app
+    APP_SPECIAL_PERMISSION_DND_DENY = 769;
+
+    // ACTION: Allow "Draw over other apps" for an app
+    APP_SPECIAL_PERMISSION_APPDRAW_ALLOW = 770;
+
+    // ACTION: Deny "Draw over other apps" for an app
+    APP_SPECIAL_PERMISSION_APPDRAW_DENY = 771;
+
+    // ACTION: Allow "VR helper services" for an app
+    APP_SPECIAL_PERMISSION_VRHELPER_ALLOW = 772;
+
+    // ACTION: Deny "VR helper services" for an app
+    APP_SPECIAL_PERMISSION_VRHELPER_DENY = 773;
+
+    // ACTION: Allow "Modify system settings" for an app
+    APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW = 774;
+
+    // ACTION: Deny "Modify system settings" for an app
+    APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY = 775;
+
+    // ACTION: Allow "Notification access" for an app
+    APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW = 776;
+
+    // ACTION: Deny "Notification access" for an app
+    APP_SPECIAL_PERMISSION_NOTIVIEW_DENY = 777;
+
+    // ACTION: "Premium SMS access" for an app - "ask user" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_ASK = 778;
+
+    // ACTION: "Premium SMS access" for an app - "never allow" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_DENY = 779;
+
+    // ACTION: "Premium SMS access" for an app - "always allow" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_ALWAYS_ALLOW = 780;
+
+    // ACTION: Allow "Unrestricted data access" for an app
+    APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW = 781;
+
+    // ACTION: Deny "Unrestricted data access" for an app
+    APP_SPECIAL_PERMISSION_UNL_DATA_DENY = 782;
+
+    // ACTION: Allow "Usage access" for an app
+    APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW = 783;
+
+    // ACTION: Deny "Usage access" for an app
+    APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY = 784;
+
+    // OPEN: Settings > Apps > Default Apps > Default browser
+    DEFAULT_BROWSER_PICKER = 785;
+
+    // OPEN: Settings > Apps > Default Apps > Default emergency app
+    DEFAULT_EMERGENCY_APP_PICKER = 786;
+
+    // OPEN: Settings > Apps > Default Apps > Default home
+    DEFAULT_HOME_PICKER = 787;
+
+    // OPEN: Settings > Apps > Default Apps > Default phone
+    DEFAULT_PHONE_PICKER = 788;
+
+    // OPEN: Settings > Apps > Default Apps > Default sms
+    DEFAULT_SMS_PICKER = 789;
+
+    // OPEN: Settings > Apps > Default Apps > Default notification assistant
+    DEFAULT_NOTIFICATION_ASSISTANT = 790;
+
+    // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection
+    DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791;
+
+
+    // OPEN: Settings > Apps > Default Apps > Default auto-fill app
+    DEFAULT_AUTO_FILL_PICKER = 792;
+
+    // These values should never appear in log outputs - they are reserved for
+    // internal Tron use.
+    NOTIFICATION_SINCE_CREATE_MILLIS = 793;
+    NOTIFICATION_SINCE_VISIBLE_MILLIS = 794;
+    NOTIFICATION_SINCE_UPDATE_MILLIS = 795;
+    NOTIFICATION_ID = 796;
+    NOTIFICATION_TAG = 797;
+    NOTIFICATION_SHADE_INDEX = 798;
+    RESERVED_FOR_LOGBUILDER_NAME = 799;
+
+    // OPEN: QS NFC tile shown
+    // ACTION: QS NFC tile tapped
+    // CATEGORY: QUICK_SETTINGS
+    QS_NFC = 800;
+
+    // These values should never appear in log outputs - they are reserved for
+    // internal Tron use.
+    RESERVED_FOR_LOGBUILDER_BUCKET = 801;
+    RESERVED_FOR_LOGBUILDER_VALUE = 802;
+    RESERVED_FOR_LOGBUILDER_COUNTER = 803;
+    RESERVED_FOR_LOGBUILDER_HISTOGRAM = 804;
+    RESERVED_FOR_LOGBUILDER_TIMESTAMP = 805;
+    RESERVED_FOR_LOGBUILDER_PACKAGENAME = 806;
+
+    // ACTION: "Force stop" action on an app
+    ACTION_APP_FORCE_STOP = 807;
+
+    // OPEN: Settings > Apps > Gear > Special Access > Install other apps
+    // CATEGORY: SETTINGS
+    // OS: 8.0
+    MANAGE_EXTERNAL_SOURCES = 808;
+
+    // ACTION: Logged when terms activity finishes.
+    // TIME: Indicates time taken by terms activity to finish in MS.
+    PROVISIONING_TERMS_ACTIVITY_TIME_MS = 809;
+
+    // Indicates number of terms displayed on the terms screen.
+    PROVISIONING_TERMS_COUNT = 810;
+
+    // Indicates number of terms read on the terms screen.
+    PROVISIONING_TERMS_READ = 811;
+
+    // Logs that the user has edited the picture-in-picture settings.
+    // CATEGORY: SETTINGS
+    SETTINGS_MANAGE_PICTURE_IN_PICTURE = 812;
+
+    // ACTION: Allow "Enable picture-in-picture on hide" for an app
+    APP_PICTURE_IN_PICTURE_ON_HIDE_ALLOW = 813;
+
+    // ACTION: Deny "Enable picture-in-picture on hide" for an app
+    APP_PICTURE_IN_PICTURE_ON_HIDE_DENY = 814;
+
+    // OPEN: Settings > Language & input > Text-to-speech output -> Speech rate & pitch
+    // CATEGORY: SETTINGS
+    // OS: 8.0
+    TTS_SLIDERS = 815;
+
+    // ACTION: Settings -> Display -> Theme
+    ACTION_THEME = 816;
+
+    // OPEN: SUW Welcome Screen -> Vision Settings -> Select to Speak
+    // ACTION: Select to Speak configuration is chosen
+    //  SUBTYPE: 0 is off, 1 is on
+    // CATEGORY: SETTINGS
+    // OS: N
+    SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK = 817;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
new file mode 100644
index 0000000..c9d5c27
--- /dev/null
+++ b/proto/src/task_snapshot.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ syntax = "proto3";
+
+ package com.android.server.wm;
+
+ option java_package = "com.android.server.wm";
+ option java_outer_classname = "WindowManagerProtos";
+
+ message TaskSnapshotProto {
+     int32 orientation = 1;
+     int32 inset_left = 2;
+     int32 inset_top = 3;
+     int32 inset_right = 4;
+     int32 inset_bottom = 5;
+ }
\ No newline at end of file
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
index c04191b..49a71b4 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
@@ -2441,10 +2441,10 @@
      * @param TransA The type of transpose applied to matrix A.
      * @param TransB The type of transpose applied to matrix B.
      * @param alpha The scalar alpha.
-     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2
-     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2
+     * @param A The input allocation contains matrix A, supported elements type {@link Element#F64_2}.
+     * @param B The input allocation contains matrix B, supported elements type {@link Element#F64_2}.
      * @param beta The scalar beta.
-     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2
+     * @param C The input allocation contains matrix C, supported elements type {@link Element#F64_2}.
      */
     public void ZGEMM(@Transpose int TransA, @Transpose int TransB, Double2 alpha, Allocation A,
                       Allocation B, Double2 beta, Allocation C) {
diff --git a/sax/tests/saxtests/Android.mk b/sax/tests/saxtests/Android.mk
index 836711b..d3fbd05 100644
--- a/sax/tests/saxtests/Android.mk
+++ b/sax/tests/saxtests/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 LOCAL_PACKAGE_NAME := FrameworksSaxTests
 
 include $(BUILD_PACKAGE)
diff --git a/services/Android.mk b/services/Android.mk
index abd1459..e760fe2 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -37,8 +37,8 @@
 
 # The convention is to name each service module 'services.$(module_name)'
 LOCAL_STATIC_JAVA_LIBRARIES := $(addprefix services.,$(services)) \
-    android.hidl.base@1.0-java \
-    android.hardware.biometrics.fingerprint@2.1-java
+    android.hidl.base@1.0-java-static \
+    android.hardware.biometrics.fingerprint@2.1-java-static
 
 ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true)
 LOCAL_EMMA_INSTRUMENT := true
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b34e4e4..ece5149 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -97,6 +97,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.server.LocalServices;
 
+import com.android.server.policy.AccessibilityShortcutController;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -1489,6 +1490,7 @@
         mInitialized = true;
         updateLegacyCapabilitiesLocked(userState);
         updateServicesLocked(userState);
+        updateAccessibilityShortcutLocked(userState);
         updateWindowsForAccessibilityCallbackLocked(userState);
         updateAccessibilityFocusBehaviorLocked(userState);
         updateFilterKeyEventsLocked(userState);
@@ -1613,7 +1615,7 @@
         somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
         somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
         somethingChanged |= readAutoclickEnabledSettingLocked(userState);
-
+        somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
         return somethingChanged;
     }
 
@@ -1722,6 +1724,50 @@
         }
     }
 
+    private boolean readAccessibilityShortcutSettingLocked(UserState userState) {
+        String componentNameToEnableString = AccessibilityShortcutController
+                .getTargetServiceComponentNameString(mContext, userState.mUserId);
+        if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) {
+            if (userState.mServiceToEnableWithShortcut == null) {
+                return false;
+            }
+            userState.mServiceToEnableWithShortcut = null;
+            return true;
+        }
+        ComponentName componentNameToEnable =
+            ComponentName.unflattenFromString(componentNameToEnableString);
+        if (componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
+            return false;
+        }
+        userState.mServiceToEnableWithShortcut = componentNameToEnable;
+        return true;
+    }
+
+    /**
+     * Check if the service that will be enabled by the shortcut is installed. If it isn't,
+     * clear the value and the associated setting so a sideloaded service can't spoof the
+     * package name of the default service.
+     *
+     * @param userState
+     */
+    private void updateAccessibilityShortcutLocked(UserState userState) {
+        if (userState.mServiceToEnableWithShortcut == null) {
+            return;
+        }
+        boolean shortcutServiceIsInstalled = false;
+        for (int i = 0; i < userState.mInstalledServices.size(); i++) {
+            if (userState.mInstalledServices.get(i).getComponentName()
+                    .equals(userState.mServiceToEnableWithShortcut)) {
+                shortcutServiceIsInstalled = true;
+            }
+        }
+        if (!shortcutServiceIsInstalled) {
+            userState.mServiceToEnableWithShortcut = null;
+            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "", userState.mUserId);
+        }
+    }
+
     private boolean canRequestAndRequestsTouchExplorationLocked(Service service) {
         // Service not ready or cannot request the feature - well nothing to do.
         if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) {
@@ -1895,44 +1941,63 @@
     }
 
     /**
+     * AIDL-exposed method to be called when the accessibility shortcut is enabled. Requires
+     * permission to write secure settings, since someone with that permission can enable
+     * accessibility services themselves.
+     */
+    public void performAccessibilityShortcut() {
+        if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
+                && (mContext.checkCallingPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED)) {
+            throw new SecurityException(
+                    "performAccessibilityShortcut requires the WRITE_SECURE_SETTINGS permission");
+        }
+        synchronized(mLock) {
+            UserState userState = getUserStateLocked(mCurrentUserId);
+            ComponentName serviceName = userState.mServiceToEnableWithShortcut;
+            if (serviceName == null) {
+                return;
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (userState.mComponentNameToServiceMap.get(serviceName) == null) {
+                    enableAccessibilityServiceLocked(serviceName, mCurrentUserId);
+                } else {
+                    disableAccessibilityServiceLocked(serviceName, mCurrentUserId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    };
+
+    /**
      * Enables accessibility service specified by {@param componentName} for the {@param userId}.
      */
-    public void enableAccessibilityService(ComponentName componentName, int userId) {
-        synchronized(mLock) {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                throw new SecurityException("only SYSTEM can call enableAccessibilityService.");
-            }
+    private void enableAccessibilityServiceLocked(ComponentName componentName, int userId) {
+        SettingsStringHelper settingsHelper = new SettingsStringHelper(
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+        settingsHelper.addService(componentName);
+        settingsHelper.writeToSettings();
 
-            SettingsStringHelper settingsHelper = new SettingsStringHelper(
-                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
-            settingsHelper.addService(componentName);
-            settingsHelper.writeToSettings();
-
-            UserState userState = getUserStateLocked(userId);
-            if (userState.mEnabledServices.add(componentName)) {
-                onUserStateChangedLocked(userState);
-            }
+        UserState userState = getUserStateLocked(userId);
+        if (userState.mEnabledServices.add(componentName)) {
+            onUserStateChangedLocked(userState);
         }
     }
 
     /**
      * Disables accessibility service specified by {@param componentName} for the {@param userId}.
      */
-    public void disableAccessibilityService(ComponentName componentName, int userId) {
-        synchronized(mLock) {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                throw new SecurityException("only SYSTEM can call disableAccessibility");
-            }
+    private void disableAccessibilityServiceLocked(ComponentName componentName, int userId) {
+        SettingsStringHelper settingsHelper = new SettingsStringHelper(
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+        settingsHelper.deleteService(componentName);
+        settingsHelper.writeToSettings();
 
-            SettingsStringHelper settingsHelper = new SettingsStringHelper(
-                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
-            settingsHelper.deleteService(componentName);
-            settingsHelper.writeToSettings();
-
-            UserState userState = getUserStateLocked(userId);
-            if (userState.mEnabledServices.remove(componentName)) {
-                onUserStateChangedLocked(userState);
-            }
+        UserState userState = getUserStateLocked(userId);
+        if (userState.mEnabledServices.remove(componentName)) {
+            onUserStateChangedLocked(userState);
         }
     }
 
@@ -4307,6 +4372,8 @@
 
         public ComponentName mServiceChangingSoftKeyboardMode;
 
+        public ComponentName mServiceToEnableWithShortcut;
+
         public int mLastSentClientState = -1;
 
         public int mSoftKeyboardShowMode = 0;
@@ -4439,6 +4506,9 @@
         private final Uri mAccessibilitySoftKeyboardModeUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
 
+        private final Uri mAccessibilityShortcutServiceIdUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+
         public AccessibilityContentObserver(Handler handler) {
             super(handler);
         }
@@ -4467,6 +4537,8 @@
                     mHighTextContrastUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
+                    mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
         }
 
         @Override
@@ -4519,6 +4591,10 @@
                         notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
                         onUserStateChangedLocked(userState);
                     }
+                } else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
+                    if (readAccessibilityShortcutSettingLocked(userState)) {
+                        onUserStateChangedLocked(userState);
+                    }
                 }
             }
         }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 87eb380..3523706 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -126,6 +126,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
 
 class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider,
         OnCrossProfileWidgetProvidersChangeListener {
@@ -152,6 +153,8 @@
     // Bump if the stored widgets need to be upgraded.
     private static final int CURRENT_VERSION = 1;
 
+    private static final AtomicLong REQUEST_COUNTER = new AtomicLong();
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -771,7 +774,8 @@
             LongSparseArray<PendingHostUpdate> updatesMap = new LongSparseArray<>();
             for (int i = 0; i < N; i++) {
                 if (host.getPendingUpdatesForId(appWidgetIds[i], updatesMap)) {
-                    // We key the updates based on time, so that the values are sorted by time.
+                    // We key the updates based on request id, so that the values are sorted in the
+                    // order they were received.
                     int M = updatesMap.size();
                     for (int j = 0; j < M; j++) {
                         outUpdates.add(updatesMap.valueAt(j));
@@ -1854,9 +1858,9 @@
             // method with a wrong id. In that case, ignore the call.
             return;
         }
-        long requestTime = SystemClock.uptimeMillis();
+        long requestId = REQUEST_COUNTER.incrementAndGet();
         if (widget != null) {
-            widget.updateTimes.put(viewId, requestTime);
+            widget.updateRequestIds.put(viewId, requestId);
         }
         if (widget == null || widget.host == null || widget.host.zombie
                 || widget.host.callbacks == null || widget.provider == null
@@ -1867,7 +1871,7 @@
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = widget.host;
         args.arg2 = widget.host.callbacks;
-        args.arg3 = requestTime;
+        args.arg3 = requestId;
         args.argi1 = widget.appWidgetId;
         args.argi2 = viewId;
 
@@ -1878,10 +1882,10 @@
 
 
     private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
-            int appWidgetId, int viewId, long requestTime) {
+            int appWidgetId, int viewId, long requestId) {
         try {
             callbacks.viewDataChanged(appWidgetId, viewId);
-            host.lastWidgetUpdateTime = requestTime;
+            host.lastWidgetUpdateRequestId = requestId;
         } catch (RemoteException re) {
             // It failed; remove the callback. No need to prune because
             // we know that this host is still referenced by this instance.
@@ -1928,9 +1932,9 @@
     }
 
     private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
-        long requestTime = SystemClock.uptimeMillis();
+        long requestId = REQUEST_COUNTER.incrementAndGet();
         if (widget != null) {
-            widget.updateTimes.put(ID_VIEWS_UPDATE, requestTime);
+            widget.updateRequestIds.put(ID_VIEWS_UPDATE, requestId);
         }
         if (widget == null || widget.provider == null || widget.provider.zombie
                 || widget.host.callbacks == null || widget.host.zombie) {
@@ -1941,7 +1945,7 @@
         args.arg1 = widget.host;
         args.arg2 = widget.host.callbacks;
         args.arg3 = (updateViews != null) ? updateViews.clone() : null;
-        args.arg4 = requestTime;
+        args.arg4 = requestId;
         args.argi1 = widget.appWidgetId;
 
         mCallbackHandler.obtainMessage(
@@ -1950,10 +1954,10 @@
     }
 
     private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
-            int appWidgetId, RemoteViews views, long requestTime) {
+            int appWidgetId, RemoteViews views, long requestId) {
         try {
             callbacks.updateAppWidget(appWidgetId, views);
-            host.lastWidgetUpdateTime = requestTime;
+            host.lastWidgetUpdateRequestId = requestId;
         } catch (RemoteException re) {
             synchronized (mLock) {
                 Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -1963,11 +1967,11 @@
     }
 
     private void scheduleNotifyProviderChangedLocked(Widget widget) {
-        long requestTime = SystemClock.uptimeMillis();
+        long requestId = REQUEST_COUNTER.incrementAndGet();
         if (widget != null) {
             // When the provider changes, reset everything else.
-            widget.updateTimes.clear();
-            widget.updateTimes.append(ID_PROVIDER_CHANGED, requestTime);
+            widget.updateRequestIds.clear();
+            widget.updateRequestIds.append(ID_PROVIDER_CHANGED, requestId);
         }
         if (widget == null || widget.provider == null || widget.provider.zombie
                 || widget.host.callbacks == null || widget.host.zombie) {
@@ -1978,7 +1982,7 @@
         args.arg1 = widget.host;
         args.arg2 = widget.host.callbacks;
         args.arg3 = widget.provider.info;
-        args.arg4 = requestTime;
+        args.arg4 = requestId;
         args.argi1 = widget.appWidgetId;
 
         mCallbackHandler.obtainMessage(
@@ -1987,10 +1991,10 @@
     }
 
     private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks,
-            int appWidgetId, AppWidgetProviderInfo info, long requestTime) {
+            int appWidgetId, AppWidgetProviderInfo info, long requestId) {
         try {
             callbacks.providerChanged(appWidgetId, info);
-            host.lastWidgetUpdateTime = requestTime;
+            host.lastWidgetUpdateRequestId = requestId;
         } catch (RemoteException re) {
             synchronized (mLock){
                 Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -3463,11 +3467,11 @@
                     Host host = (Host) args.arg1;
                     IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
                     RemoteViews views = (RemoteViews) args.arg3;
-                    long requestTime = (Long) args.arg4;
+                    long requestId = (Long) args.arg4;
                     final int appWidgetId = args.argi1;
                     args.recycle();
 
-                    handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views, requestTime);
+                    handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views, requestId);
                 } break;
 
                 case MSG_NOTIFY_PROVIDER_CHANGED: {
@@ -3475,11 +3479,11 @@
                     Host host = (Host) args.arg1;
                     IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
                     AppWidgetProviderInfo info = (AppWidgetProviderInfo)args.arg3;
-                    long requestTime = (Long) args.arg4;
+                    long requestId = (Long) args.arg4;
                     final int appWidgetId = args.argi1;
                     args.recycle();
 
-                    handleNotifyProviderChanged(host, callbacks, appWidgetId, info, requestTime);
+                    handleNotifyProviderChanged(host, callbacks, appWidgetId, info, requestId);
                 } break;
 
                 case MSG_NOTIFY_PROVIDERS_CHANGED: {
@@ -3495,13 +3499,13 @@
                     SomeArgs args = (SomeArgs) message.obj;
                     Host host = (Host) args.arg1;
                     IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
-                    long requestTime = (Long) args.arg3;
+                    long requestId = (Long) args.arg3;
                     final int appWidgetId = args.argi1;
                     final int viewId = args.argi2;
                     args.recycle();
 
                     handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId,
-                            requestTime);
+                            requestId);
                 } break;
             }
         }
@@ -3817,7 +3821,7 @@
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
 
         int tag = TAG_UNDEFINED; // for use while saving state (the index)
-        long lastWidgetUpdateTime; // last time we were successfully able to send an update.
+        long lastWidgetUpdateRequestId; // request id for the last update successfully sent
 
         public int getUserId() {
             return UserHandle.getUserId(id.uid);
@@ -3844,18 +3848,18 @@
          */
         public boolean getPendingUpdatesForId(int appWidgetId,
                 LongSparseArray<PendingHostUpdate> outUpdates) {
-            long updateTime = lastWidgetUpdateTime;
+            long updateRequestId = lastWidgetUpdateRequestId;
             int N = widgets.size();
             for (int i = 0; i < N; i++) {
                 Widget widget = widgets.get(i);
                 if (widget.appWidgetId == appWidgetId) {
                     outUpdates.clear();
-                    for (int j = widget.updateTimes.size() - 1; j >= 0; j--) {
-                        long time = widget.updateTimes.valueAt(j);
-                        if (time <= updateTime) {
+                    for (int j = widget.updateRequestIds.size() - 1; j >= 0; j--) {
+                        long requestId = widget.updateRequestIds.valueAt(j);
+                        if (requestId <= updateRequestId) {
                             continue;
                         }
-                        int id = widget.updateTimes.keyAt(j);
+                        int id = widget.updateRequestIds.keyAt(j);
                         final PendingHostUpdate update;
                         switch (id) {
                             case ID_PROVIDER_CHANGED:
@@ -3869,7 +3873,7 @@
                             default:
                                 update = PendingHostUpdate.viewDataChanged(appWidgetId, id);
                         }
-                        outUpdates.put(time, update);
+                        outUpdates.put(requestId, update);
                     }
                     return true;
                 }
@@ -3951,8 +3955,8 @@
         RemoteViews maskedViews;
         Bundle options;
         Host host;
-        // timestamps for various operations
-        SparseLongArray updateTimes = new SparseLongArray(2);
+        // Request ids for various operations
+        SparseLongArray updateRequestIds = new SparseLongArray(2);
 
         @Override
         public String toString() {
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 87eaf29..47ac1ce 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -19,10 +19,6 @@
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTO_FILL_MANAGER_SERVICE;
 import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
-import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
-
-import static com.android.server.autofill.AutoFillUI.MSG_SHOW_ALL_NOTIFICATIONS;
-import static com.android.server.autofill.AutoFillUI.SHOW_ALL_NOTIFICATIONS_DELAY_MS;
 
 import android.Manifest;
 import android.app.AppGlobals;
@@ -32,11 +28,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -51,9 +49,12 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
+import android.view.autofill.AutoFillId;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
 
@@ -70,13 +71,12 @@
 public final class AutoFillManagerService extends SystemService {
 
     private static final String TAG = "AutoFillManagerService";
-    static final boolean DEBUG = true; // TODO: change to false once stable
+    static final boolean DEBUG = true; // TODO(b/33197203): change to false once stable
 
     private static final long SERVICE_BINDING_LIFETIME_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
 
-    private static final int ARG_NOT_USED = 0;
-
     protected static final int MSG_UNBIND = 1;
+    protected static final int MSG_SHOW_AUTO_FILL = 2;
 
     private final AutoFillManagerServiceStub mServiceStub;
     private final AutoFillUI mUi;
@@ -85,23 +85,27 @@
 
     private final Object mLock = new Object();
 
-    private final Handler mHandler = new Handler() {
+    private final HandlerCaller.Callback mHandlerCallback = new HandlerCaller.Callback() {
+
         @Override
-        public void handleMessage(Message msg) {
+        public void executeMessage(Message msg) {
             switch (msg.what) {
-                case MSG_UNBIND:
+                case MSG_UNBIND: {
                     removeStaleServiceForUser(msg.arg1);
                     return;
-                case MSG_SHOW_ALL_NOTIFICATIONS:
-                    mUi.showAllNotifications();
+                } case MSG_SHOW_AUTO_FILL: {
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    showAutoFillInput(msg.arg1, (AutoFillId) args.arg1, (Rect) args.arg2);
                     return;
-                default:
+                } default: {
                     Slog.w(TAG, "Invalid message: " + msg);
+                }
             }
         }
-
     };
 
+    private HandlerCaller mHandlerCaller;
+
     /**
      * Cache of {@link AutoFillManagerServiceImpl} per user id.
      * <p>
@@ -122,6 +126,8 @@
     public AutoFillManagerService(Context context) {
         super(context);
 
+        mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
+
         mContext = context;
         mUi = new AutoFillUI(context, this, mLock);
         mResolver = context.getContentResolver();
@@ -139,14 +145,6 @@
         if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
             new SettingsObserver(BackgroundThread.getHandler());
         }
-        if (phase == PHASE_BOOT_COMPLETED) {
-            // TODO: if sent right away, the notification is not displayed. Since the notification
-            // mechanism is a temporary approach anyways, just delay it..
-            if (DEBUG)
-                Slog.d(TAG, "Showing notifications in " + SHOW_ALL_NOTIFICATIONS_DELAY_MS + "ms");
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ALL_NOTIFICATIONS),
-                    SHOW_ALL_NOTIFICATIONS_DELAY_MS);
-        }
     }
 
     private AutoFillManagerServiceImpl newServiceForUser(int userId) {
@@ -200,7 +198,7 @@
         }
         // Keep service connection alive for a while, in case user needs to interact with it
         // (for example, to save the data that was inputted in)
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UNBIND, userId, ARG_NOT_USED),
+        mHandlerCaller.sendMessageDelayed(mHandlerCaller.obtainMessageI(MSG_UNBIND, userId),
                 SERVICE_BINDING_LIFETIME_MS);
         return service;
     }
@@ -245,18 +243,41 @@
 
     }
 
+
+    private void requestAutoFillLocked(IBinder activityToken, int userId, Bundle extras,
+            int flags) {
+        final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+        if (service != null) {
+            service.requestAutoFill(activityToken, extras, flags);
+        }
+    }
+
+    private void showAutoFillInput(int userId, AutoFillId id, Rect rect) {
+        if (DEBUG) Slog.d(TAG, "handler.showAutoFillInput(): id=" + id + ", rect=" + rect);
+
+        synchronized (mLock) {
+            requestAutoFillLocked(null, userId, null, AUTO_FILL_FLAG_TYPE_FILL);
+        }
+    }
+
     final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
 
         @Override
+        public void showAutoFillInput(AutoFillId id, Rect boundaries) {
+            if (DEBUG) Slog.d(TAG, "showAutoFillInput(): id=" + id + ", boundaries=" + boundaries);
+
+            // TODO(b/33197203): fail if it's not called by same uid as the top activity
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW_AUTO_FILL,
+                    UserHandle.getCallingUserId(), id, boundaries));
+        }
+
+        @Override
         public void requestAutoFill(IBinder activityToken, int userId, Bundle extras, int flags) {
             if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", userId=" + userId);
             mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
             synchronized (mLock) {
-                final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
-                if (service != null) {
-                    service.requestAutoFill(activityToken, extras, flags);
-                }
+                requestAutoFillLocked(activityToken, userId, extras, flags);
             }
         }
 
@@ -291,7 +312,6 @@
             (new AutoFillManagerServiceShellCommand(this)).exec(
                     this, in, out, err, args, callback, resultReceiver);
         }
-
     }
 
     private final class SettingsObserver extends ContentObserver {
@@ -307,7 +327,6 @@
             if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId);
             synchronized (mLock) {
                 removeCachedServiceForUserLocked(userId);
-                mUi.updateNotification(userId);
             }
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 3de8a8b..77e7b31 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -16,7 +16,11 @@
 
 package com.android.server.autofill;
 
-import static com.android.server.autofill.AutoFillManagerService.DEBUG;
+import static com.android.server.autofill.Helper.DEBUG;
+import static com.android.server.autofill.Helper.bundleToString;
+import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_ERROR;
+import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_REQUESTED;
+import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_SUCCESS;
 
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -31,12 +35,16 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
-import android.icu.text.DateFormat;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.autofill.AutoFillService;
@@ -45,6 +53,7 @@
 import android.service.autofill.IAutoFillServerCallback;
 import android.service.autofill.IAutoFillService;
 import android.service.voice.VoiceInteractionSession;
+import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -59,7 +68,6 @@
 
 import java.io.PrintWriter;
 import java.util.Arrays;
-import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -73,7 +81,7 @@
     private static final String TAG = "AutoFillManagerServiceImpl";
 
     /** Used do assign ids to new ServerCallback instances. */
-    private static int sServerCallbackCounter = 0;
+    private static int sSessionIdCounter = 0;
 
     private final int mUserId;
     private final int mUid;
@@ -106,13 +114,15 @@
     };
 
     /**
-     * Cache of pending ServerCallbacks, keyed by {@link ServerCallback#id}.
+     * Cache of pending {@link Session}s, keyed by {@link Session#mId}.
      *
-     * <p>They're kept until the AutoFillService handles a request, or an error occurs.
+     * <p>They're kept until the {@link AutoFillService} finished handling a request, an error
+     * occurs, or the session times out.
      */
-    // TODO(b/33197203): need to make sure service is bound while callback is pending
+    // TODO(b/33197203): need to make sure service is bound while callback is pending and/or
+    // use WeakReference
     @GuardedBy("mLock")
-    private static final SparseArray<ServerCallback> mServerCallbacks = new SparseArray<>();
+    private static final SparseArray<Session> mSessions = new SparseArray<>();
 
     private final ServiceConnection mConnection = new ServiceConnection() {
         @Override
@@ -146,10 +156,9 @@
         }
     };
 
-
     /**
      * Receiver of assist data from the app's {@link Activity}, uses the {@code resultData} as
-     * the {@link ServerCallback#id}.
+     * the {@link Session#mId}.
      */
     private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
         @Override
@@ -163,25 +172,26 @@
             }
             final AssistStructure structure = resultData
                     .getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
-            final Bundle data = resultData.getBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS);
             final int flags = resultData.getInt(VoiceInteractionSession.KEY_FLAGS, 0);
 
-            final ServerCallback serverCallback;
+            final Session session;
             synchronized (mLock) {
-                serverCallback = mServerCallbacks.get(resultCode);
-                if (serverCallback == null) {
+                session = mSessions.get(resultCode);
+                if (session == null) {
                     Slog.w(TAG, "no server callback for id " + resultCode);
                     return;
                 }
-                serverCallback.appCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
+                session.mAppCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
             }
-            mService.autoFill(structure, serverCallback, serverCallback.extras, flags);
+            mService.autoFill(structure, session.mServerCallback, session.mExtras, flags);
         }
     };
 
     @GuardedBy("mLock")
     private IAutoFillService mService;
+    @GuardedBy("mLock")
     private boolean mBound;
+    @GuardedBy("mLock")
     private boolean mValid;
 
     // Estimated time when the service will be evicted from the cache.
@@ -202,7 +212,7 @@
 
         final AutoFillServiceInfo info;
         try {
-            info = new AutoFillServiceInfo(component, mUserId);
+            info = new AutoFillServiceInfo(context.getPackageManager(), component, mUserId);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.w(TAG, "Auto-fill service not found: " + component, e);
             mInfo = null;
@@ -275,8 +285,8 @@
             activityToken = topActivities.get(0);
         }
 
-        final String historyItem =
-                DateFormat.getDateTimeInstance().format(new Date()) + " - " + activityToken;
+        final String historyItem = TimeUtils.formatForLogging(System.currentTimeMillis())
+                + " - " + activityToken;
         synchronized (mLock) {
             mRequestHistory.add(historyItem);
             requestAutoFillLocked(activityToken, extras, flags, true);
@@ -295,9 +305,9 @@
             return;
         }
 
-        final int callbackId = ++sServerCallbackCounter;
-        final ServerCallback serverCallback = new ServerCallback(callbackId, extras);
-        mServerCallbacks.put(callbackId, serverCallback);
+        final int sessionId = ++sSessionIdCounter;
+        final Session session = new Session(sessionId, extras);
+        mSessions.put(sessionId, session);
 
         /*
          * TODO(b/33197203): apply security checks below:
@@ -308,7 +318,7 @@
          */
         try {
             // TODO(b/33197203): add MetricsLogger call
-            if (!mAm.requestAutoFillData(mAssistReceiver, null, callbackId, activityToken, flags)) {
+            if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken, flags)) {
                 // TODO(b/33197203): might need a way to warn user (perhaps a new method on
                 // AutoFillService).
                 Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
@@ -348,39 +358,74 @@
     /**
      * Called by {@link AutoFillUI} to fill an activity after the user selected a dataset.
      */
-    void autoFillApp(int callbackId, Dataset dataset) {
+    void autoFillApp(int sessionId, Dataset dataset) {
         // TODO(b/33197203): add MetricsLogger call
 
         if (dataset == null) {
-            Slog.w(TAG, "autoFillApp(): no dataset for callback id " + callbackId);
+            Slog.w(TAG, "autoFillApp(): no dataset for callback id " + sessionId);
             return;
         }
 
-        final ServerCallback serverCallback;
+
+        final Session session;
         synchronized (mLock) {
-            serverCallback = mServerCallbacks.get(callbackId);
-            if (serverCallback == null) {
-                Slog.w(TAG, "autoFillApp(): no server callback with id " + callbackId);
+            session = mSessions.get(sessionId);
+            if (session == null) {
+                Slog.w(TAG, "autoFillApp(): no session with id " + sessionId);
                 return;
             }
-            if (serverCallback.appCallback == null) {
-                Slog.w(TAG, "autoFillApp(): no app callback for server callback " + callbackId);
+            if (session.mAppCallback == null) {
+                Slog.w(TAG, "autoFillApp(): no app callback for session " + sessionId);
                 return;
             }
+
             // TODO(b/33197203): use a handler?
-            try {
-                if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
-                serverCallback.appCallback.autoFill(dataset);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Error auto-filling activity: " + e);
-            }
-            removeServerCallbackLocked(callbackId);
+            session.autoFill(dataset);
         }
     }
 
-    void removeServerCallbackLocked(int id) {
-        if (DEBUG) Slog.d(TAG, "Removing " + id + " from server callbacks");
-        mServerCallbacks.remove(id);
+    void removeSessionLocked(int id) {
+        if (DEBUG) Slog.d(TAG, "Removing session " + id);
+        mSessions.remove(id);
+
+        // TODO(b/33197203): notify mService so it can invalidate the FillCallback / SaveCallback?
+    }
+
+    /**
+     * Notifies the result of a {@link FillResponse} authentication request to the service.
+     *
+     * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after user
+     * used the fingerprint sensors to authenticate.
+     */
+    void notifyResponseAuthenticationResult(Bundle extras, int flags) {
+        if (DEBUG) Slog.d(TAG, "notifyResponseAuthenticationResult(): flags=" + flags
+                + ", extras=" + bundleToString(extras));
+
+        synchronized (mLock) {
+            try {
+                mService.authenticateFillResponse(extras, flags);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Error sending authentication result back to service: " + e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the result of a {@link Dataset} authentication request to the service.
+     *
+     * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after
+     * it gets the results from a fingerprint authentication.
+     */
+    void notifyDatasetAuthenticationResult(Bundle extras, int flags) {
+        if (DEBUG) Slog.d(TAG, "notifyDatasetAuthenticationResult(): flags=" + flags
+                + ", extras=" + bundleToString(extras));
+        synchronized (mLock) {
+            try {
+                mService.authenticateDataset(extras, flags);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Error sending authentication result back to service: " + e);
+            }
+        }
     }
 
     void dumpLocked(String prefix, PrintWriter pw) {
@@ -399,15 +444,15 @@
         pw.print(prefix); pw.print("mUserId="); pw.println(mUserId);
         pw.print(prefix); pw.print("mUid="); pw.println(mUid);
         pw.print(prefix); pw.print("mComponent="); pw.println(mComponent.flattenToShortString());
+        pw.print(prefix); pw.print("mService: "); pw.println(mService);
         pw.print(prefix); pw.print("mBound="); pw.println(mBound);
-        pw.print(prefix); pw.print("mService="); pw.println(mService);
         pw.print(prefix); pw.print("mEstimateTimeOfDeath=");
             TimeUtils.formatDuration(mEstimateTimeOfDeath, SystemClock.uptimeMillis(), pw);
         pw.println();
 
         if (DEBUG) {
             // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps)
-            pw.print(prefix); pw.println("Service info:");
+            pw.print(prefix); pw.println("ServiceInfo:");
             mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix);
         }
 
@@ -428,19 +473,19 @@
             }
         }
 
-        pw.print(prefix); pw.print("sServerCallbackCounter="); pw.println(sServerCallbackCounter);
-        final int size = mServerCallbacks.size();
+        pw.print(prefix); pw.print("sSessionIdCounter="); pw.println(sSessionIdCounter);
+        final int size = mSessions.size();
         if (size == 0) {
-            pw.print(prefix); pw.println("No server callbacks");
+            pw.print(prefix); pw.println("No sessions");
         } else {
-            pw.print(prefix); pw.print(size); pw.println(" server callbacks:");
+            pw.print(prefix); pw.print(size); pw.println(" sessions:");
             for (int i = 0; i < size; i++) {
-                pw.print(prefix2); pw.print(mServerCallbacks.keyAt(i));
-                final ServerCallback callback = mServerCallbacks.valueAt(i);
-                if (callback.appCallback == null) {
+                pw.print(prefix2); pw.print(mSessions.keyAt(i));
+                final Session session = mSessions.valueAt(i);
+                if (session.mAppCallback == null) {
                     pw.println("(no appCallback)");
                 } else {
-                    pw.print(" (app callback: "); pw.print(callback.appCallback) ; pw.println(")");
+                    pw.print(" (app callback: "); pw.print(session.mAppCallback) ; pw.println(")");
                 }
             }
             pw.println();
@@ -449,7 +494,7 @@
 
     @Override
     public String toString() {
-        return "[AutoFillManagerServiceImpl: userId=" + mUserId + ", uid=" + mUid
+        return "AutoFillManagerServiceImpl: [userId=" + mUserId + ", uid=" + mUid
                 + ", component=" + mComponent.flattenToShortString() + "]";
     }
 
@@ -473,49 +518,311 @@
     /**
      * A bridge between the {@link AutoFillService} implementation and the activity being
      * auto-filled (represented through the {@link IAutoFillAppCallback}).
+     *
+     * <p>Although the auto-fill requests and callbacks are stateless from the service's point of
+     * view, we need to keep state in the framework side for cases such as authentication. For
+     * example, when service return a {@link FillResponse} that contains all the fields needed
+     * to fill the activity but it requires authentication first, that response need to be held
+     * until the user authenticates or it times out.
      */
-    private final class ServerCallback extends IAutoFillServerCallback.Stub {
+    // TODO(b/33197203): make sure sessions are removed (and tested by CTS):
+    // - On all authentication scenarios.
+    // - When user does not interact back after a while.
+    // - When service is unbound.
+    private final class Session {
 
-        private final int id;
-        private final Bundle extras;
-        private IAutoFillAppCallback appCallback;
+        private final int mId;
+        private final Bundle mExtras;
+        private IAutoFillAppCallback mAppCallback;
 
-        private ServerCallback(int id, Bundle extras) {
-            this.id = id;
-            this.extras = extras;
+        // Token used on fingerprint authentication
+        private final IBinder mToken = new Binder();
+
+        private final IFingerprintService mFingerprintService;
+
+        @GuardedBy("mLock")
+        private FillResponse mResponseRequiringAuth;
+        @GuardedBy("mLock")
+        private Dataset mDatasetRequiringAuth;
+
+        // Used to auto-fill the activity directly when the FillCallback.onResponse() is called as
+        // the result of a successful user authentication on service's side.
+        @GuardedBy("mLock")
+        private boolean mAutoFillDirectly;
+
+        // TODO(b/33197203): use handler to handle results?
+        // TODO(b/33197203): handle all callback methods and/or cancelation?
+        private IFingerprintServiceReceiver mServiceReceiver =
+                new IFingerprintServiceReceiver.Stub() {
+
+            @Override
+            public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+                if (DEBUG) Slog.d(TAG, "onEnrollResult()");
+            }
+
+            @Override
+            public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+                if (DEBUG) Slog.d(TAG, "onAcquired()");
+            }
+
+            @Override
+            public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
+                if (DEBUG) Slog.d(TAG, "onAuthenticationSucceeded(): " + fp.getGroupId());
+
+                // First, check what was authenticated, a response or a dataset.
+                // Then, decide how to handle it:
+                // - If service provided data, handle them directly.
+                // - Otherwise, notify service.
+
+                mAutoFillDirectly = true;
+
+                if (mDatasetRequiringAuth != null) {
+                    if (mDatasetRequiringAuth.isEmpty()) {
+                        notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(),
+                                FLAG_AUTHENTICATION_SUCCESS);
+                    } else {
+                        autoFillAppLocked(mDatasetRequiringAuth, true);
+                    }
+                } else if (mResponseRequiringAuth != null) {
+                    final List<Dataset> datasets = mResponseRequiringAuth.getDatasets();
+                    if (datasets.isEmpty()) {
+                        notifyResponseAuthenticationResult(mResponseRequiringAuth.getExtras(),
+                                FLAG_AUTHENTICATION_SUCCESS);
+                    } else {
+                        showResponseLocked(mResponseRequiringAuth, true);
+                    }
+                } else {
+                    Slog.w(TAG, "onAuthenticationSucceeded(): no response or dataset");
+                }
+
+                mUi.dismissFingerprintRequest(mUserId, true);
+            }
+
+            @Override
+            public void onAuthenticationFailed(long deviceId) {
+                if (DEBUG) Slog.d(TAG, "onAuthenticationFailed()");
+                // Do nothing - onError() will be called after a few failures...
+            }
+
+            @Override
+            public void onError(long deviceId, int error, int vendorCode) {
+                if (DEBUG) Slog.d(TAG, "onError()");
+
+                // Notify service so it can fallback to its own authentication
+                if (mDatasetRequiringAuth != null) {
+                    notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(),
+                            FLAG_AUTHENTICATION_ERROR);
+                } else if (mResponseRequiringAuth != null) {
+                    notifyResponseAuthenticationResult(mResponseRequiringAuth.getExtras(),
+                            FLAG_AUTHENTICATION_ERROR);
+                } else {
+                    Slog.w(TAG, "onError(): no response or dataset");
+                }
+
+                mUi.dismissFingerprintRequest(mUserId, false);
+            }
+
+            @Override
+            public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
+                if (DEBUG) Slog.d(TAG, "onRemoved()");
+            }
+
+            @Override
+            public void onEnumerated(long deviceId, int fingerId, int groupId, int remaining) {
+                if (DEBUG) Slog.d(TAG, "onEnumerated()");
+            }
+        };
+
+        private IAutoFillServerCallback mServerCallback = new IAutoFillServerCallback.Stub() {
+            @Override
+            public void showResponse(FillResponse response) {
+                // TODO(b/33197203): add MetricsLogger call
+                if (response == null) {
+                    if (DEBUG) Slog.d(TAG, "showResponse(): null response");
+
+                    removeSelf();
+                    return;
+                }
+
+                synchronized (mLock) {
+                    showResponseLocked(response, response.isAuthRequired());
+                }
+            }
+
+            @Override
+            public void showError(CharSequence message) {
+                // TODO(b/33197203): add MetricsLogger call
+                if (DEBUG) Slog.d(TAG, "showError(): " + message);
+
+                mUi.showError(message);
+
+                removeSelf();
+            }
+
+            @Override
+            public void highlightSavedFields(AutoFillId[] ids) {
+                // TODO(b/33197203): add MetricsLogger call
+                if (DEBUG) Slog.d(TAG, "highlightSavedFields(): " + Arrays.toString(ids));
+
+                mUi.highlightSavedFields(ids);
+
+                removeSelf();
+            }
+
+            @Override
+            public void unlockFillResponse(int flags) {
+                // TODO(b/33197203): add proper MetricsLogger calls?
+                if (DEBUG) Log.d(TAG, "unlockUser(): flags=" + flags);
+
+                synchronized (mLock) {
+                    if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) {
+                        if (mResponseRequiringAuth == null) {
+                            Log.wtf(TAG, "unlockUser(): no mResponseRequiringAuth on flags "
+                                    + flags);
+                            removeSelf();
+                            return;
+                        }
+                        final List<Dataset> datasets = mResponseRequiringAuth.getDatasets();
+                        if (datasets.isEmpty()) {
+                            Log.w(TAG, "unlockUser(): no dataset on previous response: "
+                                    + mResponseRequiringAuth);
+                            removeSelf();
+                            return;
+                        }
+                        mAutoFillDirectly = true;
+                        showResponseLocked(mResponseRequiringAuth, false);
+                    }
+                    // TODO(b/33197203): show UI error on authentication failure?
+                    // Or let service handle it?
+                }
+            }
+
+            @Override
+            public void unlockDataset(Dataset dataset, int flags) {
+                // TODO(b/33197203): add proper MetricsLogger calls?
+                if (DEBUG) Log.d(TAG, "unlockDataset(): dataset=" + dataset + ", flags=" + flags);
+
+                if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) {
+                    autoFillAppLocked(dataset != null ? dataset : mDatasetRequiringAuth, true);
+                    return;
+                }
+                removeSelf();
+            }
+        };
+
+        private Session(int id, Bundle extras) {
+            this.mId = id;
+            this.mExtras = extras;
+            this.mFingerprintService = IFingerprintService.Stub
+                    .asInterface(ServiceManager.getService("fingerprint"));
         }
 
-        @Override
-        public void showResponse(FillResponse response) {
-            // TODO(b/33197203): add MetricsLogger call
-            if (DEBUG) Slog.d(TAG, "showResponse(): " + response);
+        private void showResponseLocked(FillResponse response, boolean authRequired) {
+            if (DEBUG) Slog.d(TAG, "showResponse(directly=" + mAutoFillDirectly
+                    + ", authRequired=" + authRequired +"):" + response);
 
-            mUi.showOptions(mUserId, id, response);
+            if (mAutoFillDirectly && response != null) {
+                final List<Dataset> datasets = response.getDatasets();
+                if (datasets.size() == 1) {
+                    // User authenticated and provider returned just 1 dataset - auto-fill it now!
+                    final Dataset dataset = datasets.get(0);
+                    if (DEBUG) Slog.d(TAG, "auto-filling directly from auth: " + dataset);
+
+                    autoFillAppLocked(dataset, true);
+                    return;
+                }
+            }
+
+            if (!authRequired) {
+                // TODO(b/33197203): add MetricsLogger call
+                mUi.showOptions(mUserId, mId, response);
+                return;
+            }
+
+            // Handles response that requires authentication.
+            // TODO(b/33197203): add MetricsLogger call, including if fingerprint requested
+
+            mResponseRequiringAuth = response;
+            final boolean requiresFingerprint = response.hasCryptoObject();
+            if (requiresFingerprint) {
+                // TODO(b/33197203): check if fingerprint is available first and call error callback
+                // with FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE if it's not.
+                // Start scanning for the fingerprint.
+                scanFingerprint(response.getCryptoObjectOpId());
+            }
+            // Displays the message asking the user to tap (or fingerprint) for AutoFill.
+            mUi.showFillResponseAuthenticationRequest(mUserId, mId, requiresFingerprint,
+                    response.getExtras(), response.getFlags());
         }
 
-        @Override
-        public void showError(String message) {
-            // TODO(b/33197203): add MetricsLogger call
-            if (DEBUG) Slog.d(TAG, "showError(): " + message);
+        void autoFill(Dataset dataset) {
+            synchronized (mLock) {
+                // Autofill it directly...
+                if (!dataset.isAuthRequired()) {
+                    autoFillAppLocked(dataset, true);
+                    return;
+                }
 
-            mUi.showError(message);
+                // ...or handle authentication.
 
-            removeSelf();
+                mDatasetRequiringAuth = dataset;
+                final boolean requiresFingerprint = dataset.hasCryptoObject();
+                if (requiresFingerprint) {
+                    // TODO(b/33197203): check if fingerprint is available first and call error callback
+                    // with FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE if it's not.
+                    // Start scanning for the fingerprint.
+                    scanFingerprint(dataset.getCryptoObjectOpId());
+                    // Displays the message asking the user to tap (or fingerprint) for AutoFill.
+                    mUi.showDatasetFingerprintAuthenticationRequest(dataset);
+                } else {
+                    try {
+                        mService.authenticateDataset(dataset.getExtras(),
+                                FLAG_AUTHENTICATION_REQUESTED);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Error authenticating dataset: " + e);
+                    }
+                }
+            }
         }
 
-        @Override
-        public void highlightSavedFields(AutoFillId[] ids) {
+        private void autoFillAppLocked(Dataset dataset, boolean removeSelf) {
+            try {
+                if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
+                mAppCallback.autoFill(dataset);
+
+                // TODO(b/33197203): temporarily hack: show the save notification, since save is
+                // not integrated with IME yet.
+                mUi.showSaveNotification(mUserId, null, dataset);
+
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Error auto-filling activity: " + e);
+            }
+            if (removeSelf) {
+                removeSelf();
+            }
+        }
+
+        private void scanFingerprint(long opId) {
             // TODO(b/33197203): add MetricsLogger call
-            if (DEBUG) Slog.d(TAG, "showSaved(): " + Arrays.toString(ids));
+            if (DEBUG) Slog.d(TAG, "Starting fingerprint scan for op id: " + opId);
 
-            mUi.highlightSavedFields(ids);
-
-            removeSelf();
+            // TODO(b/33197203): since we're clearing the AutoFillService's identity, make sure
+            // this method is only called at the proper times, otherwise a malicious provider could
+            // keep the callback refence to bypass the check
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // TODO(b/33197203): set a timeout?
+                mFingerprintService.authenticate(mToken, opId, mUserId, mServiceReceiver, 0, null);
+            } catch (RemoteException e) {
+                // Local call, shouldn't happen.
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         private void removeSelf() {
             synchronized (mLock) {
-                removeServerCallbackLocked(id);
+                removeSessionLocked(mId);
             }
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index 08e81d3..ad525d4 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -15,47 +15,36 @@
  */
 package com.android.server.autofill;
 
-import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
 import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
 
-import static com.android.server.autofill.AutoFillManagerService.DEBUG;
+import static com.android.server.autofill.Helper.DEBUG;
+import static com.android.server.autofill.Helper.bundleToString;
 
 import android.app.Activity;
-import android.app.AppGlobals;
 import android.app.Notification;
 import android.app.Notification.Action;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.UserManager;
-import android.provider.Settings;
 import android.service.autofill.AutoFillService;
-import android.text.TextUtils;
-import android.util.Log;
 import android.util.Slog;
-import android.view.autofill.Dataset;
 import android.view.autofill.AutoFillId;
+import android.view.autofill.Dataset;
 import android.view.autofill.FillResponse;
 import android.widget.Toast;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.UiThread;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.Objects;
-import java.util.Set;
 
 /**
  * Handles all auto-fill related UI tasks.
@@ -69,15 +58,16 @@
 
     AutoFillUI(Context context, AutoFillManagerService service, Object lock) {
         mContext = context;
-        mResolver = context.getContentResolver();
         mService = service;
         mLock = lock;
+
+        setNotificationListener();
     }
 
     /**
      * Displays an error message to the user.
      */
-    void showError(String message) {
+    void showError(CharSequence message) {
         // TODO(b/33197203): proper implementation
         UiThread.getHandler().runWithScissors(() -> {
             Toast.makeText(mContext, "AutoFill error: " + message, Toast.LENGTH_LONG).show();
@@ -99,10 +89,115 @@
      * Shows the options from a {@link FillResponse} so the user can pick up the proper
      * {@link Dataset} (when the response has one).
      */
-    void showOptions(int userId, int callbackId, FillResponse response) {
+    void showOptions(int userId, int sessionId, FillResponse response) {
         // TODO(b/33197203): proper implementation
         // TODO(b/33197203): make sure if removes the callback from cache
-        showOptionsNotification(userId, callbackId, response);
+        showOptionsNotification(userId, sessionId, response);
+    }
+
+    /**
+     * Shows an UI affordance indicating that user action is required before a {@link FillResponse}
+     * can be used.
+     *
+     * <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint or tap to
+     * autofill" or "Tap to autofill", depending on the value of {@code usesFingerprint}.
+     */
+    void showFillResponseAuthenticationRequest(int userId, int sessionId, boolean usesFingerprint,
+            Bundle extras, int flags) {
+        // TODO(b/33197203): proper implementation
+        showAuthNotification(userId, sessionId, usesFingerprint, extras, flags);
+    }
+
+    /**
+     * Shows an UI affordance asking indicating that user action is required before a
+     * {@link Dataset} can be used.
+     *
+     * <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint to
+     * autofill".
+     */
+    void showDatasetFingerprintAuthenticationRequest(Dataset dataset) {
+        if (DEBUG) Slog.d(TAG, "showDatasetAuthenticationRequest(): dataset=" + dataset);
+
+        // TODO(b/33197203): proper implementation (either pop up a fingerprint dialog or replace
+        // the auto-fill bar with a new message.
+        UiThread.getHandler().runWithScissors(() -> {
+            Toast.makeText(mContext, "AutoFill: press fingerprint to unlock " + dataset.getName(),
+                    Toast.LENGTH_LONG).show();
+        }, 0);
+    }
+
+    /**
+     * Called by service after the user user the fingerprint sensors to authenticate.
+     */
+    void dismissFingerprintRequest(int userId, boolean success) {
+        if (DEBUG) Slog.d(TAG, "dismissFingerprintRequest(): ok=" + success);
+
+        dismissAuthNotification(userId);
+
+        if (!success) {
+            // TODO(b/33197203): proper implementation (snack bar / i18n string)
+            UiThread.getHandler().runWithScissors(() -> {
+                Toast.makeText(mContext, "AutoFill: fingerprint failed", Toast.LENGTH_LONG).show();
+            }, 0);
+        }
+    }
+
+    private AutoFillManagerServiceImpl getServiceLocked(int userId) {
+        final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
+        if (service == null) {
+            Slog.w(TAG, "no auto-fill service for user " + userId);
+        }
+        return service;
+    }
+
+    private void onSaveRequested(int userId, Bundle responseExtras, Bundle datasetExtras) {
+        synchronized (mLock) {
+            final AutoFillManagerServiceImpl service = getServiceLocked(userId);
+            if (service == null) return;
+
+            final Bundle extras = (responseExtras == null && datasetExtras == null)
+                    ? null : new Bundle();
+
+            if (responseExtras != null) {
+                if (DEBUG) Slog.d(TAG, "response extras on save notification: " +
+                        bundleToString(responseExtras));
+                extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, responseExtras);
+            }
+            if (datasetExtras != null) {
+                if (DEBUG) Slog.d(TAG, "dataset extras on save notificataion: " +
+                        bundleToString(datasetExtras));
+                extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras);
+            }
+
+            service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_SAVE);
+        }
+    }
+
+    private void onDatasetPicked(int userId, Dataset dataset, int sessionId) {
+        synchronized (mLock) {
+            final AutoFillManagerServiceImpl service = getServiceLocked(userId);
+            if (service == null) return;
+
+            service.autoFillApp(sessionId, dataset);
+        }
+    }
+
+    private void onSessionDone(int userId, int sessionId) {
+        synchronized (mLock) {
+            final AutoFillManagerServiceImpl service = getServiceLocked(userId);
+            if (service == null) return;
+
+            service.removeSessionLocked(sessionId);
+        }
+    }
+
+    private void onResponseAuthenticationRequested(int userId, Bundle extras, int flags) {
+        synchronized (mLock) {
+            final AutoFillManagerServiceImpl service = getServiceLocked(userId);
+            if (service == null) return;
+
+            service.notifyResponseAuthenticationResult(extras, flags);
+        }
     }
 
     /////////////////////////////////////////////////////////////////////////////////
@@ -117,144 +212,81 @@
     // Extras used in the notification intents
     private static final String EXTRA_USER_ID = "user_id";
     private static final String EXTRA_NOTIFICATION_TYPE = "notification_type";
-    private static final String EXTRA_CALLBACK_ID = "callback_id";
+    private static final String EXTRA_SESSION_ID = "session_id";
     private static final String EXTRA_FILL_RESPONSE = "fill_response";
     private static final String EXTRA_DATASET = "dataset";
+    private static final String EXTRA_AUTH_REQUIRED_EXTRAS = "auth_required_extras";
+    private static final String EXTRA_FLAGS = "flags";
 
-    private static final String TYPE_EMULATE = "emulate";
     private static final String TYPE_OPTIONS = "options";
-    private static final String TYPE_DELETE_CALLBACK = "delete_callback";
+    private static final String TYPE_FINISH_SESSION = "finish_session";
     private static final String TYPE_PICK_DATASET = "pick_dataset";
     private static final String TYPE_SAVE = "save";
+    private static final String TYPE_AUTH_RESPONSE = "auth_response";
 
-    static final int MSG_SHOW_ALL_NOTIFICATIONS = 42;
-    static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000;
-
+    @GuardedBy("mLock")
     private BroadcastReceiver mNotificationReceiver;
-    private final ContentResolver mResolver;
+    @GuardedBy("mLock")
     private final AutoFillManagerService mService;
     private final Object mLock;
 
     // Hack used to generate unique pending intents
     static int sResultCode = 0;
 
+    private void setNotificationListener() {
+        synchronized (mLock) {
+            if (mNotificationReceiver == null) {
+                mNotificationReceiver = new NotificationReceiver();
+                mContext.registerReceiver(mNotificationReceiver,
+                        new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT));
+            }
+        }
+    }
+
     final class NotificationReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             final int userId = intent.getIntExtra(EXTRA_USER_ID, -1);
-
-            final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
-            if (service == null) {
-                Slog.w(TAG, "no auto-fill service for user " + userId);
-                return;
-            }
-
-            final int callbackId = intent.getIntExtra(EXTRA_CALLBACK_ID, -1);
+            final int sessionId = intent.getIntExtra(EXTRA_SESSION_ID, -1);
             final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
             if (type == null) {
                 Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
                 return;
             }
-            final FillResponse fillData = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
+            final FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
             final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
-            final Bundle datasetArgs = dataset == null ? null : dataset.getExtras();
-            final Bundle fillDataArgs = fillData == null ? null : fillData.getExtras();
-
-            // Bundle sent on AutoFillService methods - only set if service provided a bundle
-            final Bundle extras = (datasetArgs == null && fillDataArgs == null)
-                    ? null : new Bundle();
+            final Bundle responseExtras = response == null ? null : response.getExtras();
+            final Bundle datasetExtras = dataset == null ? null : dataset.getExtras();
+            final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
 
             if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId
-                    + ", callbackId=" + callbackId);
+                    + ", sessionId=" + sessionId);
             synchronized (mLock) {
                 switch (type) {
-                    case TYPE_EMULATE:
-                        service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_FILL);
-                        break;
                     case TYPE_SAVE:
-                        if (datasetArgs != null) {
-                            if (DEBUG) Log.d(TAG, "filldata args on save notificataion: " +
-                                    bundleToString(fillDataArgs));
-                            extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, fillDataArgs);
-                        }
-                        if (dataset != null) {
-                            if (DEBUG) Log.d(TAG, "dataset args on save notificataion: " +
-                                    bundleToString(datasetArgs));
-                            extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetArgs);
-                        }
-                        service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_SAVE);
+                        onSaveRequested(userId, responseExtras, datasetExtras);
                         break;
-                    case TYPE_DELETE_CALLBACK:
-                        service.removeServerCallbackLocked(callbackId);
+                    case TYPE_FINISH_SESSION:
+                        onSessionDone(userId, sessionId);
                         break;
                     case TYPE_PICK_DATASET:
-                        service.autoFillApp(callbackId, dataset);
+                        onDatasetPicked(userId, dataset, sessionId);
+
                         // Must cancel notification because it might be comming from action
-                        if (DEBUG) Log.d(TAG, "Cancelling notification");
+                        if (DEBUG) Slog.d(TAG, "Cancelling notification");
                         NotificationManager.from(mContext).cancel(TYPE_OPTIONS, userId);
 
-                        if (datasetArgs != null) {
-                            if (DEBUG) Log.d(TAG, "adding dataset's extra_data on save intent: "
-                                    + bundleToString(datasetArgs));
-                            extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetArgs);
-                        }
-
-                        // Also show notification with option to save the data
-                        showSaveNotification(userId, fillData, dataset);
+                        break;
+                    case TYPE_AUTH_RESPONSE:
+                        onResponseAuthenticationRequested(userId,
+                                intent.getBundleExtra(EXTRA_AUTH_REQUIRED_EXTRAS), flags);
                         break;
                     default: {
                         Slog.w(TAG, "Unknown notification type: " + type);
                     }
                 }
             }
-        }
-    }
-
-    private ComponentName getProviderForUser(int userId) {
-        ComponentName serviceComponent = null;
-        ServiceInfo serviceInfo = null;
-        final String componentName = Settings.Secure.getStringForUser(
-                mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId);
-        if (!TextUtils.isEmpty(componentName)) {
-            try {
-                serviceComponent = ComponentName.unflattenFromString(componentName);
-                serviceInfo =
-                        AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId);
-            } catch (RuntimeException | RemoteException e) {
-                Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e);
-                return null;
-            }
-        }
-
-        if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
-                + serviceComponent + ", info: " + serviceInfo);
-        if (serviceInfo == null) {
-            Slog.w(TAG, "no service info for " + serviceComponent);
-            return null;
-        }
-        return serviceComponent;
-    }
-
-    void showAllNotifications() {
-        final UserManager userManager =
-                (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-
-        final List<UserInfo> allUsers = userManager.getUsers(true);
-
-        for (UserInfo user : allUsers) {
-            final ComponentName serviceComponent = getProviderForUser(user.id);
-            if (serviceComponent != null) {
-                showMainNotification(serviceComponent, user.id);
-            }
-        }
-    }
-
-    void updateNotification(int userId) {
-        final ComponentName serviceComponent = getProviderForUser(userId);
-        if (serviceComponent == null) {
-            cancelMainNotification(userId);
-        } else {
-            showMainNotification(serviceComponent, userId);
+            collapseStatusBar();
         }
     }
 
@@ -265,125 +297,45 @@
         return intent;
     }
 
-    private PendingIntent newPickDatasetPI(int userId, int callbackId, FillResponse response,
+    private PendingIntent newPickDatasetPI(int userId, int sessionId, FillResponse response,
             Dataset dataset) {
         final int resultCode = ++ sResultCode;
-        if (DEBUG) Log.d(TAG, "newPickDatasetPI: userId=" + userId + ", callback=" + callbackId
+        if (DEBUG) Slog.d(TAG, "newPickDatasetPI: userId=" + userId + ", sessionId=" + sessionId
                 + ", resultCode=" + resultCode);
 
         final Intent intent = newNotificationIntent(userId, TYPE_PICK_DATASET);
-        intent.putExtra(EXTRA_CALLBACK_ID, callbackId);
+        intent.putExtra(EXTRA_SESSION_ID, sessionId);
         intent.putExtra(EXTRA_FILL_RESPONSE, response);
         intent.putExtra(EXTRA_DATASET, dataset);
         return PendingIntent.getBroadcast(mContext, resultCode, intent,
                 PendingIntent.FLAG_ONE_SHOT);
     }
 
-    private static String bundleToString(Bundle bundle) {
-        if (bundle == null) {
-            return "null";
-        }
-        final Set<String> keySet = bundle.keySet();
-        final StringBuilder builder = new StringBuilder("[Bundle with ").append(keySet.size())
-                .append(" keys:");
-        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);
-        }
-        return builder.append(']').toString();
-    }
-
-    /**
-     * Shows a permanent notification that triggers the auto-fill workflow for the given user.
-     *
-     * <p>It emulates calling the auto-fill service when the IME is shown.
-     */
-    private void showMainNotification(ComponentName serviceComponent, int userId) {
-        if (DEBUG) Log.d(TAG, "showNotification() for " + userId + ": " + serviceComponent);
-
-        synchronized (mLock) {
-            if (mNotificationReceiver == null) {
-                mNotificationReceiver = new NotificationReceiver();
-                mContext.registerReceiver(mNotificationReceiver,
-                        new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT));
-            }
-        }
-
-        final Intent fillIntent = newNotificationIntent(userId, TYPE_EMULATE);
-        final PendingIntent fillPendingIntent = PendingIntent.getBroadcast(mContext,
-                -1, fillIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-
-        final String packageName = serviceComponent.getPackageName();
-        String providerName = null;
-        final PackageManager pm = mContext.getPackageManager();
-        try {
-            final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId);
-            if (info != null) {
-                providerName = pm.getApplicationLabel(info).toString();
-            }
-        } catch (Exception e) {
-            providerName = packageName;
-        }
-        final String title = "AutoFill IME Emulation";
-        final String subTitle = "Tap notification to start auto-fill workflow (by '" + providerName
-                + "' on top activity on user " + userId + ".\n"
-                + "Once provider replies, a new notification will show your options.";
-
-        final Notification notification = new Notification.Builder(mContext)
-                .setCategory(Notification.CATEGORY_SYSTEM)
-                .setOngoing(true)
-                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                .setLocalOnly(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
-                .setContentTitle(title)
-                .setStyle(new Notification.BigTextStyle().bigText(subTitle))
-                .setContentIntent(fillPendingIntent)
-                .build();
-        NotificationManager.from(mContext).notify(TYPE_EMULATE, userId, notification);
-    }
-
-    /**
-     * Cancels the permament notification created by
-     * {@link #showMainNotification(ComponentName, int)}.
-     */
-    private void cancelMainNotification(int userId) {
-        if (DEBUG) Log.d(TAG, "cancelNotificationLocked(): " + userId);
-        NotificationManager.from(mContext).cancel(TYPE_EMULATE, userId);
-    }
-
     /**
      * Shows a notification with the results of an auto-fill request, using notications actions
      * to emulate the auto-fill bar buttons displaying the dataset names.
      */
-    private void showOptionsNotification(int userId, int callbackId, FillResponse response) {
+    private void showOptionsNotification(int userId, int sessionId, FillResponse response) {
         final long token = Binder.clearCallingIdentity();
         try {
-            showOptionsNotificationAsSystem(userId, callbackId, response);
+            showOptionsNotificationAsSystem(userId, sessionId, response);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
-    private void showOptionsNotificationAsSystem(int userId, int callbackId,
+    private void showOptionsNotificationAsSystem(int userId, int sessionId,
             FillResponse response) {
         // Make sure server callback is removed from cache if user cancels the notification.
-        final Intent deleteIntent = newNotificationIntent(userId, TYPE_DELETE_CALLBACK);
-        deleteIntent.putExtra(EXTRA_CALLBACK_ID, callbackId);
+        final Intent deleteIntent = newNotificationIntent(userId, TYPE_FINISH_SESSION)
+                .putExtra(EXTRA_SESSION_ID, sessionId);
         final PendingIntent deletePendingIntent = PendingIntent.getBroadcast(mContext,
                 ++sResultCode, deleteIntent, PendingIntent.FLAG_ONE_SHOT);
 
         final String title = "AutoFill Options";
 
-        final Notification.Builder notification = new Notification.Builder(mContext)
-                .setCategory(Notification.CATEGORY_SYSTEM)
+        final Notification.Builder notification = newNotificationBuilder()
                 .setOngoing(false)
-                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                .setLocalOnly(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
                 .setDeleteIntent(deletePendingIntent)
                 .setContentTitle(title);
 
@@ -418,10 +370,19 @@
                 autoCancel = false;
                 final int size = datasets.size();
                 subTitle = "There are " + size + " option(s).\n"
-                        + "Use the notification action(s) to select the proper one.";
+                        + "Use the notification action(s) to select the proper one."
+                        + "Actions with (F) require fingerprint unlock, and with (P) require"
+                        + "provider authentication to unlock";
                 for (Dataset dataset : datasets) {
-                    final CharSequence name = dataset.getName();
-                    final PendingIntent pi = newPickDatasetPI(userId, callbackId, response, dataset);
+                    final StringBuilder name = new StringBuilder(dataset.getName());
+                    if (dataset.isAuthRequired()) {
+                        if (dataset.hasCryptoObject()) {
+                            name.append("(F)");
+                        } else {
+                            name.append("(P)");
+                        }
+                    }
+                    final PendingIntent pi = newPickDatasetPI(userId, sessionId, response, dataset);
                     notification.addAction(new Action.Builder(null, name, pi).build());
                 }
             }
@@ -437,9 +398,20 @@
         }
     }
 
-    private void showSaveNotification(int userId, FillResponse response, Dataset dataset) {
+    void showSaveNotification(int userId, FillResponse response, Dataset dataset) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            showSaveNotificationAsSystem(userId, response, dataset);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void showSaveNotificationAsSystem(int userId, FillResponse response, Dataset dataset) {
         final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE);
-        saveIntent.putExtra(EXTRA_FILL_RESPONSE, response);
+        if (response != null) {
+            saveIntent.putExtra(EXTRA_FILL_RESPONSE, response);
+        }
         if (dataset != null) {
             saveIntent.putExtra(EXTRA_DATASET, dataset);
         }
@@ -447,17 +419,15 @@
                 ++sResultCode, saveIntent, PendingIntent.FLAG_ONE_SHOT);
 
         final String title = "AutoFill Save";
-        final String subTitle = "Tap notification to ask provider to save fields: \n"
-                + Arrays.toString(response.getSavableIds());
+        // Response is not set after fillign an authenticated dataset...
+        final String subTitle = response == null
+                ? "Tap notification to ask provider to save fields."
+                : "Tap notification to ask provider to save fields: \n"
+                        + Arrays.toString(response.getSavableIds());
 
-        final Notification notification = new Notification.Builder(mContext)
-                .setCategory(Notification.CATEGORY_SYSTEM)
+        final Notification notification = newNotificationBuilder()
                 .setAutoCancel(true)
                 .setOngoing(false)
-                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                .setLocalOnly(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
                 .setContentTitle(title)
                 .setContentIntent(savePendingIntent)
                 .setStyle(new Notification.BigTextStyle().bigText(subTitle))
@@ -465,6 +435,68 @@
         NotificationManager.from(mContext).notify(TYPE_SAVE, userId, notification);
     }
 
+    private void showAuthNotification(int userId, int sessionId, boolean usesFingerprint,
+            Bundle extras, int flags) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            showAuthNotificationAsSystem(userId, sessionId, usesFingerprint, extras, flags);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void showAuthNotificationAsSystem(int userId, int sessionId,
+            boolean usesFingerprint, Bundle extras, int flags) {
+        final String title = "AutoFill Authentication";
+        final StringBuilder subTitle = new StringBuilder("Provider require user authentication.\n");
+
+        final Intent authIntent = newNotificationIntent(userId, TYPE_AUTH_RESPONSE)
+                .putExtra(EXTRA_SESSION_ID, sessionId);
+        if (extras != null) {
+            authIntent.putExtra(EXTRA_AUTH_REQUIRED_EXTRAS, extras);
+        }
+        if (flags != 0) {
+            authIntent.putExtra(EXTRA_FLAGS, flags);
+        }
+        final PendingIntent authPendingIntent = PendingIntent.getBroadcast(mContext, ++sResultCode,
+                authIntent, PendingIntent.FLAG_ONE_SHOT);
+
+        if (usesFingerprint) {
+            subTitle.append("But kindly accepts your fingerprint instead"
+                    + "\n(tap fingerprint sensor to trigger it)");
+
+        } else {
+            subTitle.append("Tap notification to launch its authentication UI.");
+        }
+
+        final Notification.Builder notification = newNotificationBuilder()
+                .setAutoCancel(true)
+                .setOngoing(false)
+                .setContentTitle(title)
+                .setStyle(new Notification.BigTextStyle().bigText(subTitle.toString()));
+        if (authPendingIntent != null) {
+            notification.setContentIntent(authPendingIntent);
+        }
+        NotificationManager.from(mContext).notify(TYPE_AUTH_RESPONSE, userId, notification.build());
+    }
+
+    private void dismissAuthNotification(int userId) {
+        NotificationManager.from(mContext).cancel(TYPE_AUTH_RESPONSE, userId);
+    }
+
+    private Notification.Builder newNotificationBuilder() {
+        return new Notification.Builder(mContext)
+                .setCategory(Notification.CATEGORY_SYSTEM)
+                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                .setLocalOnly(true)
+                .setColor(mContext.getColor(
+                        com.android.internal.R.color.system_notification_accent_color));
+    }
+
+    private void collapseStatusBar() {
+        final StatusBarManager sbm = (StatusBarManager) mContext.getSystemService("statusbar");
+        sbm.collapsePanels();
+    }
     /////////////////////////////////////////
     // End of temporary notification code. //
     /////////////////////////////////////////
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
new file mode 100644
index 0000000..79095a1
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import android.os.Bundle;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+
+final class Helper {
+
+    static final boolean DEBUG = true; // TODO(b/33197203): set to false when stable
+    static final String REDACTED = "[REDACTED]";
+
+    static void append(StringBuilder builder, Bundle bundle) {
+        if (bundle == null) {
+            builder.append("N/A");
+        } else if (!DEBUG) {
+            builder.append(REDACTED);
+        } else {
+            final Set<String> keySet = bundle.keySet();
+            builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
+            for (String key : keySet) {
+                final Object value = bundle.get(key);
+                builder.append(' ').append(key).append('=');
+                builder.append((value instanceof Object[])
+                        ? Arrays.toString((Objects[]) value) : value);
+            }
+            builder.append(']');
+        }
+    }
+
+    static String bundleToString(Bundle bundle) {
+        final StringBuilder builder = new StringBuilder();
+        append(builder, bundle);
+        return builder.toString();
+    }
+
+    private Helper() {
+        throw new UnsupportedOperationException("contains static members only");
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 31ecb75..88c05b5 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -34,13 +34,15 @@
 import android.app.backup.BackupTransport;
 import android.app.backup.FullBackup;
 import android.app.backup.FullBackupDataOutput;
-import android.app.backup.IBackupObserver;
-import android.app.backup.RestoreDescription;
-import android.app.backup.RestoreSet;
 import android.app.backup.IBackupManager;
+import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreObserver;
 import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.app.backup.RestoreDescription;
+import android.app.backup.RestoreSet;
+import android.app.backup.SelectBackupTransportCallback;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -55,16 +57,15 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.Signature;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Environment.UserEnvironment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -79,15 +80,12 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.os.Environment.UserEnvironment;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
@@ -105,6 +103,8 @@
 import com.android.server.SystemService;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
 
+import libcore.io.IoUtils;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -139,7 +139,6 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Random;
@@ -166,8 +165,6 @@
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
 
-import libcore.io.IoUtils;
-
 public class BackupManagerService {
 
     private static final String TAG = "BackupManagerService";
@@ -271,6 +268,8 @@
     private IStorageManager mStorageManager;
     IBackupManager mBackupManagerBinder;
 
+    private final TransportManager mTransportManager;
+
     boolean mEnabled;   // access to this is synchronized on 'this'
     boolean mProvisioned;
     boolean mAutoRestore;
@@ -322,16 +321,6 @@
     final Object mClearDataLock = new Object();
     volatile boolean mClearingData;
 
-    // Transport bookkeeping
-    final ArraySet<ComponentName> mTransportWhitelist;
-    final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
-    final ArrayMap<String,String> mTransportNames
-            = new ArrayMap<String,String>();             // component name -> registration name
-    final ArrayMap<String,IBackupTransport> mTransports
-            = new ArrayMap<String,IBackupTransport>();   // registration name -> binder
-    final ArrayMap<String,TransportConnection> mTransportConnections
-            = new ArrayMap<String,TransportConnection>();
-    String mCurrentTransport;
     ActiveRestoreSession mActiveRestoreSession;
 
     // Watch the device provisioning operation during setup
@@ -577,15 +566,18 @@
         public ArrayList<String> fullPackages;
         public IBackupObserver observer;
         public boolean userInitiated;
+        public boolean nonIncrementalBackup;
 
         BackupParams(IBackupTransport transport, String dirName, ArrayList<String> kvPackages,
-                ArrayList<String> fullPackages, IBackupObserver observer, boolean userInitiated) {
+                ArrayList<String> fullPackages, IBackupObserver observer, boolean userInitiated,
+                boolean nonIncrementalBackup) {
             this.transport = transport;
             this.dirName = dirName;
             this.kvPackages = kvPackages;
             this.fullPackages = fullPackages;
             this.observer = observer;
             this.userInitiated = userInitiated;
+            this.nonIncrementalBackup = nonIncrementalBackup;
         }
     }
 
@@ -753,7 +745,7 @@
             {
                 mLastBackupPass = System.currentTimeMillis();
 
-                IBackupTransport transport = getTransport(mCurrentTransport);
+                IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
                 if (transport == null) {
                     Slog.v(TAG, "Backup requested but no transport available");
                     synchronized (mQueueLock) {
@@ -794,7 +786,7 @@
                     try {
                         String dirName = transport.transportDirName();
                         PerformBackupTask pbt = new PerformBackupTask(transport, dirName,
-                                queue, oldJournal, null, null, false);
+                                queue, oldJournal, null, null, false, false /* nonIncremental */);
                         Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
                         sendMessage(pbtMessage);
                     } catch (Exception e) {
@@ -1033,7 +1025,8 @@
                 mWakelock.acquire();
 
                 PerformBackupTask pbt = new PerformBackupTask(params.transport, params.dirName,
-                    kvQueue, null, params.observer, params.fullPackages, true);
+                        kvQueue, null, params.observer, params.fullPackages, true,
+                        params.nonIncrementalBackup);
                 Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
                 sendMessage(pbtMessage);
                 break;
@@ -1198,32 +1191,19 @@
         // Set up our transport options and initialize the default transport
         // TODO: Don't create transports that we don't need to?
         SystemConfig systemConfig = SystemConfig.getInstance();
-        mTransportWhitelist = systemConfig.getBackupTransportWhitelist();
+        Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
 
         String transport = Settings.Secure.getString(context.getContentResolver(),
                 Settings.Secure.BACKUP_TRANSPORT);
         if (TextUtils.isEmpty(transport)) {
             transport = null;
         }
-        mCurrentTransport = transport;
-        if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
+        String currentTransport = transport;
+        if (DEBUG) Slog.v(TAG, "Starting with transport " + currentTransport);
 
-        // Find all transport hosts and bind to their services
-        // TODO: http://b/22388012
-        List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
-                mTransportServiceIntent, 0, UserHandle.USER_SYSTEM);
-        if (DEBUG) {
-            Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
-        }
-        if (hosts != null) {
-            for (int i = 0; i < hosts.size(); i++) {
-                final ServiceInfo transportService = hosts.get(i).serviceInfo;
-                if (MORE_DEBUG) {
-                    Slog.v(TAG, "   " + transportService.packageName + "/" + transportService.name);
-                }
-                tryBindTransport(transportService);
-            }
-        }
+        mTransportManager = new TransportManager(context, transportWhitelist, currentTransport,
+                mTransportBoundListener);
+        mTransportManager.registerAllTransports();
 
         // Now that we know about valid backup participants, parse any
         // leftover journal files into the pending backup set
@@ -1747,7 +1727,7 @@
         mBackupHandler.removeMessages(MSG_RETRY_INIT);
 
         try {
-            IBackupTransport transport = getTransport(transportName);
+            IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
             if (transport != null) {
                 String transportDirName = transport.transportDirName();
                 File stateDir = new File(mBaseStateDir, transportDirName);
@@ -1825,49 +1805,39 @@
         }
     }
 
-    // Add a transport to our set of available backends.  If 'transport' is null, this
-    // is an unregistration, and the transport's entry is removed from our bookkeeping.
-    private void registerTransport(String name, String component, IBackupTransport transport) {
-        synchronized (mTransports) {
-            if (DEBUG) Slog.v(TAG, "Registering transport "
-                    + component + "::" + name + " = " + transport);
-            if (transport != null) {
-                mTransports.put(name, transport);
-                mTransportNames.put(component, name);
-            } else {
-                mTransports.remove(mTransportNames.get(component));
-                mTransportNames.remove(component);
-                // Nothing further to do in the unregistration case
-                return;
-            }
-        }
+    private TransportManager.TransportBoundListener mTransportBoundListener =
+            new TransportManager.TransportBoundListener() {
+        @Override
+        public boolean onTransportBound(IBackupTransport transport) {
+            // If the init sentinel file exists, we need to be sure to perform the init
+            // as soon as practical.  We also create the state directory at registration
+            // time to ensure it's present from the outset.
+            String name = null;
+            try {
+                name = transport.name();
+                String transportDirName = transport.transportDirName();
+                File stateDir = new File(mBaseStateDir, transportDirName);
+                stateDir.mkdirs();
 
-        // If the init sentinel file exists, we need to be sure to perform the init
-        // as soon as practical.  We also create the state directory at registration
-        // time to ensure it's present from the outset.
-        try {
-            String transportName = transport.transportDirName();
-            File stateDir = new File(mBaseStateDir, transportName);
-            stateDir.mkdirs();
+                File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+                if (initSentinel.exists()) {
+                    synchronized (mQueueLock) {
+                        mPendingInits.add(name);
 
-            File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
-            if (initSentinel.exists()) {
-                synchronized (mQueueLock) {
-                    mPendingInits.add(name);
-
-                    // TODO: pick a better starting time than now + 1 minute
-                    long delay = 1000 * 60; // one minute, in milliseconds
-                    mAlarmManager.set(AlarmManager.RTC_WAKEUP,
-                            System.currentTimeMillis() + delay, mRunInitIntent);
+                        // TODO: pick a better starting time than now + 1 minute
+                        long delay = 1000 * 60; // one minute, in milliseconds
+                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+                                System.currentTimeMillis() + delay, mRunInitIntent);
+                    }
                 }
+                return true;
+            } catch (Exception e) {
+                // the transport threw when asked its file naming prefs; declare it invalid
+                Slog.w(TAG, "Failed to regiser transport: " + name);
+                return false;
             }
-        } catch (Exception e) {
-            // the transport threw when asked its file naming prefs; declare it invalid
-            Slog.e(TAG, "Unable to register transport as " + name);
-            mTransportNames.remove(component);
-            mTransports.remove(name);
         }
-    }
+    };
 
     // ----- Track installation/removal of packages -----
     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -1895,75 +1865,17 @@
 
                 // At package-changed we only care about looking at new transport states
                 if (changed) {
-                    try {
-                        String[] components =
-                                intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+                    String[] components =
+                            intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
 
-                        if (MORE_DEBUG) {
-                            Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
-                            for (int i = 0; i < components.length; i++) {
-                                Slog.i(TAG, "   * " + components[i]);
-                            }
-                        }
-
-                        // In general we need to try to bind any time we see a component enable
-                        // state change, because that change may have made a transport available.
-                        // However, because we currently only support a single transport component
-                        // per package, we can skip the bind attempt if the change (a) affects a
-                        // package known to host a transport, but (b) does not affect the known
-                        // transport component itself.
-                        //
-                        // In addition, if the change *is* to a known transport component, we need
-                        // to unbind it before retrying the binding.
-                        boolean tryBind = true;
-                        synchronized (mTransports) {
-                            TransportConnection conn = mTransportConnections.get(pkgName);
-                            if (conn != null) {
-                                // We have a bound transport in this package; do we need to rebind it?
-                                final ServiceInfo svc = conn.mTransport;
-                                ComponentName svcName =
-                                        new ComponentName(svc.packageName, svc.name);
-                                if (svc.packageName.equals(pkgName)) {
-                                    final String className = svcName.getClassName();
-                                    if (MORE_DEBUG) {
-                                        Slog.i(TAG, "Checking need to rebind " + className);
-                                    }
-                                    // See whether it's the transport component within this package
-                                    boolean isTransport = false;
-                                    for (int i = 0; i < components.length; i++) {
-                                        if (className.equals(components[i])) {
-                                            // Okay, it's an existing transport component.
-                                            final String flatName = svcName.flattenToShortString();
-                                            mContext.unbindService(conn);
-                                            mTransportConnections.remove(pkgName);
-                                            mTransports.remove(mTransportNames.get(flatName));
-                                            mTransportNames.remove(flatName);
-                                            isTransport = true;
-                                            break;
-                                        }
-                                    }
-                                    if (!isTransport) {
-                                        // A non-transport component within a package that is hosting
-                                        // a bound transport
-                                        tryBind = false;
-                                    }
-                                }
-                            }
-                        }
-                        // and now (re)bind as appropriate
-                        if (tryBind) {
-                            if (MORE_DEBUG) {
-                                Slog.i(TAG, "Yes, need to recheck binding");
-                            }
-                            PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0);
-                            checkForTransportAndBind(app);
-                        }
-                    } catch (NameNotFoundException e) {
-                        // Nope, can't find it - just ignore
-                        if (MORE_DEBUG) {
-                            Slog.w(TAG, "Can't find changed package " + pkgName);
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+                        for (int i = 0; i < components.length; i++) {
+                            Slog.i(TAG, "   * " + components[i]);
                         }
                     }
+
+                    mTransportManager.onPackageChanged(pkgName, components);
                     return; // nothing more to do in the PACKAGE_CHANGED case
                 }
 
@@ -2011,19 +1923,7 @@
                             writeFullBackupScheduleAsync();
                         }
 
-                        // Transport maintenance: rebind to known existing transports that have
-                        // just been updated; and bind to any newly-installed transport services.
-                        synchronized (mTransports) {
-                            final TransportConnection conn = mTransportConnections.get(packageName);
-                            if (conn != null) {
-                                if (MORE_DEBUG) {
-                                    Slog.i(TAG, "Transport package changed; rebinding");
-                                }
-                                bindTransport(conn.mTransport);
-                            } else {
-                                checkForTransportAndBind(app);
-                            }
-                        }
+                        mTransportManager.onPackageAdded(packageName);
 
                     } catch (NameNotFoundException e) {
                         // doesn't really exist; ignore it
@@ -2047,107 +1947,13 @@
                         removePackageParticipantsLocked(pkgList, uid);
                     }
                 }
+                for (String pkgName : pkgList) {
+                    mTransportManager.onPackageRemoved(pkgName);
+                }
             }
         }
     };
 
-    // ----- Track connection to transports service -----
-    class TransportConnection implements ServiceConnection {
-        ServiceInfo mTransport;
-
-        public TransportConnection(ServiceInfo transport) {
-            mTransport = transport;
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName component, IBinder service) {
-            if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
-            final String name = component.flattenToShortString();
-            try {
-                IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
-                registerTransport(transport.name(), name, transport);
-                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1);
-            } catch (Exception e) {
-                Slog.e(TAG, "Unable to register transport " + component
-                        + ": " + e.getMessage());
-                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName component) {
-            if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component);
-            final String name = component.flattenToShortString();
-            EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
-            registerTransport(null, name, null);
-        }
-    };
-
-    // Check whether the given package hosts a transport, and bind if so
-    void checkForTransportAndBind(PackageInfo pkgInfo) {
-        Intent intent = new Intent(mTransportServiceIntent)
-                .setPackage(pkgInfo.packageName);
-        // TODO: http://b/22388012
-        List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
-                intent, 0, UserHandle.USER_SYSTEM);
-        if (hosts != null) {
-            final int N = hosts.size();
-            for (int i = 0; i < N; i++) {
-                final ServiceInfo info = hosts.get(i).serviceInfo;
-                tryBindTransport(info);
-            }
-        }
-    }
-
-    // Verify that the service exists and is hosted by a privileged app, then proceed to bind
-    boolean tryBindTransport(ServiceInfo info) {
-        try {
-            PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
-            if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
-                    != 0) {
-                return bindTransport(info);
-            } else {
-                Slog.w(TAG, "Transport package " + info.packageName + " not privileged");
-            }
-        } catch (NameNotFoundException e) {
-            Slog.w(TAG, "Problem resolving transport package " + info.packageName);
-        }
-        return false;
-    }
-
-    // Actually bind; presumes that we have already validated the transport service
-    boolean bindTransport(ServiceInfo transport) {
-        ComponentName svcName = new ComponentName(transport.packageName, transport.name);
-        if (!mTransportWhitelist.contains(svcName)) {
-            Slog.w(TAG, "Proposed transport " + svcName + " not whitelisted; ignoring");
-            return false;
-        }
-
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "Binding to transport host " + svcName);
-        }
-        Intent intent = new Intent(mTransportServiceIntent);
-        intent.setComponent(svcName);
-
-        TransportConnection connection;
-        synchronized (mTransports) {
-            connection = mTransportConnections.get(transport.packageName);
-            if (null == connection) {
-                connection = new TransportConnection(transport);
-                mTransportConnections.put(transport.packageName, connection);
-            } else {
-                // This is a rebind due to package upgrade.  The service won't be
-                // automatically relaunched for us until we explicitly rebind, but
-                // we need to unbind the now-orphaned original connection.
-                mContext.unbindService(connection);
-            }
-        }
-        // TODO: http://b/22388012
-        return mContext.bindServiceAsUser(intent,
-                connection, Context.BIND_AUTO_CREATE,
-                UserHandle.SYSTEM);
-    }
-
     // Add the backup agents in the given packages to our set of known backup participants.
     // If 'packageNames' is null, adds all backup agents in the whole system.
     void addPackageParticipantsLocked(String[] packageNames) {
@@ -2348,34 +2154,12 @@
         }
     }
 
-    // Return the given transport
-    private IBackupTransport getTransport(String transportName) {
-        synchronized (mTransports) {
-            IBackupTransport transport = mTransports.get(transportName);
-            if (transport == null) {
-                Slog.w(TAG, "Requested unavailable transport: " + transportName);
-            }
-            return transport;
-        }
-    }
-
     // What name is this transport registered under...?
     private String getTransportName(IBackupTransport transport) {
         if (MORE_DEBUG) {
             Slog.v(TAG, "Searching for transport name of " + transport);
         }
-        synchronized (mTransports) {
-            final int N = mTransports.size();
-            for (int i = 0; i < N; i++) {
-                if (mTransports.valueAt(i).equals(transport)) {
-                    if (MORE_DEBUG) {
-                        Slog.v(TAG, "  Name found: " + mTransports.keyAt(i));
-                    }
-                    return mTransports.keyAt(i);
-                }
-            }
-        }
-        return null;
+        return mTransportManager.getTransportName(transport);
     }
 
     // fire off a backup agent, blocking until it attaches or times out
@@ -2492,7 +2276,7 @@
         return token;
     }
 
-    public int requestBackup(String[] packages, IBackupObserver observer) {
+    public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
 
         if (packages == null || packages.length < 1) {
@@ -2501,7 +2285,7 @@
             throw new IllegalArgumentException("No packages are provided for backup");
         }
 
-        IBackupTransport transport = getTransport(mCurrentTransport);
+        IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
         if (transport == null) {
             sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
             return BackupManager.ERROR_TRANSPORT_ABORTED;
@@ -2510,6 +2294,10 @@
         ArrayList<String> fullBackupList = new ArrayList<>();
         ArrayList<String> kvBackupList = new ArrayList<>();
         for (String packageName : packages) {
+            if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+                kvBackupList.add(packageName);
+                continue;
+            }
             try {
                 PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
                         PackageManager.GET_SIGNATURES);
@@ -2543,9 +2331,12 @@
             sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
             return BackupManager.ERROR_TRANSPORT_ABORTED;
         }
+
+        boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
+
         Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
         msg.obj = new BackupParams(transport, dirName, kvBackupList, fullBackupList, observer,
-                true);
+                true, nonIncrementalBackup);
         mBackupHandler.sendMessage(msg);
         return BackupManager.SUCCESS;
     }
@@ -2673,17 +2464,20 @@
         ParcelFileDescriptor mNewState;
         int mStatus;
         boolean mFinished;
-        boolean mUserInitiated;
+        final boolean mUserInitiated;
+        final boolean mNonIncremental;
 
         public PerformBackupTask(IBackupTransport transport, String dirName,
                 ArrayList<BackupRequest> queue, File journal, IBackupObserver observer,
-                ArrayList<String> pendingFullBackups, boolean userInitiated) {
+                ArrayList<String> pendingFullBackups, boolean userInitiated,
+                boolean nonIncremental) {
             mTransport = transport;
             mOriginalQueue = queue;
             mJournal = journal;
             mObserver = observer;
             mPendingFullBackups = pendingFullBackups;
             mUserInitiated = userInitiated;
+            mNonIncremental = nonIncremental;
 
             mStateDir = new File(mBaseStateDir, dirName);
 
@@ -2748,6 +2542,10 @@
             // the way.
             mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
 
+            // When the transport is forcing non-incremental key/value payloads, we send the
+            // metadata only if it explicitly asks for it.
+            boolean skipPm = mNonIncremental;
+
             // The app metadata pseudopackage might also be represented in the
             // backup queue if apps have been added/removed since the last time
             // we performed a backup.  Drop it from the working queue now that
@@ -2758,6 +2556,7 @@
                         Slog.i(TAG, "Metadata in queue; eliding");
                     }
                     mQueue.remove(i);
+                    skipPm = false;
                     break;
                 }
             }
@@ -2785,22 +2584,27 @@
                     }
                 }
 
-                // The package manager doesn't have a proper <application> etc, but since
-                // it's running here in the system process we can just set up its agent
-                // directly and use a synthetic BackupRequest.  We always run this pass
-                // because it's cheap and this way we guarantee that we don't get out of
-                // step even if we're selecting among various transports at run time.
-                if (mStatus == BackupTransport.TRANSPORT_OK) {
-                    PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
-                            mPackageManager);
-                    mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
-                            IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
-                    addBackupTrace("PMBA invoke: " + mStatus);
+                if (skipPm) {
+                    Slog.d(TAG, "Skipping backup of package metadata.");
+                    executeNextState(BackupState.RUNNING_QUEUE);
+                } else {
+                    // The package manager doesn't have a proper <application> etc, but since
+                    // it's running here in the system process we can just set up its agent
+                    // directly and use a synthetic BackupRequest.  We always run this pass
+                    // because it's cheap and this way we guarantee that we don't get out of
+                    // step even if we're selecting among various transports at run time.
+                    if (mStatus == BackupTransport.TRANSPORT_OK) {
+                        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+                                mPackageManager);
+                        mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
+                                IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
+                        addBackupTrace("PMBA invoke: " + mStatus);
 
-                    // Because the PMBA is a local instance, it has already executed its
-                    // backup callback and returned.  Blow away the lingering (spurious)
-                    // pending timeout message for it.
-                    mBackupHandler.removeMessages(MSG_TIMEOUT);
+                        // Because the PMBA is a local instance, it has already executed its
+                        // backup callback and returned.  Blow away the lingering (spurious)
+                        // pending timeout message for it.
+                        mBackupHandler.removeMessages(MSG_TIMEOUT);
+                    }
                 }
 
                 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
@@ -3001,7 +2805,7 @@
                     if (MORE_DEBUG) Slog.d(TAG, "Server requires init; rerunning");
                     addBackupTrace("init required; rerunning");
                     try {
-                        final String name = getTransportName(mTransport);
+                        final String name = mTransportManager.getTransportName(mTransport);
                         if (name != null) {
                             mPendingInits.add(name);
                         } else {
@@ -3066,6 +2870,7 @@
             if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
             addBackupTrace("invoking " + packageName);
 
+            File blankStateName = new File(mStateDir, "blank_state");
             mSavedStateName = new File(mStateDir, packageName);
             mBackupDataName = new File(mDataDir, packageName + ".data");
             mNewStateName = new File(mStateDir, packageName + ".new");
@@ -3088,9 +2893,10 @@
                 }
 
                 // In a full backup, we pass a null ParcelFileDescriptor as
-                // the saved-state "file". This is by definition an incremental,
-                // so we build a saved state file to pass.
-                mSavedState = ParcelFileDescriptor.open(mSavedStateName,
+                // the saved-state "file". For key/value backups we pass the old state if
+                // an incremental backup is required, and a blank state otherwise.
+                mSavedState = ParcelFileDescriptor.open(
+                        mNonIncremental ? blankStateName : mSavedStateName,
                         ParcelFileDescriptor.MODE_READ_ONLY |
                         ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
 
@@ -3120,6 +2926,10 @@
                         e.toString());
                 agentErrorCleanup();
                 return BackupTransport.AGENT_ERROR;
+            } finally {
+                if (mNonIncremental) {
+                    blankStateName.delete();
+                }
             }
 
             // At this point the agent is off and running.  The next thing to happen will
@@ -4473,7 +4283,7 @@
                     return;
                 }
 
-                IBackupTransport transport = getTransport(mCurrentTransport);
+                IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
                 if (transport == null) {
                     Slog.w(TAG, "Transport not present; full data backup not performed");
                     backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
@@ -5089,7 +4899,7 @@
 
                 headBusy = false;
 
-                if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
+                if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
                     if (MORE_DEBUG) {
                         Slog.i(TAG, "Preconditions not met; not running full backup");
                     }
@@ -9085,7 +8895,8 @@
         public void run() {
             try {
                 for (String transportName : mQueue) {
-                    IBackupTransport transport = getTransport(transportName);
+                    IBackupTransport transport =
+                            mTransportManager.getTransportBinder(transportName);
                     if (transport == null) {
                         Slog.e(TAG, "Requested init for " + transportName + " but not found");
                         continue;
@@ -9282,7 +9093,8 @@
             if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
             mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
             synchronized (mQueueLock) {
-                final IBackupTransport transport = getTransport(transportName);
+                final IBackupTransport transport =
+                        mTransportManager.getTransportBinder(transportName);
                 if (transport == null) {
                     // transport is currently unavailable -- make sure to retry
                     Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
@@ -9420,7 +9232,7 @@
             throw new IllegalStateException("Restore supported only for the device owner");
         }
 
-        if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
+        if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
             Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
         } else {
             if (DEBUG) {
@@ -9688,10 +9500,7 @@
                     if (wasEnabled && mProvisioned) {
                         // NOTE: we currently flush every registered transport, not just
                         // the currently-active one.
-                        HashSet<String> allTransports;
-                        synchronized (mTransports) {
-                            allTransports = new HashSet<String>(mTransports.keySet());
-                        }
+                        String[] allTransports = mTransportManager.getBoundTransportNames();
                         // build the set of transports for which we are posting an init
                         for (String transport : allTransports) {
                             recordInitPendingLocked(true, transport);
@@ -9744,36 +9553,27 @@
     public String getCurrentTransport() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getCurrentTransport");
-        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
-        return mCurrentTransport;
+        String currentTransport = mTransportManager.getCurrentTransportName();
+        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
+        return currentTransport;
     }
 
     // Report all known, available backup transports
     public String[] listAllTransports() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
 
-        String[] list = null;
-        ArrayList<String> known = new ArrayList<String>();
-        for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
-            if (entry.getValue() != null) {
-                known.add(entry.getKey());
-            }
-        }
+        return mTransportManager.getBoundTransportNames();
+    }
 
-        if (known.size() > 0) {
-            list = new String[known.size()];
-            known.toArray(list);
-        }
-        return list;
+    public ComponentName[] listAllTransportComponents() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "listAllTransportComponents");
+        return mTransportManager.getAllTransportCompenents();
     }
 
     public String[] getTransportWhitelist() {
         // No permission check, intentionally.
-        String[] whitelist = new String[mTransportWhitelist.size()];
-        for (int i = mTransportWhitelist.size() - 1; i >= 0; i--) {
-            whitelist[i] = mTransportWhitelist.valueAt(i).flattenToShortString();
-        }
-        return whitelist;
+        return mTransportManager.getTransportWhitelist().toArray(new String[0]);
     }
 
     // Select which transport to use for the next backup operation.
@@ -9781,22 +9581,58 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "selectBackupTransport");
 
-        synchronized (mTransports) {
-            final long oldId = Binder.clearCallingIdentity();
-            try {
-                String prevTransport = mCurrentTransport;
-                mCurrentTransport = transport;
-                Settings.Secure.putString(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_TRANSPORT, transport);
-                Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
-                        + " returning " + prevTransport);
-                return prevTransport;
-            } finally {
-                Binder.restoreCallingIdentity(oldId);
-            }
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            String prevTransport = mTransportManager.selectTransport(transport);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.BACKUP_TRANSPORT, transport);
+            Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName()
+                    + " returning " + prevTransport);
+            return prevTransport;
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
         }
     }
 
+    public void selectBackupTransportAsync(final ComponentName transport,
+            final ISelectBackupTransportCallback listener) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "selectBackupTransportAsync");
+
+        final long oldId = Binder.clearCallingIdentity();
+
+        Slog.v(TAG, "selectBackupTransportAsync() called with transport " +
+                transport.flattenToShortString());
+
+        mTransportManager.ensureTransportReady(transport, new SelectBackupTransportCallback() {
+            @Override
+            public void onSuccess(String transportName) {
+                mTransportManager.selectTransport(transportName);
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_TRANSPORT,
+                        mTransportManager.getCurrentTransportName());
+                Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString());
+                try {
+                    listener.onSuccess(transportName);
+                } catch (RemoteException e) {
+                    // Nothing to do here.
+                }
+            }
+
+            @Override
+            public void onFailure(int reason) {
+                Slog.v(TAG, "Failed to select transport: " + transport.flattenToShortString());
+                try {
+                    listener.onFailure(reason);
+                } catch (RemoteException e) {
+                    // Nothing to do here.
+                }
+            }
+        });
+
+        Binder.restoreCallingIdentity(oldId);
+    }
+
     // Supply the configuration Intent for the given transport.  If the name is not one
     // of the available transports, or if the transport does not supply any configuration
     // UI, the method returns null.
@@ -9804,18 +9640,16 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getConfigurationIntent");
 
-        synchronized (mTransports) {
-            final IBackupTransport transport = mTransports.get(transportName);
-            if (transport != null) {
-                try {
-                    final Intent intent = transport.configurationIntent();
-                    if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
-                            + intent);
-                    return intent;
-                } catch (Exception e) {
-                    /* fall through to return null */
-                    Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
-                }
+        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
+        if (transport != null) {
+            try {
+                final Intent intent = transport.configurationIntent();
+                if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
+                        + intent);
+                return intent;
+            } catch (Exception e) {
+                /* fall through to return null */
+                Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
             }
         }
 
@@ -9831,17 +9665,15 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getDestinationString");
 
-        synchronized (mTransports) {
-            final IBackupTransport transport = mTransports.get(transportName);
-            if (transport != null) {
-                try {
-                    final String text = transport.currentDestinationString();
-                    if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
-                    return text;
-                } catch (Exception e) {
-                    /* fall through to return null */
-                    Slog.e(TAG, "Unable to get string from transport: " + e.getMessage());
-                }
+        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
+        if (transport != null) {
+            try {
+                final String text = transport.currentDestinationString();
+                if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
+                return text;
+            } catch (Exception e) {
+                /* fall through to return null */
+                Slog.e(TAG, "Unable to get string from transport: " + e.getMessage());
             }
         }
 
@@ -9853,18 +9685,16 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getDataManagementIntent");
 
-        synchronized (mTransports) {
-            final IBackupTransport transport = mTransports.get(transportName);
-            if (transport != null) {
-                try {
-                    final Intent intent = transport.dataManagementIntent();
-                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
-                            + intent);
-                    return intent;
-                } catch (Exception e) {
-                    /* fall through to return null */
-                    Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
-                }
+        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
+        if (transport != null) {
+            try {
+                final Intent intent = transport.dataManagementIntent();
+                if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
+                        + intent);
+                return intent;
+            } catch (Exception e) {
+                /* fall through to return null */
+                Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
             }
         }
 
@@ -9877,17 +9707,15 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getDataManagementLabel");
 
-        synchronized (mTransports) {
-            final IBackupTransport transport = mTransports.get(transportName);
-            if (transport != null) {
-                try {
-                    final String text = transport.dataManagementLabel();
-                    if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
-                    return text;
-                } catch (Exception e) {
-                    /* fall through to return null */
-                    Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
-                }
+        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
+        if (transport != null) {
+            try {
+                final String text = transport.dataManagementLabel();
+                if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
+                return text;
+            } catch (Exception e) {
+                /* fall through to return null */
+                Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
             }
         }
 
@@ -9949,7 +9777,7 @@
         }
 
         // Do we have a transport to fetch data for us?
-        IBackupTransport transport = getTransport(mCurrentTransport);
+        IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
         if (transport == null) {
             if (DEBUG) Slog.w(TAG, "No transport");
             skip = true;
@@ -10003,7 +9831,7 @@
 
         boolean needPermission = true;
         if (transport == null) {
-            transport = mCurrentTransport;
+            transport = mTransportManager.getCurrentTransportName();
 
             if (packageName != null) {
                 PackageInfo app = null;
@@ -10097,7 +9925,7 @@
                     appIsStopped(packageInfo.applicationInfo)) {
                 return false;
             }
-            IBackupTransport transport = getTransport(mCurrentTransport);
+            IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
             if (transport != null) {
                 try {
                     return transport.isAppEligibleForBackup(packageInfo,
@@ -10126,7 +9954,7 @@
 
         ActiveRestoreSession(String packageName, String transport) {
             mPackageName = packageName;
-            mRestoreTransport = getTransport(transport);
+            mRestoreTransport = mTransportManager.getTransportBinder(transport);
         }
 
         public void markTimedOut() {
@@ -10485,7 +10313,7 @@
             pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
 
             pw.println("Transport whitelist:");
-            for (ComponentName transport : mTransportWhitelist) {
+            for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
                 pw.print("    ");
                 pw.println(transport.flattenToShortString());
             }
@@ -10494,9 +10322,9 @@
             final String[] transports = listAllTransports();
             if (transports != null) {
                 for (String t : listAllTransports()) {
-                    pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
+                    pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? "  * " : "    ") + t);
                     try {
-                        IBackupTransport transport = getTransport(t);
+                        IBackupTransport transport = mTransportManager.getTransportBinder(t);
                         File dir = new File(mBaseStateDir, transport.transportDirName());
                         pw.println("       destination: " + transport.currentDestinationString());
                         pw.println("       intent: " + transport.configurationIntent());
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 312b878..a1a2c95 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -20,6 +20,8 @@
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
@@ -275,6 +277,12 @@
     }
 
     @Override
+    public ComponentName[] listAllTransportComponents() throws RemoteException {
+        BackupManagerService svc = mService;
+        return (svc != null) ? svc.listAllTransportComponents() : null;
+    }
+
+    @Override
     public String[] getTransportWhitelist() {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getTransportWhitelist() : null;
@@ -287,6 +295,15 @@
     }
 
     @Override
+    public void selectBackupTransportAsync(ComponentName transport,
+            ISelectBackupTransportCallback listener) throws RemoteException {
+        BackupManagerService svc = mService;
+        if (svc != null) {
+            svc.selectBackupTransportAsync(transport, listener);
+        }
+    }
+
+    @Override
     public Intent getConfigurationIntent(String transport) throws RemoteException {
         BackupManagerService svc = mService;
         return (svc != null) ? svc.getConfigurationIntent(transport) : null;
@@ -338,9 +355,10 @@
     }
 
     @Override
-    public int requestBackup(String[] packages, IBackupObserver observer) throws RemoteException {
+    public int requestBackup(String[] packages, IBackupObserver observer, int flags)
+            throws RemoteException {
         BackupManagerService svc = mService;
-        return (svc != null) ? svc.requestBackup(packages, observer) : null;
+        return (svc != null) ? svc.requestBackup(packages, observer, flags) : null;
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
new file mode 100644
index 0000000..93d5a1e
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import android.app.backup.BackupManager;
+import android.app.backup.SelectBackupTransportCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.EventLogTags;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Handles in-memory bookkeeping of all BackupTransport objects.
+ */
+class TransportManager {
+
+    private static final String TAG = "BackupTransportManager";
+
+    private static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
+
+    private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final Set<ComponentName> mTransportWhitelist;
+
+    /**
+     * This listener is called after we bind to any transport. If it returns true, this is a valid
+     * transport.
+     */
+    private final TransportBoundListener mTransportBoundListener;
+
+    private String mCurrentTransportName;
+
+    /** Lock on this before accessing mValidTransports and mBoundTransports. */
+    private final Object mTransportLock = new Object();
+
+    /**
+     * We have detected these transports on the device. Unless in exceptional cases, we are also
+     * bound to all of these.
+     */
+    @GuardedBy("mTransportLock")
+    private final Map<ComponentName, TransportConnection> mValidTransports = new ArrayMap<>();
+
+    /** We are currently bound to these transports. */
+    @GuardedBy("mTransportLock")
+    private final Map<String, ComponentName> mBoundTransports = new ArrayMap<>();
+
+    TransportManager(Context context, Set<ComponentName> whitelist, String defaultTransport,
+            TransportBoundListener listener) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mTransportWhitelist = whitelist;
+        mCurrentTransportName = defaultTransport;
+        mTransportBoundListener = listener;
+    }
+
+    void onPackageAdded(String packageName) {
+        // New package added. Bind to all transports it contains.
+        synchronized (mTransportLock) {
+            log_verbose("Package added. Binding to all transports. " + packageName);
+            bindToAllInternal(packageName, null /* all components */);
+        }
+    }
+
+    void onPackageRemoved(String packageName) {
+        // Package removed. Remove all its transports from our list. These transports have already
+        // been removed from mBoundTransports because onServiceDisconnected would already been
+        // called on TransportConnection objects.
+        synchronized (mTransportLock) {
+            for (ComponentName transport : mValidTransports.keySet()) {
+                if (transport.getPackageName().equals(packageName)) {
+                    TransportConnection removed = mValidTransports.remove(transport);
+                    if (removed != null) {
+                        mContext.unbindService(removed);
+                        log_verbose("Package removed, Removing transport: " +
+                                transport.flattenToShortString());
+                    }
+                }
+            }
+        }
+    }
+
+    void onPackageChanged(String packageName, String[] components) {
+        synchronized (mTransportLock) {
+            // Remove all changed components from mValidTransports. We'll bind to them again
+            // and re-add them if still valid.
+            for (String component : components) {
+                ComponentName componentName = new ComponentName(packageName, component);
+                TransportConnection removed = mValidTransports.remove(componentName);
+                if (removed != null) {
+                    mContext.unbindService(removed);
+                    log_verbose("Package changed. Removing transport: " +
+                            componentName.flattenToShortString());
+                }
+            }
+            bindToAllInternal(packageName, components);
+        }
+    }
+
+    IBackupTransport getTransportBinder(String transportName) {
+        synchronized (mTransportLock) {
+            ComponentName component = mBoundTransports.get(transportName);
+            if (component == null) {
+                Slog.w(TAG, "Transport " + transportName + " not bound.");
+                return null;
+            }
+            TransportConnection conn = mValidTransports.get(component);
+            if (conn == null) {
+                Slog.w(TAG, "Transport " + transportName + " not valid.");
+                return null;
+            }
+            return conn.getBinder();
+        }
+    }
+
+    IBackupTransport getCurrentTransportBinder() {
+        return getTransportBinder(mCurrentTransportName);
+    }
+
+    String getTransportName(IBackupTransport binder) {
+        synchronized (mTransportLock) {
+            for (TransportConnection conn : mValidTransports.values()) {
+                if (conn.getBinder() == binder) {
+                    return conn.getName();
+                }
+            }
+        }
+        return null;
+    }
+
+    String[] getBoundTransportNames() {
+        synchronized (mTransportLock) {
+            return mBoundTransports.keySet().toArray(new String[0]);
+        }
+    }
+
+    ComponentName[] getAllTransportCompenents() {
+        synchronized (mTransportLock) {
+            return mValidTransports.keySet().toArray(new ComponentName[0]);
+        }
+    }
+
+    String getCurrentTransportName() {
+        return mCurrentTransportName;
+    }
+
+    Set<ComponentName> getTransportWhitelist() {
+        return mTransportWhitelist;
+    }
+
+    String selectTransport(String transport) {
+        synchronized (mTransportLock) {
+            String prevTransport = mCurrentTransportName;
+            mCurrentTransportName = transport;
+            return prevTransport;
+        }
+    }
+
+    void ensureTransportReady(ComponentName transportComponent, SelectBackupTransportCallback listener) {
+        synchronized (mTransportLock) {
+            TransportConnection conn = mValidTransports.get(transportComponent);
+            if (conn == null) {
+                listener.onFailure(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
+                return;
+            }
+            // Transport can be unbound if the process hosting it crashed.
+            conn.bindIfUnbound();
+            conn.addListener(listener);
+        }
+    }
+
+    void registerAllTransports() {
+        bindToAllInternal(null /* all packages */, null /* all components */);
+    }
+
+    /**
+     * Bind to all transports belonging to the given package and the given component list.
+     * null acts a wildcard.
+     *
+     * If packageName is null, bind to all transports in all packages.
+     * If components is null, bind to all transports in the given package.
+     */
+    private void bindToAllInternal(String packageName, String[] components) {
+        PackageInfo pkgInfo = null;
+        if (packageName != null) {
+            try {
+                pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
+            } catch (PackageManager.NameNotFoundException e) {
+                Slog.w(TAG, "Package not found: " + packageName);
+                return;
+            }
+        }
+
+        Intent intent = new Intent(mTransportServiceIntent);
+        if (packageName != null) {
+            intent.setPackage(packageName);
+        }
+
+        List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
+                intent, 0, UserHandle.USER_SYSTEM);
+        if (hosts != null) {
+            for (ResolveInfo host : hosts) {
+                final ServiceInfo info = host.serviceInfo;
+                boolean shouldBind = false;
+                if (components != null && packageName != null) {
+                    for (String component : components) {
+                        ComponentName cn = new ComponentName(pkgInfo.packageName, component);
+                        if (info.getComponentName().equals(cn)) {
+                            shouldBind = true;
+                            break;
+                        }
+                    }
+                } else {
+                    shouldBind = true;
+                }
+                if (shouldBind && isTransportTrusted(info.getComponentName())) {
+                    tryBindTransport(info);
+                }
+            }
+        }
+    }
+
+    /** Transport has to be whitelisted and privileged. */
+    private boolean isTransportTrusted(ComponentName transport) {
+        if (!mTransportWhitelist.contains(transport)) {
+            Slog.w(TAG, "BackupTransport " + transport.flattenToShortString() +
+                    " not whitelisted.");
+            return false;
+        }
+        try {
+            PackageInfo packInfo = mPackageManager.getPackageInfo(transport.getPackageName(), 0);
+            if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+                    == 0) {
+                Slog.w(TAG, "Transport package " + transport.getPackageName() + " not privileged");
+                return false;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Package not found.", e);
+            return false;
+        }
+        return true;
+    }
+
+    private void tryBindTransport(ServiceInfo transport) {
+        Slog.d(TAG, "Binding to transport: " + transport.getComponentName().flattenToShortString());
+        // TODO: b/22388012 (Multi user backup and restore)
+        TransportConnection connection = new TransportConnection(transport.getComponentName());
+        if (bindToTransport(transport.getComponentName(), connection)) {
+            synchronized (mTransportLock) {
+                mValidTransports.put(transport.getComponentName(), connection);
+            }
+        } else {
+            Slog.w(TAG, "Couldn't bind to transport " + transport.getComponentName());
+        }
+    }
+
+    private boolean bindToTransport(ComponentName componentName, ServiceConnection connection) {
+        Intent intent = new Intent(mTransportServiceIntent)
+                .setComponent(componentName);
+        return mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
+                UserHandle.SYSTEM);
+    }
+
+    private class TransportConnection implements ServiceConnection {
+
+        // Hold mTransportsLock to access these fields so as to provide a consistent view of them.
+        private IBackupTransport mBinder;
+        private final List<SelectBackupTransportCallback> mListeners = new ArrayList<>();
+        private String mTransportName;
+
+        private final ComponentName mTransportComponent;
+
+        private TransportConnection(ComponentName transportComponent) {
+            mTransportComponent = transportComponent;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder binder) {
+            synchronized (mTransportLock) {
+                mBinder = IBackupTransport.Stub.asInterface(binder);
+                boolean success = false;
+
+                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
+                    component.flattenToShortString(), 1);
+
+                try {
+                    mTransportName = mBinder.name();
+                    // BackupManager requests some fields from the transport. If they are
+                    // invalid, throw away this transport.
+                    success = mTransportBoundListener.onTransportBound(mBinder);
+                } catch (RemoteException e) {
+                    success = false;
+                    Slog.e(TAG, "Couldn't get transport name.", e);
+                } finally {
+                    if (success) {
+                        Slog.d(TAG, "Bound to transport: " + component.flattenToShortString());
+                        mBoundTransports.put(mTransportName, component);
+                        for (SelectBackupTransportCallback listener : mListeners) {
+                            listener.onSuccess(mTransportName);
+                        }
+                    } else {
+                        Slog.w(TAG, "Bound to transport " + component.flattenToShortString() +
+                                " but it is invalid");
+                        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
+                                component.flattenToShortString(), 0);
+                        mContext.unbindService(this);
+                        mValidTransports.remove(component);
+                        mBinder = null;
+                        for (SelectBackupTransportCallback listener : mListeners) {
+                            listener.onFailure(BackupManager.ERROR_TRANSPORT_INVALID);
+                        }
+                    }
+                    mListeners.clear();
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            synchronized (mTransportLock) {
+                mBinder = null;
+                mBoundTransports.remove(mTransportName);
+            }
+            EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
+                    component.flattenToShortString(), 0);
+            Slog.w(TAG, "Disconnected from transport " + component.flattenToShortString());
+        }
+
+        private IBackupTransport getBinder() {
+            synchronized (mTransportLock) {
+                return mBinder;
+            }
+        }
+
+        private String getName() {
+            synchronized (mTransportLock) {
+                return mTransportName;
+            }
+        }
+
+        private void bindIfUnbound() {
+            synchronized (mTransportLock) {
+                if (mBinder == null) {
+                    Slog.d(TAG,
+                            "Rebinding to transport " + mTransportComponent.flattenToShortString());
+                    bindToTransport(mTransportComponent, this);
+                }
+            }
+        }
+
+        private void addListener(SelectBackupTransportCallback listener) {
+            synchronized (mTransportLock) {
+                if (mBinder == null) {
+                    // We are waiting for bind to complete. If mBinder is set to null after the bind
+                    // is complete due to transport being invalid, we won't find 'this' connection
+                    // object in mValidTransports list and this function can't be called.
+                    mListeners.add(listener);
+                } else {
+                    listener.onSuccess(mTransportName);
+                }
+            }
+        }
+    }
+
+    interface TransportBoundListener {
+        /** Should return true if this is a valid transport. */
+        boolean onTransportBound(IBackupTransport binder);
+    }
+
+    private static void log_verbose(String message) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Slog.v(TAG, message);
+        }
+    }
+}
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 07f14d4..cd88b85 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -25,8 +25,8 @@
     android.hardware.tv.cec@1.0-java
 
 LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update2 \
-    android.hidl.base@1.0-java \
-    android.hardware.biometrics.fingerprint@2.1-java \
+    android.hidl.base@1.0-java-static \
+    android.hardware.biometrics.fingerprint@2.1-java-static \
 
 ifneq ($(INCREMENTAL_BUILDS),)
     LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 581aa05..b1560e6 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -275,7 +275,7 @@
                 } catch (IllegalArgumentException e) {
                     // Failed to parse the settings string, log this and move on
                     // with defaults.
-                    Slog.e(TAG, "Bad device idle settings", e);
+                    Slog.e(TAG, "Bad alarm manager settings", e);
                 }
 
                 MIN_FUTURITY = mParser.getLong(KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY);
@@ -504,8 +504,8 @@
             for (int i = alarms.size()-1; i >= 0; i--) {
                 Alarm alarm = alarms.get(i);
                 try {
-                    if (alarm.uid == uid && ActivityManager.getService().getAppStartMode(
-                            uid, alarm.packageName) == ActivityManager.APP_START_MODE_DISABLED) {
+                    if (alarm.uid == uid && ActivityManager.getService().isAppStartModeDisabled(
+                            uid, alarm.packageName)) {
                         alarms.remove(i);
                         didRemove = true;
                         if (alarm.alarmClock != null) {
@@ -1089,8 +1089,7 @@
                 operation, directReceiver, listenerTag, workSource, flags, alarmClock,
                 callingUid, callingPackage);
         try {
-            if (ActivityManager.getService().getAppStartMode(callingUid, callingPackage)
-                    == ActivityManager.APP_START_MODE_DISABLED) {
+            if (ActivityManager.getService().isAppStartModeDisabled(callingUid, callingPackage)) {
                 Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a
                         + " -- package not allowed to start");
                 return;
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 570843e..dc0e3e1 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -41,6 +41,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.media.AudioAttributes;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -294,6 +295,25 @@
             }
         }
 
+        PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
+        packageManagerInternal.setExternalSourcesPolicy(
+                new PackageManagerInternal.ExternalSourcesPolicy() {
+                    @Override
+                    public int getPackageTrustedToInstallApps(String packageName, int uid) {
+                        int appOpMode = checkOperation(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
+                                uid, packageName);
+                        switch (appOpMode) {
+                            case AppOpsManager.MODE_ALLOWED:
+                                return PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
+                            case AppOpsManager.MODE_ERRORED:
+                                return PackageManagerInternal.ExternalSourcesPolicy.USER_BLOCKED;
+                            default:
+                                return PackageManagerInternal.ExternalSourcesPolicy.USER_DEFAULT;
+                        }
+                    }
+                });
+
         StorageManagerInternal storageManagerInternal = LocalServices.getService(
                 StorageManagerInternal.class);
         storageManagerInternal.addExternalStoragePolicy(
@@ -881,11 +901,9 @@
             }
             code = AppOpsManager.opToSwitch(code);
             UidState uidState = getUidStateLocked(uid, false);
-            if (uidState != null && uidState.opModes != null) {
-                final int uidMode = uidState.opModes.get(code);
-                if (uidMode != AppOpsManager.MODE_ALLOWED) {
-                    return uidMode;
-                }
+            if (uidState != null && uidState.opModes != null
+                    && uidState.opModes.indexOfKey(code) >= 0) {
+                return uidState.opModes.get(code);
             }
             Op op = getOpLocked(code, uid, resolvedPackageName, false);
             if (op == null) {
@@ -2126,6 +2144,7 @@
                 UidState uidState = mUidStates.valueAt(i);
 
                 pw.print("  Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":");
+                needSep = true;
 
                 SparseIntArray opModes = uidState.opModes;
                 if (opModes != null) {
@@ -2166,6 +2185,55 @@
                     }
                 }
             }
+            if (needSep) {
+                pw.println();
+            }
+
+            final int userRestrictionCount = mOpUserRestrictions.size();
+            for (int i = 0; i < userRestrictionCount; i++) {
+                IBinder token = mOpUserRestrictions.keyAt(i);
+                ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
+                pw.println("  User restrictions for token " + token + ":");
+
+                final int restrictionCount = restrictionState.perUserRestrictions != null
+                        ? restrictionState.perUserRestrictions.size() : 0;
+                if (restrictionCount > 0) {
+                    pw.println("      Restricted ops:");
+                    for (int j = 0; j < restrictionCount; j++) {
+                        int userId = restrictionState.perUserRestrictions.keyAt(j);
+                        boolean[] restrictedOps = restrictionState.perUserRestrictions.valueAt(j);
+                        if (restrictedOps == null) {
+                            continue;
+                        }
+                        StringBuilder restrictedOpsValue = new StringBuilder();
+                        restrictedOpsValue.append("[");
+                        final int restrictedOpCount = restrictedOps.length;
+                        for (int k = 0; k < restrictedOpCount; k++) {
+                            if (restrictedOps[k]) {
+                                if (restrictedOpsValue.length() > 1) {
+                                    restrictedOpsValue.append(", ");
+                                }
+                                restrictedOpsValue.append(AppOpsManager.opToName(k));
+                            }
+                        }
+                        restrictedOpsValue.append("]");
+                        pw.print("        "); pw.print("user: "); pw.print(userId);
+                                pw.print(" restricted ops: "); pw.println(restrictedOpsValue);
+                    }
+                }
+
+                final int excludedPackageCount = restrictionState.perUserExcludedPackages != null
+                        ? restrictionState.perUserExcludedPackages.size() : 0;
+                if (excludedPackageCount > 0) {
+                    pw.println("      Excluded packages:");
+                    for (int j = 0; j < excludedPackageCount; j++) {
+                        int userId = restrictionState.perUserExcludedPackages.keyAt(j);
+                        String[] packageNames = restrictionState.perUserExcludedPackages.valueAt(j);
+                        pw.print("        "); pw.print("user: "); pw.print(userId);
+                                pw.print(" packages: "); pw.println(Arrays.toString(packageNames));
+                    }
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index e7f1d16..f315553 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -50,6 +50,7 @@
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -59,6 +60,8 @@
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
 
+import com.android.server.pm.PackageManagerService;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.concurrent.ConcurrentHashMap;
@@ -218,6 +221,11 @@
         @Override
         public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
                 Bundle prevRestrictions) {
+            if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)
+                    && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) {
+                // The relevant restriction has not changed - do nothing.
+                return;
+            }
             final boolean bluetoothDisallowed =
                     newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH);
             if ((mEnable || mEnableExternal) && bluetoothDisallowed) {
@@ -228,6 +236,7 @@
                             e);
                 }
             }
+            updateOppLauncherComponentState(bluetoothDisallowed);
         }
     };
 
@@ -953,7 +962,13 @@
         UserManagerInternal userManagerInternal =
                 LocalServices.getService(UserManagerInternal.class);
         userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
-        if (isBluetoothDisallowed()) {
+        final boolean isBluetoothDisallowed = isBluetoothDisallowed();
+        PackageManagerService packageManagerService =
+                (PackageManagerService) ServiceManager.getService("package");
+        if (packageManagerService != null && !packageManagerService.isOnlyCoreApps()) {
+            updateOppLauncherComponentState(isBluetoothDisallowed);
+        }
+        if (isBluetoothDisallowed) {
             return;
         }
         if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
@@ -2011,6 +2026,28 @@
         }
     }
 
+    /**
+     * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not
+     * offered to the user if Bluetooth is disallowed. Puts the component to its default state if
+     * Bluetooth is not disallowed.
+     *
+     * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user
+     * restriction was set.
+     */
+    private void updateOppLauncherComponentState(boolean bluetoothDisallowed) {
+        final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth",
+                "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
+        final int newState = bluetoothDisallowed
+                ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        try {
+            mContext.getPackageManager()
+                    .setComponentEnabledSetting(oppLauncherComponent, newState, 0);
+        } catch (Exception e) {
+            // The component was not found, do nothing.
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
@@ -2034,21 +2071,32 @@
                 writer.println("  time since enabled: " + onDurationString + "\n");
             }
 
-            writer.println("Enable log:");
-            for (ActiveLog log : mActiveLogs) {
-                writer.println(log);
+            if (mActiveLogs.size() == 0) {
+                writer.println("Bluetooth never enabled!");
+            } else {
+                writer.println("Enable log:");
+                for (ActiveLog log : mActiveLogs) {
+                    writer.println("  " + log);
+                }
             }
 
-            writer.println("\n" + mBleApps.size() + " BLE Apps registered:");
+            String bleAppString = "No BLE Apps registered.";
+            if (mBleApps.size() == 1) {
+                bleAppString = "1 BLE App registered:";
+            } else if (mBleApps.size() > 1) {
+                bleAppString = mBleApps.size() + " BLE Apps registered:";
+            }
+            writer.println("\n" + bleAppString);
             for (ClientDeathRecipient app : mBleApps.values()) {
-                writer.println(app.getPackageName());
+                writer.println("  " + app.getPackageName());
             }
 
+            writer.println("");
             writer.flush();
             if (args.length == 0) {
-              // Add arg to produce output
-              args = new String[1];
-              args[0] = "--print";
+                // Add arg to produce output
+                args = new String[1];
+                args[0] = "--print";
             }
         }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0a088e9..f3f8da8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3121,10 +3121,7 @@
                 Settings.Global.TETHER_SUPPORTED, defaultVal) != 0)
                 && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
         return tetherEnabledInSettings && mUserManager.isAdminUser() &&
-                ((mTethering.getTetherableUsbRegexs().length != 0 ||
-                mTethering.getTetherableWifiRegexs().length != 0 ||
-                mTethering.getTetherableBluetoothRegexs().length != 0) &&
-                mTethering.getUpstreamIfaceTypes().length != 0);
+               mTethering.hasTetherableConfiguration();
     }
 
     @Override
@@ -5515,6 +5512,18 @@
                 }
             }
 
+            // Turn Always-on VPN off
+            if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    mKeyStore.delete(Credentials.LOCKDOWN_VPN);
+                    mLockdownEnabled = false;
+                    setLockdownTracker(null);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+
             // Turn VPN off
             VpnConfig vpnConfig = getVpnConfig(userId);
             if (vpnConfig != null) {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 9f63e30..97edb15 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -114,6 +115,7 @@
 
     private AlarmManager mAlarmManager;
     private IBatteryStats mBatteryStats;
+    private ActivityManagerInternal mLocalActivityManager;
     private PowerManagerInternal mLocalPowerManager;
     private PowerManager mPowerManager;
     private ConnectivityService mConnectivityService;
@@ -1272,6 +1274,11 @@
             DeviceIdleController.this.setAlarmsActive(active);
         }
 
+        /** Is the app on any of the power save whitelists, whether system or user? */
+        public boolean isAppOnWhitelist(int appid) {
+            return DeviceIdleController.this.isAppOnWhitelistInternal(appid);
+        }
+
         /**
          * Returns the array of app ids whitelisted by user. Take care not to
          * modify this, as it is a reference to the original copy. But the reference
@@ -1289,6 +1296,12 @@
         mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
     }
 
+    boolean isAppOnWhitelistInternal(int appid) {
+        synchronized (this) {
+            return Arrays.binarySearch(mPowerSaveWhitelistAllAppIdArray, appid) >= 0;
+        }
+    }
+
     int[] getPowerSaveWhitelistUserAppIds() {
         synchronized (this) {
             return mPowerSaveWhitelistUserAppIdArray;
@@ -1362,6 +1375,7 @@
             synchronized (this) {
                 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
                 mBatteryStats = BatteryStatsService.getService();
+                mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
                 mLocalPowerManager = getLocalService(PowerManagerInternal.class);
                 mPowerManager = getContext().getSystemService(PowerManager.class);
                 mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -1431,6 +1445,7 @@
                 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
                 getContext().registerReceiver(mReceiver, filter);
 
+                mLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                 mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
                 mDisplayManager.registerDisplayListener(mDisplayListener, null);
@@ -1652,7 +1667,7 @@
                 } catch (RemoteException e) {
                 }
                 postTempActiveTimeoutMessage(appId, duration);
-                updateTempWhitelistAppIdsLocked();
+                updateTempWhitelistAppIdsLocked(appId, true);
                 if (mNetworkPolicyTempWhitelistCallback != null) {
                     if (!sync) {
                         mHandler.post(mNetworkPolicyTempWhitelistCallback);
@@ -1698,7 +1713,7 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Removing UID " + uid + " from temp whitelist");
                 }
-                updateTempWhitelistAppIdsLocked();
+                updateTempWhitelistAppIdsLocked(uid, false);
                 if (mNetworkPolicyTempWhitelistCallback != null) {
                     mHandler.post(mNetworkPolicyTempWhitelistCallback);
                 }
@@ -2318,6 +2333,13 @@
                 mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);
         mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null,
                 mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds);
+        if (mLocalActivityManager != null) {
+            if (DEBUG) {
+                Slog.d(TAG, "Setting activity manager whitelist to "
+                        + Arrays.toString(mPowerSaveWhitelistAllAppIdArray));
+            }
+            mLocalActivityManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
+        }
         if (mLocalPowerManager != null) {
             if (DEBUG) {
                 Slog.d(TAG, "Setting wakelock whitelist to "
@@ -2334,7 +2356,7 @@
         }
     }
 
-    private void updateTempWhitelistAppIdsLocked() {
+    private void updateTempWhitelistAppIdsLocked(int appId, boolean adding) {
         final int size = mTempWhitelistAppIdEndTimes.size();
         if (mTempWhitelistAppIdArray.length != size) {
             mTempWhitelistAppIdArray = new int[size];
@@ -2342,6 +2364,14 @@
         for (int i = 0; i < size; i++) {
             mTempWhitelistAppIdArray[i] = mTempWhitelistAppIdEndTimes.keyAt(i);
         }
+        if (mLocalActivityManager != null) {
+            if (DEBUG) {
+                Slog.d(TAG, "Setting activity manager temp whitelist to "
+                        + Arrays.toString(mTempWhitelistAppIdArray));
+            }
+            mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, appId,
+                    adding);
+        }
         if (mLocalPowerManager != null) {
             if (DEBUG) {
                 Slog.d(TAG, "Setting wakelock temp whitelist to "
@@ -2512,8 +2542,9 @@
         pw.println("    Add (prefix with +) or remove (prefix with -) packages.");
         pw.println("  tempwhitelist");
         pw.println("    Print packages that are temporarily whitelisted.");
-        pw.println("  tempwhitelist [-u] [package ..]");
-        pw.println("    Temporarily place packages in whitelist for 10 seconds.");
+        pw.println("  tempwhitelist [-u USER] [-d DURATION] [package ..]");
+        pw.println("    Temporarily place packages in whitelist for DURATION milliseconds.");
+        pw.println("    If no DURATION is specified, 10 seconds is used");
     }
 
     class Shell extends ShellCommand {
@@ -2796,6 +2827,7 @@
                 }
             }
         } else if ("tempwhitelist".equals(cmd)) {
+            long duration = 10000;
             String opt;
             while ((opt=shell.getNextOption()) != null) {
                 if ("-u".equals(opt)) {
@@ -2805,12 +2837,19 @@
                         return -1;
                     }
                     shell.userId = Integer.parseInt(opt);
+                } else if ("-d".equals(opt)) {
+                    opt = shell.getNextArg();
+                    if (opt == null) {
+                        pw.println("-d requires a duration");
+                        return -1;
+                    }
+                    duration = Long.parseLong(opt);
                 }
             }
             String arg = shell.getNextArg();
             if (arg != null) {
                 try {
-                    addPowerSaveTempWhitelistAppChecked(arg, 10000L, shell.userId, "shell");
+                    addPowerSaveTempWhitelistAppChecked(arg, duration, shell.userId, "shell");
                 } catch (RemoteException re) {
                     pw.println("Failed: " + re);
                 }
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index dd95f67..1bdff6b 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -22,13 +22,20 @@
 import android.os.StatFs;
 import android.os.SystemClock;
 import android.os.storage.StorageManager;
+import android.service.diskstats.DiskStatsAppSizesProto;
+import android.service.diskstats.DiskStatsCachedValuesProto;
+import android.service.diskstats.DiskStatsFreeSpaceProto;
+import android.service.diskstats.DiskStatsServiceDumpProto;
 import android.util.Log;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.server.storage.DiskStatsFileLogger;
 import com.android.server.storage.DiskStatsLoggingService;
 
 import libcore.io.IoUtils;
 
+import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -78,32 +85,68 @@
         long after = SystemClock.uptimeMillis();
         if (tmp.exists()) tmp.delete();
 
-        if (error != null) {
-            pw.print("Test-Error: ");
-            pw.println(error.toString());
+        boolean protoFormat = hasOption(args, "--proto");
+        ProtoOutputStream proto = null;
+
+        if (protoFormat) {
+            proto = new ProtoOutputStream(fd);
+            pw = null;
+            proto.write(DiskStatsServiceDumpProto.HAS_TEST_ERROR, error != null);
+            if (error != null) {
+                proto.write(DiskStatsServiceDumpProto.ERROR_MESSAGE, error.toString());
+            } else {
+                proto.write(DiskStatsServiceDumpProto.WRITE_512B_LATENCY_MILLIS, after - before);
+            }
         } else {
-            pw.print("Latency: ");
-            pw.print(after - before);
-            pw.println("ms [512B Data Write]");
+            if (error != null) {
+                pw.print("Test-Error: ");
+                pw.println(error.toString());
+            } else {
+                pw.print("Latency: ");
+                pw.print(after - before);
+                pw.println("ms [512B Data Write]");
+            }
         }
 
-        reportFreeSpace(Environment.getDataDirectory(), "Data", pw);
-        reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw);
-        reportFreeSpace(new File("/system"), "System", pw);
+        reportFreeSpace(Environment.getDataDirectory(), "Data", pw, proto,
+                DiskStatsFreeSpaceProto.FOLDER_DATA);
+        reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw, proto,
+                DiskStatsFreeSpaceProto.FOLDER_CACHE);
+        reportFreeSpace(new File("/system"), "System", pw, proto,
+                DiskStatsFreeSpaceProto.FOLDER_SYSTEM);
 
-        if (StorageManager.isFileEncryptedNativeOnly()) {
+        boolean fileBased = StorageManager.isFileEncryptedNativeOnly();
+        boolean blockBased = fileBased ? false : StorageManager.isBlockEncrypted();
+        if (protoFormat) {
+            if (fileBased) {
+                proto.write(DiskStatsServiceDumpProto.ENCRYPTION,
+                        DiskStatsServiceDumpProto.ENCRYPTION_FILE_BASED);
+            } else if (blockBased) {
+                proto.write(DiskStatsServiceDumpProto.ENCRYPTION,
+                        DiskStatsServiceDumpProto.ENCRYPTION_FULL_DISK);
+            } else {
+                proto.write(DiskStatsServiceDumpProto.ENCRYPTION,
+                        DiskStatsServiceDumpProto.ENCRYPTION_NONE);
+            }
+        } else if (fileBased) {
             pw.println("File-based Encryption: true");
         }
 
-        if (isCheckin(args)) {
+        if (protoFormat) {
+            reportCachedValuesProto(proto);
+        } else {
             reportCachedValues(pw);
         }
 
+        if (protoFormat) {
+            proto.flush();
+        }
         // TODO: Read /proc/yaffs and report interesting values;
         // add configurable (through args) performance test parameters.
     }
 
-    private void reportFreeSpace(File path, String name, PrintWriter pw) {
+    private void reportFreeSpace(File path, String name, PrintWriter pw,
+            ProtoOutputStream proto, int folderType) {
         try {
             StatFs statfs = new StatFs(path.getPath());
             long bsize = statfs.getBlockSize();
@@ -114,31 +157,44 @@
                         "Invalid stat: bsize=" + bsize + " avail=" + avail + " total=" + total);
             }
 
-            pw.print(name);
-            pw.print("-Free: ");
-            pw.print(avail * bsize / 1024);
-            pw.print("K / ");
-            pw.print(total * bsize / 1024);
-            pw.print("K total = ");
-            pw.print(avail * 100 / total);
-            pw.println("% free");
+            if (proto != null) {
+                long freeSpaceToken = proto.start(DiskStatsServiceDumpProto.PARTITIONS_FREE_SPACE);
+                proto.write(DiskStatsFreeSpaceProto.FOLDER, folderType);
+                proto.write(DiskStatsFreeSpaceProto.AVAILABLE_SPACE, avail * bsize / 1024);
+                proto.write(DiskStatsFreeSpaceProto.TOTAL_SPACE, total * bsize / 1024);
+                proto.end(freeSpaceToken);
+            } else {
+                pw.print(name);
+                pw.print("-Free: ");
+                pw.print(avail * bsize / 1024);
+                pw.print("K / ");
+                pw.print(total * bsize / 1024);
+                pw.print("K total = ");
+                pw.print(avail * 100 / total);
+                pw.println("% free");
+            }
         } catch (IllegalArgumentException e) {
-            pw.print(name);
-            pw.print("-Error: ");
-            pw.println(e.toString());
+            if (proto != null) {
+                // Empty proto
+            } else {
+                pw.print(name);
+                pw.print("-Error: ");
+                pw.println(e.toString());
+            }
             return;
         }
     }
 
-    private boolean isCheckin(String[] args) {
+    private boolean hasOption(String[] args, String arg) {
         for (String opt : args) {
-            if ("--checkin".equals(opt)) {
+            if (arg.equals(opt)) {
                 return true;
             }
         }
         return false;
     }
 
+    // If you change this method, make sure to modify the Proto version of this method as well.
     private void reportCachedValues(PrintWriter pw) {
         try {
             String jsonString = IoUtils.readFileAsString(DISKSTATS_DUMP_FILE);
@@ -170,4 +226,52 @@
         }
     }
 
+    private void reportCachedValuesProto(ProtoOutputStream proto) {
+        try {
+            String jsonString = IoUtils.readFileAsString(DISKSTATS_DUMP_FILE);
+            JSONObject json = new JSONObject(jsonString);
+            long cachedValuesToken = proto.start(DiskStatsServiceDumpProto.CACHED_FOLDER_SIZES);
+
+            proto.write(DiskStatsCachedValuesProto.AGG_APPS_SIZE,
+                    json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY));
+            proto.write(DiskStatsCachedValuesProto.AGG_APPS_CACHE_SIZE,
+                    json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY));
+            proto.write(DiskStatsCachedValuesProto.PHOTOS_SIZE,
+                    json.getLong(DiskStatsFileLogger.PHOTOS_KEY));
+            proto.write(DiskStatsCachedValuesProto.VIDEOS_SIZE,
+                    json.getLong(DiskStatsFileLogger.VIDEOS_KEY));
+            proto.write(DiskStatsCachedValuesProto.AUDIO_SIZE,
+                    json.getLong(DiskStatsFileLogger.AUDIO_KEY));
+            proto.write(DiskStatsCachedValuesProto.DOWNLOADS_SIZE,
+                    json.getLong(DiskStatsFileLogger.DOWNLOADS_KEY));
+            proto.write(DiskStatsCachedValuesProto.SYSTEM_SIZE,
+                    json.getLong(DiskStatsFileLogger.SYSTEM_KEY));
+            proto.write(DiskStatsCachedValuesProto.OTHER_SIZE,
+                    json.getLong(DiskStatsFileLogger.MISC_KEY));
+
+            JSONArray packageNamesArray = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
+            JSONArray appSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
+            JSONArray cacheSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
+            final int len = packageNamesArray.length();
+            if (len == appSizesArray.length() && len == cacheSizesArray.length()) {
+                for (int i = 0; i < len; i++) {
+                    long packageToken = proto.start(DiskStatsCachedValuesProto.APP_SIZES);
+
+                    proto.write(DiskStatsAppSizesProto.PACKAGE_NAME,
+                            packageNamesArray.getString(i));
+                    proto.write(DiskStatsAppSizesProto.APP_SIZE, appSizesArray.getLong(i));
+                    proto.write(DiskStatsAppSizesProto.CACHE_SIZE, cacheSizesArray.getLong(i));
+
+                    proto.end(packageToken);
+                }
+            } else {
+                Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray and cacheSizesArray "
+                        + "are not the same");
+            }
+
+            proto.end(cachedValuesToken);
+        } catch (IOException | JSONException e) {
+            Log.w(TAG, "exception reading diskstats cache file", e);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index d42fe11..4559254 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -99,6 +99,13 @@
 2810 watchdog_vmstat (runtime|2|3),(pgfree|1|1),(pgactivate|1|1),(pgdeactivate|1|1),(pgfault|1|1),(pgmajfault|1|1)
 2811 watchdog_requested_reboot (NoWait|1|1),(ScheduleInterval|1|3),(RecheckInterval|1|3),(StartTime|1|3),(Window|1|3),(MinScreenOff|1|3),(MinNextAlarm|1|3)
 
+# ---------------------------
+# RescueParty.java
+# ---------------------------
+2900 rescue_note (uid|1),(count|1),(window|2)
+2901 rescue_level (level|1),(trigger_uid|1)
+2902 rescue_success (level|1)
+2903 rescue_failure (level|1),(msg|3)
 
 # ---------------------------
 # BackupManagerService.java
diff --git a/services/core/java/com/android/server/FontManagerService.java b/services/core/java/com/android/server/FontManagerService.java
new file mode 100644
index 0000000..593c322
--- /dev/null
+++ b/services/core/java/com/android/server/FontManagerService.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.graphics.FontListParser;
+import android.os.ParcelFileDescriptor;
+import android.text.FontConfig;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.font.IFontManager;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+public class FontManagerService extends IFontManager.Stub {
+    private static final String TAG = "FontManagerService";
+    private static final String FONTS_CONFIG = "/system/etc/fonts.xml";
+
+    @GuardedBy("mLock")
+    private FontConfig mConfig;
+    private final Object mLock = new Object();
+
+    public static final class Lifecycle extends SystemService {
+        private final FontManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mService = new FontManagerService();
+        }
+
+        @Override
+        public void onStart() {
+            try {
+                publishBinderService(Context.FONT_SERVICE, mService);
+            } catch (Throwable t) {
+                // Starting this service is not critical to the running of this device and should
+                // therefore not crash the device. If it fails, log the error and continue.
+                Slog.e(TAG, "Could not start the FontManagerService.", t);
+            }
+        }
+    }
+
+    @Override
+    public FontConfig getSystemFonts() {
+        synchronized (mLock) {
+            if (mConfig != null) {
+                return new FontConfig(mConfig);
+            }
+
+            FontConfig config = loadFromSystem();
+            if (config == null) {
+                return null;
+            }
+
+            final int size = config.getFamilies().size();
+            for (int i = 0; i < size; ++i) {
+                FontConfig.Family family = config.getFamilies().get(i);
+                for (int j = 0; j < family.getFonts().size(); ++j) {
+                    FontConfig.Font font = family.getFonts().get(j);
+                    File fontFile = new File(font.getFontName());
+                    try {
+                        font.setFd(ParcelFileDescriptor.open(
+                                fontFile, ParcelFileDescriptor.MODE_READ_ONLY));
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Error opening font file " + font.getFontName(), e);
+                    }
+                }
+            }
+
+            mConfig = config;
+            return new FontConfig(mConfig);
+        }
+    }
+
+    private FontConfig loadFromSystem() {
+        File configFilename = new File(FONTS_CONFIG);
+        try {
+            FileInputStream fontsIn = new FileInputStream(configFilename);
+            return FontListParser.parse(fontsIn);
+        } catch (IOException | XmlPullParserException e) {
+            Slog.e(TAG, "Error opening " + configFilename, e);
+        }
+        return null;
+    }
+
+    public FontManagerService() {
+    }
+}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index f718fa1..bee1f97 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -243,7 +243,6 @@
     private PendingIntent mImeSwitchPendingIntent;
     private boolean mShowOngoingImeSwitcherForPhones;
     private boolean mNotificationShown;
-    private final boolean mImeSelectedOnBoot;
 
     static class SessionState {
         final ClientState client;
@@ -566,7 +565,7 @@
         }
     }
 
-    class ImmsBroadcastReceiver extends android.content.BroadcastReceiver {
+    class ImmsBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
@@ -587,6 +586,10 @@
                             Intent.EXTRA_SETTING_NEW_VALUE);
                     restoreEnabledInputMethods(mContext, prevValue, newValue);
                 }
+            } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
+                synchronized (mMethodMap) {
+                    resetStateIfCurrentLocaleChangedLocked();
+                }
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
             }
@@ -845,9 +848,11 @@
                 return;
             }
             mSettings.switchCurrentUser(currentUserId, !mSystemReady);
-            // We need to rebuild IMEs.
-            buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
-            updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
+            if (mSystemReady) {
+                // We need to rebuild IMEs.
+                buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
+                updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
+            }
         }
     }
 
@@ -897,13 +902,6 @@
 
         mShowOngoingImeSwitcherForPhones = false;
 
-        final IntentFilter broadcastFilter = new IntentFilter();
-        broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
-        broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
-        broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
-        mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
-
         mNotificationShown = false;
         int userId = 0;
         try {
@@ -911,7 +909,6 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
         }
-        mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
 
         // mSettings should be created before buildInputMethodListLocked
         mSettings = new InputMethodSettings(
@@ -919,48 +916,8 @@
 
         updateCurrentProfileIds();
         mFileManager = new InputMethodFileManager(mMethodMap, userId);
-        synchronized (mMethodMap) {
-            mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
-                    mSettings, context);
-        }
-
-        // Just checking if defaultImiId is empty or not
-        final String defaultImiId = mSettings.getSelectedInputMethod();
-        if (DEBUG) {
-            Slog.d(TAG, "Initial default ime = " + defaultImiId);
-        }
-        mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
-
-        synchronized (mMethodMap) {
-            buildInputMethodListLocked(!mImeSelectedOnBoot /* resetDefaultEnabledIme */);
-        }
-        mSettings.enableAllIMEsIfThereIsNoEnabledIME();
-
-        if (!mImeSelectedOnBoot) {
-            Slog.w(TAG, "No IME selected. Choose the most applicable IME.");
-            synchronized (mMethodMap) {
-                resetDefaultImeLocked(context);
-            }
-        }
-
-        synchronized (mMethodMap) {
-            mSettingsObserver.registerContentObserverLocked(userId);
-            updateFromSettingsLocked(true);
-        }
-
-        // IMMS wants to receive Intent.ACTION_LOCALE_CHANGED in order to update the current IME
-        // according to the new system locale.
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        synchronized(mMethodMap) {
-                            resetStateIfCurrentLocaleChangedLocked();
-                        }
-                    }
-                }, filter);
+        mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+                mSettings, context);
     }
 
     private void resetDefaultImeLocked(Context context) {
@@ -1089,6 +1046,7 @@
             }
             if (!mSystemReady) {
                 mSystemReady = true;
+                mLastSystemLocales = mRes.getConfiguration().getLocales();
                 final int currentUserId = mSettings.getCurrentUserId();
                 mSettings.switchCurrentUser(currentUserId,
                         !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
@@ -1105,14 +1063,25 @@
                     mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
                             mHardKeyboardListener);
                 }
-                if (!mImeSelectedOnBoot) {
-                    Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
-                    resetStateIfCurrentLocaleChangedLocked();
-                    InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
-                            mSettings.getEnabledInputMethodListLocked(),
-                            mSettings.getCurrentUserId(), mContext.getBasePackageName());
-                }
-                mLastSystemLocales = mRes.getConfiguration().getLocales();
+
+                mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+                mSettingsObserver.registerContentObserverLocked(currentUserId);
+
+                final IntentFilter broadcastFilter = new IntentFilter();
+                broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+                broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
+                broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
+                broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
+                broadcastFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
+                mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
+
+                buildInputMethodListLocked(true /* resetDefaultEnabledIme */);
+                resetDefaultImeLocked(mContext);
+                updateFromSettingsLocked(true);
+                InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
+                        mSettings.getEnabledInputMethodListLocked(), currentUserId,
+                        mContext.getBasePackageName());
+
                 try {
                     startInputInnerLocked();
                 } catch (RuntimeException e) {
@@ -2624,6 +2593,9 @@
         // additional input method subtypes to the IME.
         if (TextUtils.isEmpty(imiId) || subtypes == null) return;
         synchronized (mMethodMap) {
+            if (!mSystemReady) {
+                return;
+            }
             final InputMethodInfo imi = mMethodMap.get(imiId);
             if (imi == null) return;
             final String[] packageInfos;
@@ -3048,6 +3020,10 @@
             Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
                     + " \n ------ caller=" + Debug.getCallers(10));
         }
+        if (!mSystemReady) {
+            Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
+            return;
+        }
         mMethodList.clear();
         mMethodMap.clear();
 
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index c9b59ade..6cc72de 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.app.ActivityManager;
 import android.content.pm.PackageManagerInternal;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.location.ProviderProperties;
@@ -138,6 +139,9 @@
     // The maximum interval a location request can have and still be considered "high power".
     private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
 
+    // default background throttling interval if not overriden in settings
+    private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 1000;
+
     // Location Providers may sometimes deliver location updates
     // slightly faster that requested - provide grace period so
     // we don't unnecessarily filter events that are otherwise on
@@ -157,6 +161,7 @@
     private GeofenceManager mGeofenceManager;
     private PackageManager mPackageManager;
     private PowerManager mPowerManager;
+    private ActivityManager mActivityManager;
     private UserManager mUserManager;
     private GeocoderProxy mGeocodeProvider;
     private IGnssStatusProvider mGnssStatusProvider;
@@ -171,47 +176,47 @@
     // --- fields below are protected by mLock ---
     // Set of providers that are explicitly enabled
     // Only used by passive, fused & test.  Network & GPS are controlled separately, and not listed.
-    private final Set<String> mEnabledProviders = new HashSet<String>();
+    private final Set<String> mEnabledProviders = new HashSet<>();
 
     // Set of providers that are explicitly disabled
-    private final Set<String> mDisabledProviders = new HashSet<String>();
+    private final Set<String> mDisabledProviders = new HashSet<>();
 
     // Mock (test) providers
     private final HashMap<String, MockProvider> mMockProviders =
-            new HashMap<String, MockProvider>();
+            new HashMap<>();
 
     // all receivers
-    private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
+    private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
 
     // currently installed providers (with mocks replacing real providers)
     private final ArrayList<LocationProviderInterface> mProviders =
-            new ArrayList<LocationProviderInterface>();
+            new ArrayList<>();
 
     // real providers, saved here when mocked out
     private final HashMap<String, LocationProviderInterface> mRealProviders =
-            new HashMap<String, LocationProviderInterface>();
+            new HashMap<>();
 
     // mapping from provider name to provider
     private final HashMap<String, LocationProviderInterface> mProvidersByName =
-            new HashMap<String, LocationProviderInterface>();
+            new HashMap<>();
 
     // mapping from provider name to all its UpdateRecords
     private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
-            new HashMap<String, ArrayList<UpdateRecord>>();
+            new HashMap<>();
 
     private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics();
 
     // mapping from provider name to last known location
-    private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
+    private final HashMap<String, Location> mLastLocation = new HashMap<>();
 
     // same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS.
     // locations stored here are not fudged for coarse permissions.
     private final HashMap<String, Location> mLastLocationCoarseInterval =
-            new HashMap<String, Location>();
+            new HashMap<>();
 
     // all providers that operate over proxy, for authorizing incoming location
     private final ArrayList<LocationProviderProxy> mProxyProviders =
-            new ArrayList<LocationProviderProxy>();
+            new ArrayList<>();
 
     // current active user on the device - other users are denied location data
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -252,6 +257,10 @@
             // fetch power manager
             mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
 
+            // fetch activity manager
+            mActivityManager
+                    = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+
             // prepare worker thread
             mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
 
@@ -286,6 +295,40 @@
             };
             mPackageManager.addOnPermissionsChangeListener(permissionListener);
 
+            // listen for background/foreground changes
+            ActivityManager.OnUidImportanceListener uidImportanceListener
+                    = new ActivityManager.OnUidImportanceListener() {
+                @Override
+                public void onUidImportance(int uid, int importance) {
+                    boolean foreground = isImportanceForeground(importance);
+                    HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
+                    synchronized (mLock) {
+                        for (Map.Entry<String, ArrayList<UpdateRecord>> entry
+                                : mRecordsByProvider.entrySet()) {
+                            String provider = entry.getKey();
+                            for (UpdateRecord record : entry.getValue()) {
+                                if (record.mReceiver.mUid == uid
+                                        && record.mIsForegroundUid != foreground) {
+                                    if (D) Log.d(TAG, "request from uid " + uid + " is now "
+                                            + (foreground ? "foreground" : "background)"));
+                                    record.mIsForegroundUid = foreground;
+
+                                    if (!isThrottlingExemptLocked(record.mReceiver)) {
+                                        affectedProviders.add(provider);
+                                    }
+                                }
+                            }
+                        }
+                        for (String provider : affectedProviders) {
+                            applyRequirementsLocked(provider);
+                        }
+                    }
+
+                }
+            };
+            mActivityManager.addOnUidImportanceListener(uidImportanceListener,
+                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE);
+
             mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
             updateUserProfiles(mCurrentUserId);
 
@@ -305,6 +348,17 @@
                         }
                     }
                 }, UserHandle.USER_ALL);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
+                true,
+                new ContentObserver(mLocationHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        synchronized (mLock) {
+                            updateProvidersLocked();
+                        }
+                    }
+                }, UserHandle.USER_ALL);
         mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
 
         // listen for user change
@@ -334,6 +388,10 @@
         }, UserHandle.ALL, intentFilter, null, mLocationHandler);
     }
 
+    private static boolean isImportanceForeground(int importance) {
+        return importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+    }
+
     /**
      * Provides a way for components held by the {@link LocationManagerService} to clean-up
      * gracefully on system's shutdown.
@@ -483,7 +541,7 @@
         that matches the signature of at least one package on this list.
         */
         Resources resources = mContext.getResources();
-        ArrayList<String> providerPackageNames = new ArrayList<String>();
+        ArrayList<String> providerPackageNames = new ArrayList<>();
         String[] pkgs = resources.getStringArray(
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (D) Log.d(TAG, "certificates for location providers pulled from: " +
@@ -651,7 +709,7 @@
         final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
         final Object mKey;
 
-        final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
+        final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<>();
 
         // True if app ops has started monitoring this receiver for locations.
         boolean mOpMonitoring;
@@ -691,10 +749,7 @@
 
         @Override
         public boolean equals(Object otherObj) {
-            if (otherObj instanceof Receiver) {
-                return mKey.equals(((Receiver)otherObj).mKey);
-            }
-            return false;
+            return (otherObj instanceof Receiver) && mKey.equals(((Receiver) otherObj).mKey);
         }
 
         @Override
@@ -1011,13 +1066,25 @@
         mProvidersByName.remove(provider.getName());
     }
 
+    private boolean isOverlayProviderPackageLocked(String packageName) {
+        for (LocationProviderInterface provider : mProviders) {
+            if (provider instanceof LocationProviderProxy) {
+                if (packageName.equals(
+                        ((LocationProviderProxy) provider).getConnectedPackageName())) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
     /**
      * Returns "true" if access to the specified location provider is allowed by the current
      * user's settings. Access to all location providers is forbidden to non-location-provider
      * processes belonging to background users.
      *
      * @param provider the name of the location provider
-     * @return
      */
     private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
         if (mEnabledProviders.contains(provider)) {
@@ -1039,7 +1106,6 @@
      *
      * @param provider the name of the location provider
      * @param uid the requestor's UID
-     * @return
      */
     private boolean isAllowedByUserSettingsLocked(String provider, int uid) {
         if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
@@ -1197,11 +1263,7 @@
             }
         }
 
-        if (getAllowedResolutionLevel(pid, uid) < allowedResolutionLevel) {
-            return false;
-        }
-
-        return true;
+        return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
     }
 
     boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
@@ -1212,11 +1274,7 @@
             }
         }
 
-        if (getAllowedResolutionLevel(pid, uid) < allowedResolutionLevel) {
-            return false;
-        }
-
-        return true;
+        return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
     }
 
     /**
@@ -1228,7 +1286,7 @@
     public List<String> getAllProviders() {
         ArrayList<String> out;
         synchronized (mLock) {
-            out = new ArrayList<String>(mProviders.size());
+            out = new ArrayList<>(mProviders.size());
             for (LocationProviderInterface provider : mProviders) {
                 String name = provider.getName();
                 if (LocationManager.FUSED_PROVIDER.equals(name)) {
@@ -1251,11 +1309,11 @@
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         ArrayList<String> out;
-        int uid = Binder.getCallingUid();;
+        int uid = Binder.getCallingUid();
         long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                out = new ArrayList<String>(mProviders.size());
+                out = new ArrayList<>(mProviders.size());
                 for (LocationProviderInterface provider : mProviders) {
                     String name = provider.getName();
                     if (LocationManager.FUSED_PROVIDER.equals(name)) {
@@ -1370,14 +1428,12 @@
 
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
         if (records != null) {
-            final int N = records.size();
-            for (int i = 0; i < N; i++) {
-                UpdateRecord record = records.get(i);
+            for (UpdateRecord record : records) {
                 if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
                     // Sends a notification message to the receiver
                     if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
                         if (deadReceivers == null) {
-                            deadReceivers = new ArrayList<Receiver>();
+                            deadReceivers = new ArrayList<>();
                         }
                         deadReceivers.add(record.mReceiver);
                     }
@@ -1410,6 +1466,12 @@
         WorkSource worksource = new WorkSource();
         ProviderRequest providerRequest = new ProviderRequest();
 
+        ContentResolver resolver = mContext.getContentResolver();
+        long backgroundThrottleInterval = Settings.Global.getLong(
+                resolver,
+                Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
+                DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+
         if (records != null) {
             for (UpdateRecord record : records) {
                 if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
@@ -1419,10 +1481,22 @@
                             record.mReceiver.mPackageName,
                             record.mReceiver.mAllowedResolutionLevel)) {
                         LocationRequest locationRequest = record.mRequest;
+                        long interval = locationRequest.getInterval();
+
+                        if (!isThrottlingExemptLocked(record.mReceiver)) {
+                            if (!record.mIsForegroundUid) {
+                                interval = Math.max(interval, backgroundThrottleInterval);
+                            }
+                            if (interval != locationRequest.getInterval()) {
+                                locationRequest = new LocationRequest(locationRequest);
+                                locationRequest.setInterval(interval);
+                            }
+                        }
+
                         providerRequest.locationRequests.add(locationRequest);
-                        if (locationRequest.getInterval() < providerRequest.interval) {
+                        if (interval < providerRequest.interval) {
                             providerRequest.reportLocation = true;
-                            providerRequest.interval = locationRequest.getInterval();
+                            providerRequest.interval = interval;
                         }
                     }
                 }
@@ -1468,10 +1542,15 @@
         p.setRequest(providerRequest, worksource);
     }
 
+    private boolean isThrottlingExemptLocked(Receiver recevier) {
+        return isOverlayProviderPackageLocked(recevier.mPackageName);
+    }
+
     private class UpdateRecord {
         final String mProvider;
         final LocationRequest mRequest;
         final Receiver mReceiver;
+        boolean mIsForegroundUid;
         Location mLastFixBroadcast;
         long mLastStatusBroadcast;
 
@@ -1482,10 +1561,12 @@
             mProvider = provider;
             mRequest = request;
             mReceiver = receiver;
+            mIsForegroundUid = isImportanceForeground(
+                    mActivityManager.getPackageImportance(mReceiver.mPackageName));
 
             ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
             if (records == null) {
-                records = new ArrayList<UpdateRecord>();
+                records = new ArrayList<>();
                 mRecordsByProvider.put(provider, records);
             }
             if (!records.contains(this)) {
@@ -1517,7 +1598,7 @@
                 receiverRecords.remove(this.mProvider);
 
                 // and also remove the Receiver if it has no more update records
-                if (removeReceiver && receiverRecords.size() == 0) {
+                if (receiverRecords.size() == 0) {
                     removeUpdatesLocked(mReceiver);
                 }
             }
@@ -1525,14 +1606,9 @@
 
         @Override
         public String toString() {
-            StringBuilder s = new StringBuilder();
-            s.append("UpdateRecord[");
-            s.append(mProvider);
-            s.append(' ').append(mReceiver.mPackageName).append('(');
-            s.append(mReceiver.mUid).append(')');
-            s.append(' ').append(mRequest);
-            s.append(']');
-            return s.toString();
+            return "UpdateRecord[" + mProvider + " " + mReceiver.mPackageName
+                    + "(" + mReceiver.mUid + (mIsForegroundUid ? " foreground" : " background")
+                    + ")" + " " + mRequest + "]";
         }
     }
 
@@ -1681,14 +1757,17 @@
             throw new IllegalArgumentException("provider name must not be null");
         }
 
-        if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
-                + " " + name + " " + request + " from " + packageName + "(" + uid + ")");
         LocationProviderInterface provider = mProvidersByName.get(name);
         if (provider == null) {
             throw new IllegalArgumentException("provider doesn't exist: " + name);
         }
 
         UpdateRecord record = new UpdateRecord(name, request, receiver);
+        if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
+                + " " + name + " " + request + " from " + packageName + "(" + uid + " "
+                + (record.mIsForegroundUid ? "foreground" : "background")
+                + (isOverlayProviderPackageLocked(receiver.mPackageName) ? " [whitelisted]" : "") + ")");
+
         UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
         if (oldRecord != null) {
             oldRecord.disposeLocked(false);
@@ -1743,7 +1822,7 @@
         receiver.updateMonitoring(false);
 
         // Record which providers were associated with this listener
-        HashSet<String> providers = new HashSet<String>();
+        HashSet<String> providers = new HashSet<>();
         HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
         if (oldRecords != null) {
             // Call dispose() on the obsolete update records.
@@ -2084,9 +2163,7 @@
         try {
             synchronized (mLock) {
                 LocationProviderInterface p = mProvidersByName.get(provider);
-                if (p == null) return false;
-
-                return isAllowedByUserSettingsLocked(provider, uid);
+                return p != null && isAllowedByUserSettingsLocked(provider, uid);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -2197,11 +2274,7 @@
         }
 
         // Check whether the expiry date has passed
-        if (record.mRequest.getExpireAt() < now) {
-            return false;
-        }
-
-        return true;
+        return record.mRequest.getExpireAt() >= now;
     }
 
     private void handleLocationChangedLocked(Location location, boolean passive) {
@@ -2216,7 +2289,7 @@
 
         // Update last known locations
         Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
-        Location lastNoGPSLocation = null;
+        Location lastNoGPSLocation;
         Location lastLocation = mLastLocation.get(provider);
         if (lastLocation == null) {
             lastLocation = new Location(provider);
@@ -2296,7 +2369,7 @@
                 continue;
             }
 
-            Location notifyLocation = null;
+            Location notifyLocation;
             if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
                 notifyLocation = coarseLocation;  // use coarse location
             } else {
@@ -2333,14 +2406,14 @@
             // track expired records
             if (r.mRequest.getNumUpdates() <= 0 || r.mRequest.getExpireAt() < now) {
                 if (deadUpdateRecords == null) {
-                    deadUpdateRecords = new ArrayList<UpdateRecord>();
+                    deadUpdateRecords = new ArrayList<>();
                 }
                 deadUpdateRecords.add(r);
             }
             // track dead receivers
             if (receiverDead) {
                 if (deadReceivers == null) {
-                    deadReceivers = new ArrayList<Receiver>();
+                    deadReceivers = new ArrayList<>();
                 }
                 if (!deadReceivers.contains(receiver)) {
                     deadReceivers.add(receiver);
@@ -2417,7 +2490,7 @@
                 for (Receiver receiver : mReceivers.values()) {
                     if (receiver.mPackageName.equals(packageName)) {
                         if (deadReceivers == null) {
-                            deadReceivers = new ArrayList<Receiver>();
+                            deadReceivers = new ArrayList<>();
                         }
                         deadReceivers.add(receiver);
                     }
@@ -2694,6 +2767,13 @@
                     pw.println("      " + record);
                 }
             }
+            pw.println("  Overlay Provider Packages:");
+            for (LocationProviderInterface provider : mProviders) {
+                if (provider instanceof LocationProviderProxy) {
+                    pw.println("    " + provider.getName() + ": "
+                            + ((LocationProviderProxy) provider).getConnectedPackageName());
+                }
+            }
             pw.println("  Historical Records by Provider:");
             for (Map.Entry<PackageProviderKey, PackageStatistics> entry
                     : mRequestStatistics.statistics.entrySet()) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 51503c0..8ef34dc 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -23,6 +23,7 @@
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -70,6 +71,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.ILockSettings;
@@ -123,20 +125,23 @@
 
     private final Object mSeparateChallengeLock = new Object();
 
+    private final Injector mInjector;
     private final Context mContext;
     private final Handler mHandler;
-    private final LockSettingsStorage mStorage;
+    @VisibleForTesting
+    protected final LockSettingsStorage mStorage;
     private final LockSettingsStrongAuth mStrongAuth;
     private final SynchronizedStrongAuthTracker mStrongAuthTracker;
 
-    private LockPatternUtils mLockPatternUtils;
+    private final LockPatternUtils mLockPatternUtils;
+    private final NotificationManager mNotificationManager;
+    private final UserManager mUserManager;
+    private final IActivityManager mActivityManager;
+
+    private final KeyStore mKeyStore;
+
     private boolean mFirstCallToVold;
-    private IGateKeeperService mGateKeeperService;
-    private NotificationManager mNotificationManager;
-    private UserManager mUserManager;
-
-    private final KeyStore mKeyStore = KeyStore.getInstance();
-
+    protected IGateKeeperService mGateKeeperService;
     /**
      * The UIDs that are used for system credential storage in keystore.
      */
@@ -177,7 +182,9 @@
         }
     }
 
-    private class SynchronizedStrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+    @VisibleForTesting
+    protected static class SynchronizedStrongAuthTracker
+            extends LockPatternUtils.StrongAuthTracker {
         public SynchronizedStrongAuthTracker(Context context) {
             super(context);
         }
@@ -196,8 +203,8 @@
             }
         }
 
-        void register() {
-            mStrongAuth.registerStrongAuthTracker(this.mStub);
+        void register(LockSettingsStrongAuth strongAuth) {
+            strongAuth.registerStrongAuthTracker(this.mStub);
         }
     }
 
@@ -211,7 +218,7 @@
     public void tieManagedProfileLockIfNecessary(int managedUserId, String managedUserPassword) {
         if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId);
         // Only for managed profile
-        if (!UserManager.get(mContext).getUserInfo(managedUserId).isManagedProfile()) {
+        if (!mUserManager.getUserInfo(managedUserId).isManagedProfile()) {
             return;
         }
         // Do not tie managed profile when work challenge is enabled
@@ -258,38 +265,103 @@
         }
     }
 
-    public LockSettingsService(Context context) {
-        mContext = context;
-        mHandler = new Handler();
-        mStrongAuth = new LockSettingsStrongAuth(context);
-        // Open the database
+    static class Injector {
 
-        mLockPatternUtils = new LockPatternUtils(context);
+        protected Context mContext;
+
+        public Injector(Context context) {
+            mContext = context;
+        }
+
+        public Context getContext() {
+            return mContext;
+        }
+
+        public Handler getHandler() {
+            return new Handler();
+        }
+
+        public LockSettingsStorage getStorage() {
+            final LockSettingsStorage storage = new LockSettingsStorage(mContext);
+            storage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() {
+                @Override
+                public void initialize(SQLiteDatabase db) {
+                    // Get the lockscreen default from a system property, if available
+                    boolean lockScreenDisable = SystemProperties.getBoolean(
+                            "ro.lockscreen.disable.default", false);
+                    if (lockScreenDisable) {
+                        storage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
+                    }
+                }
+            });
+            return storage;
+        }
+
+        public LockSettingsStrongAuth getStrongAuth() {
+            return new LockSettingsStrongAuth(mContext);
+        }
+
+        public SynchronizedStrongAuthTracker getStrongAuthTracker() {
+            return new SynchronizedStrongAuthTracker(mContext);
+        }
+
+        public IActivityManager getActivityManager() {
+            return ActivityManager.getService();
+        }
+
+        public LockPatternUtils getLockPatternUtils() {
+            return new LockPatternUtils(mContext);
+        }
+
+        public NotificationManager getNotificationManager() {
+            return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        }
+
+        public UserManager getUserManager() {
+            return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        }
+
+        public KeyStore getKeyStore() {
+            return KeyStore.getInstance();
+        }
+
+        public IStorageManager getStorageManager() {
+            final IBinder service = ServiceManager.getService("mount");
+            if (service != null) {
+                return IStorageManager.Stub.asInterface(service);
+            }
+            return null;
+        }
+    }
+
+    public LockSettingsService(Context context) {
+        this(new Injector(context));
+    }
+
+    @VisibleForTesting
+    protected LockSettingsService(Injector injector) {
+        mInjector = injector;
+        mContext = injector.getContext();
+        mKeyStore = injector.getKeyStore();
+        mHandler = injector.getHandler();
+        mStrongAuth = injector.getStrongAuth();
+        mActivityManager = injector.getActivityManager();
+
+        mLockPatternUtils = injector.getLockPatternUtils();
         mFirstCallToVold = true;
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_STARTING);
         filter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+        injector.getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter,
+                null, null);
 
-        mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
-            @Override
-            public void initialize(SQLiteDatabase db) {
-                // Get the lockscreen default from a system property, if available
-                boolean lockScreenDisable = SystemProperties.getBoolean(
-                        "ro.lockscreen.disable.default", false);
-                if (lockScreenDisable) {
-                    mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
-                }
-            }
-        });
-        mNotificationManager = (NotificationManager)
-                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        mStrongAuthTracker = new SynchronizedStrongAuthTracker(mContext);
-        mStrongAuthTracker.register();
-
+        mStorage = injector.getStorage();
+        mNotificationManager = injector.getNotificationManager();
+        mUserManager = injector.getUserManager();
+        mStrongAuthTracker = injector.getStrongAuthTracker();
+        mStrongAuthTracker.register(mStrongAuth);
     }
 
     /**
@@ -748,7 +820,8 @@
         ks.unlock(userHandle, password);
     }
 
-    private String getDecryptedPasswordForTiedProfile(int userId)
+    @VisibleForTesting
+    protected String getDecryptedPasswordForTiedProfile(int userId)
             throws KeyStoreException, UnrecoverableKeyException,
             NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
             InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
@@ -814,7 +887,7 @@
         };
 
         try {
-            ActivityManager.getService().unlockUser(userId, token, secret, listener);
+            mActivityManager.unlockUser(userId, token, secret, listener);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -961,7 +1034,8 @@
         }
     }
 
-    private void tieProfileLockToParent(int userId, String password) {
+    @VisibleForTesting
+    protected void tieProfileLockToParent(int userId, String password) {
         if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
         byte[] randomLockSeed = password.getBytes(StandardCharsets.UTF_8);
         byte[] encryptionResult;
@@ -1085,8 +1159,8 @@
 
     private void addUserKeyAuth(int userId, byte[] token, byte[] secret)
             throws RemoteException {
-        final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
-        final IStorageManager storageManager = getStorageManager();
+        final UserInfo userInfo = mUserManager.getUserInfo(userId);
+        final IStorageManager storageManager = mInjector.getStorageManager();
         final long callingId = Binder.clearCallingIdentity();
         try {
             storageManager.addUserKeyAuth(userId, userInfo.serialNumber, token, secret);
@@ -1097,7 +1171,7 @@
 
     private void fixateNewestUserKeyAuth(int userId)
             throws RemoteException {
-        final IStorageManager storageManager = getStorageManager();
+        final IStorageManager storageManager = mInjector.getStorageManager();
         final long callingId = Binder.clearCallingIdentity();
         try {
             storageManager.fixateNewestUserKeyAuth(userId);
@@ -1196,9 +1270,11 @@
         VerifyCredentialResponse response = verifyCredential(userId, storedHash, credentialToVerify,
                 hasChallenge, challenge, progressCallback);
 
-        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
-                && shouldReEnrollBaseZero) {
-            setLockCredentialInternal(credential, storedHash.type, credentialToVerify, userId);
+        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+            mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
+            if (shouldReEnrollBaseZero) {
+                setLockCredentialInternal(credential, storedHash.type, credentialToVerify, userId);
+            }
         }
 
         return response;
@@ -1394,7 +1470,7 @@
         // we should, within the first minute of decrypting the phone if this
         // service can't connect to vold, it restarts, and then the new instance
         // does successfully connect.
-        final IStorageManager service = getStorageManager();
+        final IStorageManager service = mInjector.getStorageManager();
         String password;
         long identity = Binder.clearCallingIdentity();
         try {
@@ -1559,14 +1635,6 @@
             Secure.LOCK_SCREEN_OWNER_INFO
     };
 
-    private IStorageManager getStorageManager() {
-        final IBinder service = ServiceManager.getService("mount");
-        if (service != null) {
-            return IStorageManager.Stub.asInterface(service);
-        }
-        return null;
-    }
-
     private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
diff --git a/services/core/java/com/android/server/LockSettingsShellCommand.java b/services/core/java/com/android/server/LockSettingsShellCommand.java
index f72663a..e131251 100644
--- a/services/core/java/com/android/server/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/LockSettingsShellCommand.java
@@ -77,7 +77,8 @@
             }
             return 0;
         } catch (Exception e) {
-            getErrPrintWriter().println("Error while executing command: " + e);
+            getErrPrintWriter().println("Error while executing command: " + cmd);
+            e.printStackTrace(getErrPrintWriter());
             return -1;
         }
     }
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index 3d973a0..c858036 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -119,9 +119,13 @@
         boolean isBaseZeroPattern;
     }
 
-    public LockSettingsStorage(Context context, Callback callback) {
+    public LockSettingsStorage(Context context) {
         mContext = context;
-        mOpenHelper = new DatabaseHelper(context, callback);
+        mOpenHelper = new DatabaseHelper(context);
+    }
+
+    public void setDatabaseOnCreateCallback(Callback callback) {
+        mOpenHelper.setCallback(callback);
     }
 
     public void writeKeyValue(String key, String value, int userId) {
@@ -472,11 +476,14 @@
 
         private static final int DATABASE_VERSION = 2;
 
-        private final Callback mCallback;
+        private Callback mCallback;
 
-        public DatabaseHelper(Context context, Callback callback) {
+        public DatabaseHelper(Context context) {
             super(context, DATABASE_NAME, null, DATABASE_VERSION);
             setWriteAheadLoggingEnabled(true);
+        }
+
+        public void setCallback(Callback callback) {
             mCallback = callback;
         }
 
@@ -492,7 +499,9 @@
         @Override
         public void onCreate(SQLiteDatabase db) {
             createTable(db);
-            mCallback.initialize(db);
+            if (mCallback != null) {
+                mCallback.initialize(db);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java
index 551ceb8..1314110 100644
--- a/services/core/java/com/android/server/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java
@@ -16,23 +16,30 @@
 
 package com.android.server;
 
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
 
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
+import android.app.admin.DevicePolicyManager;
 import android.app.trust.IStrongAuthTracker;
 import android.content.Context;
+import android.os.Binder;
 import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseIntArray;
 
 import java.util.ArrayList;
 
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
-
 /**
  * Keeps track of requests for strong authentication.
  */
@@ -45,12 +52,22 @@
     private static final int MSG_UNREGISTER_TRACKER = 3;
     private static final int MSG_REMOVE_USER = 4;
 
+    private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
+            "LockSettingsStrongAuth.timeoutForUser";
+
     private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>();
     private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
+    private final ArrayMap<Integer, StrongAuthTimeoutAlarmListener>
+            mStrongAuthTimeoutAlarmListenerForUser = new ArrayMap<>();
     private final int mDefaultStrongAuthFlags;
+    private final Context mContext;
+
+    private AlarmManager mAlarmManager;
 
     public LockSettingsStrongAuth(Context context) {
+        mContext = context;
         mDefaultStrongAuthFlags = StrongAuthTracker.getDefaultFlags(context);
+        mAlarmManager = context.getSystemService(AlarmManager.class);
     }
 
     private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
@@ -151,6 +168,46 @@
         requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
     }
 
+    public void reportSuccessfulStrongAuthUnlock(int userId) {
+        scheduleStrongAuthTimeout(userId);
+    }
+
+    private void scheduleStrongAuthTimeout(int userId) {
+        final DevicePolicyManager dpm =
+                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        long when = SystemClock.elapsedRealtime() + dpm.getRequiredStrongAuthTimeout(null, userId);
+        // cancel current alarm listener for the user (if there was one)
+        StrongAuthTimeoutAlarmListener alarm = mStrongAuthTimeoutAlarmListenerForUser.get(userId);
+        if (alarm != null) {
+            mAlarmManager.cancel(alarm);
+        } else {
+            alarm = new StrongAuthTimeoutAlarmListener(userId);
+            mStrongAuthTimeoutAlarmListenerForUser.put(userId, alarm);
+        }
+        // schedule a new alarm listener for the user
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG,
+                    alarm, mHandler);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private class StrongAuthTimeoutAlarmListener implements OnAlarmListener {
+
+        private final int mUserId;
+
+        public StrongAuthTimeoutAlarmListener(int userId) {
+            mUserId = userId;
+        }
+
+        @Override
+        public void onAlarm() {
+            requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, mUserId);
+        }
+    }
+
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 74e44d5..adc5e33 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -45,6 +45,7 @@
 import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsListResult;
 import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
+
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.ContentResolver;
@@ -374,15 +375,17 @@
         mObservers.unregister(observer);
     }
 
-    /**
-     * Notify our observers of an interface status change
-     */
-    private void notifyInterfaceStatusChanged(String iface, boolean up) {
+    @FunctionalInterface
+    private interface NetworkManagementEventCallback {
+        public void sendCallback(INetworkManagementEventObserver o) throws RemoteException;
+    }
+
+    private void invokeForAllObservers(NetworkManagementEventCallback eventCallback) {
         final int length = mObservers.beginBroadcast();
         try {
             for (int i = 0; i < length; i++) {
                 try {
-                    mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
+                    eventCallback.sendCallback(mObservers.getBroadcastItem(i));
                 } catch (RemoteException | RuntimeException e) {
                 }
             }
@@ -392,38 +395,25 @@
     }
 
     /**
+     * Notify our observers of an interface status change
+     */
+    private void notifyInterfaceStatusChanged(String iface, boolean up) {
+        invokeForAllObservers(o -> o.interfaceStatusChanged(iface, up));
+    }
+
+    /**
      * Notify our observers of an interface link state change
      * (typically, an Ethernet cable has been plugged-in or unplugged).
      */
     private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
-                } catch (RemoteException | RuntimeException e) {
-                }
-            }
-        } finally {
-            mObservers.finishBroadcast();
-        }
+        invokeForAllObservers(o -> o.interfaceLinkStateChanged(iface, up));
     }
 
     /**
      * Notify our observers of an interface addition.
      */
     private void notifyInterfaceAdded(String iface) {
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    mObservers.getBroadcastItem(i).interfaceAdded(iface);
-                } catch (RemoteException | RuntimeException e) {
-                }
-            }
-        } finally {
-            mObservers.finishBroadcast();
-        }
+        invokeForAllObservers(o -> o.interfaceAdded(iface));
     }
 
     /**
@@ -435,34 +425,14 @@
         mActiveAlerts.remove(iface);
         mActiveQuotas.remove(iface);
 
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    mObservers.getBroadcastItem(i).interfaceRemoved(iface);
-                } catch (RemoteException | RuntimeException e) {
-                }
-            }
-        } finally {
-            mObservers.finishBroadcast();
-        }
+        invokeForAllObservers(o -> o.interfaceRemoved(iface));
     }
 
     /**
      * Notify our observers of a limit reached.
      */
     private void notifyLimitReached(String limitName, String iface) {
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    mObservers.getBroadcastItem(i).limitReached(limitName, iface);
-                } catch (RemoteException | RuntimeException e) {
-                }
-            }
-        } finally {
-            mObservers.finishBroadcast();
-        }
+        invokeForAllObservers(o -> o.limitReached(limitName, iface));
     }
 
     /**
@@ -509,18 +479,9 @@
             // on the mobile network, that is not coming from the radio itself, and we
             // have previously seen change reports from the radio.  In that case only
             // the radio is the authority for the current state.
-            final int length = mObservers.beginBroadcast();
-            try {
-                for (int i = 0; i < length; i++) {
-                    try {
-                        mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
-                                Integer.toString(type), isActive, tsNanos);
-                    } catch (RemoteException | RuntimeException e) {
-                    }
-                }
-            } finally {
-                mObservers.finishBroadcast();
-            }
+            final boolean active = isActive;
+            invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
+                    Integer.toString(type), active, tsNanos));
         }
 
         boolean report = false;
@@ -692,72 +653,31 @@
      * Notify our observers of a new or updated interface address.
      */
     private void notifyAddressUpdated(String iface, LinkAddress address) {
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    mObservers.getBroadcastItem(i).addressUpdated(iface, address);
-                } catch (RemoteException | RuntimeException e) {
-                }
-            }
-        } finally {
-            mObservers.finishBroadcast();
-        }
+        invokeForAllObservers(o -> o.addressUpdated(iface, address));
     }
 
     /**
      * Notify our observers of a deleted interface address.
      */
     private void notifyAddressRemoved(String iface, LinkAddress address) {
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    mObservers.getBroadcastItem(i).addressRemoved(iface, address);
-                } catch (RemoteException | RuntimeException e) {
-                }
-            }
-        } finally {
-            mObservers.finishBroadcast();
-        }
+        invokeForAllObservers(o -> o.addressRemoved(iface, address));
     }
 
     /**
      * Notify our observers of DNS server information received.
      */
     private void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime,
-                        addresses);
-                } catch (RemoteException | RuntimeException e) {
-                }
-            }
-        } finally {
-            mObservers.finishBroadcast();
-        }
+        invokeForAllObservers(o -> o.interfaceDnsServerInfo(iface, lifetime, addresses));
     }
 
     /**
      * Notify our observers of a route change.
      */
     private void notifyRouteChange(String action, RouteInfo route) {
-        final int length = mObservers.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    if (action.equals("updated")) {
-                        mObservers.getBroadcastItem(i).routeUpdated(route);
-                    } else {
-                        mObservers.getBroadcastItem(i).routeRemoved(route);
-                    }
-                } catch (RemoteException | RuntimeException e) {
-                }
-            }
-        } finally {
-            mObservers.finishBroadcast();
+        if (action.equals("updated")) {
+            invokeForAllObservers(o -> o.routeUpdated(route));
+        } else {
+            invokeForAllObservers(o -> o.routeRemoved(route));
         }
     }
 
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index e23844c..25016f6 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -41,16 +41,28 @@
 import android.net.RecommendationResult;
 import android.net.ScoredNetwork;
 import android.net.Uri;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
+import android.util.Pair;
 import android.util.TimedRemoteCaller;
 
 import com.android.internal.annotations.GuardedBy;
@@ -66,8 +78,13 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.TimeoutException;
-import java.util.function.Consumer;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
 
 /**
  * Backing service for {@link android.net.NetworkScoreManager}.
@@ -75,21 +92,25 @@
  */
 public class NetworkScoreService extends INetworkScoreService.Stub {
     private static final String TAG = "NetworkScoreService";
-    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DBG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean VERBOSE = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
 
     private final Context mContext;
     private final NetworkScorerAppManager mNetworkScorerAppManager;
-    private final RequestRecommendationCaller mRequestRecommendationCaller;
+    private final AtomicReference<RequestRecommendationCaller> mReqRecommendationCallerRef;
     @GuardedBy("mScoreCaches")
     private final Map<Integer, RemoteCallbackList<INetworkScoreCache>> mScoreCaches;
     /** Lock used to update mPackageMonitor when scorer package changes occur. */
-    private final Object mPackageMonitorLock = new Object[0];
-    private final Object mServiceConnectionLock = new Object[0];
+    private final Object mPackageMonitorLock = new Object();
+    private final Object mServiceConnectionLock = new Object();
+    private final Handler mHandler;
+    private final DispatchingContentObserver mContentObserver;
 
     @GuardedBy("mPackageMonitorLock")
     private NetworkScorerPackageMonitor mPackageMonitor;
     @GuardedBy("mServiceConnectionLock")
     private ScoringServiceConnection mServiceConnection;
+    private volatile long mRecommendationRequestTimeoutMs;
 
     private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
         @Override
@@ -185,12 +206,25 @@
     }
 
     /**
-     * Reevaluates the service binding when the Settings toggle is changed.
+     * Dispatches observed content changes to a handler for further processing.
      */
-    private class SettingsObserver extends ContentObserver {
+    @VisibleForTesting
+    public static class DispatchingContentObserver extends ContentObserver {
+        final private Map<Uri, Integer> mUriEventMap;
+        final private Context mContext;
+        final private Handler mHandler;
 
-        public SettingsObserver() {
-            super(null /*handler*/);
+        public DispatchingContentObserver(Context context, Handler handler) {
+            super(handler);
+            mContext = context;
+            mHandler = handler;
+            mUriEventMap = new ArrayMap<>();
+        }
+
+        void observe(Uri uri, int what) {
+            mUriEventMap.put(uri, what);
+            final ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(uri, false /*notifyForDescendants*/, this);
         }
 
         @Override
@@ -201,16 +235,22 @@
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (DBG) Log.d(TAG, String.format("onChange(%s, %s)", selfChange, uri));
-            bindToScoringServiceIfNeeded();
+            final Integer what = mUriEventMap.get(uri);
+            if (what != null) {
+                mHandler.obtainMessage(what).sendToTarget();
+            } else {
+                Log.w(TAG, "No matching event to send for URI = " + uri);
+            }
         }
     }
 
     public NetworkScoreService(Context context) {
-      this(context, new NetworkScorerAppManager(context));
+      this(context, new NetworkScorerAppManager(context), Looper.myLooper());
     }
 
     @VisibleForTesting
-    NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager) {
+    NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager,
+            Looper looper) {
         mContext = context;
         mNetworkScorerAppManager = networkScoreAppManager;
         mScoreCaches = new ArrayMap<>();
@@ -219,16 +259,19 @@
         mContext.registerReceiverAsUser(
                 mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
                 null /* scheduler */);
-        // TODO(jjoslin): 12/15/16 - Make timeout configurable.
-        mRequestRecommendationCaller =
-            new RequestRecommendationCaller(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+        mReqRecommendationCallerRef = new AtomicReference<>(
+                new RequestRecommendationCaller(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS));
+        mRecommendationRequestTimeoutMs = TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS;
+        mHandler = new ServiceHandler(looper);
+        mContentObserver = new DispatchingContentObserver(context, mHandler);
     }
 
     /** Called when the system is ready to run third-party code but before it actually does so. */
     void systemReady() {
         if (DBG) Log.d(TAG, "systemReady");
         registerPackageMonitorIfNeeded();
-        registerRecommendationSettingObserverIfNeeded();
+        registerRecommendationSettingsObserver();
+        refreshRecommendationRequestTimeoutMs();
     }
 
     /** Called when the system is ready for us to start third-party code. */
@@ -242,14 +285,18 @@
         bindToScoringServiceIfNeeded();
     }
 
-    private void registerRecommendationSettingObserverIfNeeded() {
+    private void registerRecommendationSettingsObserver() {
         final List<String> providerPackages =
             mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
         if (!providerPackages.isEmpty()) {
-            final ContentResolver resolver = mContext.getContentResolver();
-            final Uri uri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
-            resolver.registerContentObserver(uri, false, new SettingsObserver());
+            final Uri enabledUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
+            mContentObserver.observe(enabledUri,
+                    ServiceHandler.MSG_RECOMMENDATIONS_ENABLED_CHANGED);
         }
+
+        final Uri timeoutUri = Global.getUriFor(Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS);
+        mContentObserver.observe(timeoutUri,
+                ServiceHandler.MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED);
     }
 
     private void registerPackageMonitorIfNeeded() {
@@ -353,6 +400,7 @@
                     isEmpty = callbackList == null
                             || callbackList.getRegisteredCallbackCount() == 0;
                 }
+
                 if (isEmpty) {
                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
                         Log.v(TAG, "No scorer registered for type " + entry.getKey()
@@ -361,18 +409,10 @@
                     continue;
                 }
 
-                sendCallback(new Consumer<INetworkScoreCache>() {
-                    @Override
-                    public void accept(INetworkScoreCache networkScoreCache) {
-                        try {
-                            networkScoreCache.updateScores(entry.getValue());
-                        } catch (RemoteException e) {
-                            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                                Log.v(TAG, "Unable to update scores of type " + entry.getKey(), e);
-                            }
-                        }
-                    }
-                }, Collections.singleton(callbackList));
+                final BiConsumer<INetworkScoreCache, Object> consumer =
+                        FilteringCacheUpdatingConsumer.create(mContext, entry.getValue(),
+                                entry.getKey());
+                sendCacheUpdateCallback(consumer, Collections.singleton(callbackList));
             }
 
             return true;
@@ -381,6 +421,210 @@
         }
     }
 
+    /**
+     * A {@link BiConsumer} implementation that filters the given {@link ScoredNetwork}
+     * list (if needed) before invoking {@link INetworkScoreCache#updateScores(List)} on the
+     * accepted {@link INetworkScoreCache} implementation.
+     */
+    @VisibleForTesting
+    static class FilteringCacheUpdatingConsumer
+            implements BiConsumer<INetworkScoreCache, Object> {
+        private final Context mContext;
+        private final List<ScoredNetwork> mScoredNetworkList;
+        private final int mNetworkType;
+        // TODO: 1/23/17 - Consider a Map if we implement more filters.
+        // These are created on-demand to defer the construction cost until
+        // an instance is actually needed.
+        private UnaryOperator<List<ScoredNetwork>> mCurrentNetworkFilter;
+        private UnaryOperator<List<ScoredNetwork>> mScanResultsFilter;
+
+        static FilteringCacheUpdatingConsumer create(Context context,
+                List<ScoredNetwork> scoredNetworkList, int networkType) {
+            return new FilteringCacheUpdatingConsumer(context, scoredNetworkList, networkType,
+                    null, null);
+        }
+
+        @VisibleForTesting
+        FilteringCacheUpdatingConsumer(Context context,
+                List<ScoredNetwork> scoredNetworkList, int networkType,
+                UnaryOperator<List<ScoredNetwork>> currentNetworkFilter,
+                UnaryOperator<List<ScoredNetwork>> scanResultsFilter) {
+            mContext = context;
+            mScoredNetworkList = scoredNetworkList;
+            mNetworkType = networkType;
+            mCurrentNetworkFilter = currentNetworkFilter;
+            mScanResultsFilter = scanResultsFilter;
+        }
+
+        @Override
+        public void accept(INetworkScoreCache networkScoreCache, Object cookie) {
+            int filterType = NetworkScoreManager.CACHE_FILTER_NONE;
+            if (cookie instanceof Integer) {
+                filterType = (Integer) cookie;
+            }
+
+            try {
+                final List<ScoredNetwork> filteredNetworkList =
+                        filterScores(mScoredNetworkList, filterType);
+                if (!filteredNetworkList.isEmpty()) {
+                    networkScoreCache.updateScores(filteredNetworkList);
+                }
+            } catch (RemoteException e) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Unable to update scores of type " + mNetworkType, e);
+                }
+            }
+        }
+
+        /**
+         * Applies the appropriate filter and returns the filtered results.
+         */
+        private List<ScoredNetwork> filterScores(List<ScoredNetwork> scoredNetworkList,
+                int filterType) {
+            switch (filterType) {
+                case NetworkScoreManager.CACHE_FILTER_NONE:
+                    return scoredNetworkList;
+
+                case NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK:
+                    if (mCurrentNetworkFilter == null) {
+                        mCurrentNetworkFilter =
+                                new CurrentNetworkScoreCacheFilter(new WifiInfoSupplier(mContext));
+                    }
+                    return mCurrentNetworkFilter.apply(scoredNetworkList);
+
+                case NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS:
+                    if (mScanResultsFilter == null) {
+                        mScanResultsFilter = new ScanResultsScoreCacheFilter(
+                                new ScanResultsSupplier(mContext));
+                    }
+                    return mScanResultsFilter.apply(scoredNetworkList);
+
+                default:
+                    Log.w(TAG, "Unknown filter type: " + filterType);
+                    return scoredNetworkList;
+            }
+        }
+    }
+
+    /**
+     * Helper class that improves the testability of the cache filter Functions.
+     */
+    private static class WifiInfoSupplier implements Supplier<WifiInfo> {
+        private final Context mContext;
+
+        WifiInfoSupplier(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public WifiInfo get() {
+            WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+            if (wifiManager != null) {
+                return wifiManager.getConnectionInfo();
+            }
+            Log.w(TAG, "WifiManager is null, failed to return the WifiInfo.");
+            return null;
+        }
+    }
+
+    /**
+     * Helper class that improves the testability of the cache filter Functions.
+     */
+    private static class ScanResultsSupplier implements Supplier<List<ScanResult>> {
+        private final Context mContext;
+
+        ScanResultsSupplier(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public List<ScanResult> get() {
+            WifiScanner wifiScanner = mContext.getSystemService(WifiScanner.class);
+            if (wifiScanner != null) {
+                return wifiScanner.getSingleScanResults();
+            }
+            Log.w(TAG, "WifiScanner is null, failed to return scan results.");
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Filters the given set of {@link ScoredNetwork}s and returns a new List containing only the
+     * {@link ScoredNetwork} associated with the current network. If no network is connected the
+     * returned list will be empty.
+     * <p>
+     * Note: this filter performs some internal caching for consistency and performance. The
+     *       current network is determined at construction time and never changed. Also, the
+     *       last filtered list is saved so if the same input is provided multiple times in a row
+     *       the computation is only done once.
+     */
+    @VisibleForTesting
+    static class CurrentNetworkScoreCacheFilter implements UnaryOperator<List<ScoredNetwork>> {
+        private final NetworkKey mCurrentNetwork;
+
+        CurrentNetworkScoreCacheFilter(Supplier<WifiInfo> wifiInfoSupplier) {
+            mCurrentNetwork = NetworkKey.createFromWifiInfo(wifiInfoSupplier.get());
+        }
+
+        @Override
+        public List<ScoredNetwork> apply(List<ScoredNetwork> scoredNetworks) {
+            if (mCurrentNetwork == null || scoredNetworks.isEmpty()) {
+                return Collections.emptyList();
+            }
+
+            for (int i = 0; i < scoredNetworks.size(); i++) {
+                final ScoredNetwork scoredNetwork = scoredNetworks.get(i);
+                if (scoredNetwork.networkKey.equals(mCurrentNetwork)) {
+                    return Collections.singletonList(scoredNetwork);
+                }
+            }
+
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Filters the given set of {@link ScoredNetwork}s and returns a new List containing only the
+     * {@link ScoredNetwork} associated with the current set of {@link ScanResult}s.
+     * If there are no {@link ScanResult}s the returned list will be empty.
+     * <p>
+     * Note: this filter performs some internal caching for consistency and performance. The
+     *       current set of ScanResults is determined at construction time and never changed.
+     *       Also, the last filtered list is saved so if the same input is provided multiple
+     *       times in a row the computation is only done once.
+     */
+    @VisibleForTesting
+    static class ScanResultsScoreCacheFilter implements UnaryOperator<List<ScoredNetwork>> {
+        private final Set<NetworkKey> mScanResultKeys;
+
+        ScanResultsScoreCacheFilter(Supplier<List<ScanResult>> resultsSupplier) {
+            List<ScanResult> scanResults = resultsSupplier.get();
+            final int size = scanResults.size();
+            mScanResultKeys = new ArraySet<>(size);
+            for (int i = 0; i < size; i++) {
+                ScanResult scanResult = scanResults.get(i);
+                mScanResultKeys.add(NetworkKey.createFromScanResult(scanResult));
+            }
+        }
+
+        @Override
+        public List<ScoredNetwork> apply(List<ScoredNetwork> scoredNetworks) {
+            if (mScanResultKeys.isEmpty() || scoredNetworks.isEmpty()) {
+                return Collections.emptyList();
+            }
+
+            List<ScoredNetwork> filteredScores = new ArrayList<>();
+            for (int i = 0; i < scoredNetworks.size(); i++) {
+                final ScoredNetwork scoredNetwork = scoredNetworks.get(i);
+                if (mScanResultKeys.contains(scoredNetwork.networkKey)) {
+                    filteredScores.add(scoredNetwork);
+                }
+            }
+
+            return filteredScores;
+        }
+    }
+
     private boolean isCallerSystemUid() {
         // REQUEST_NETWORK_SCORES is a signature only permission.
         return mContext.checkCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES) ==
@@ -461,9 +705,9 @@
 
     /** Clear scores. Callers are responsible for checking permissions as appropriate. */
     private void clearInternal() {
-        sendCallback(new Consumer<INetworkScoreCache>() {
+        sendCacheUpdateCallback(new BiConsumer<INetworkScoreCache, Object>() {
             @Override
-            public void accept(INetworkScoreCache networkScoreCache) {
+            public void accept(INetworkScoreCache networkScoreCache, Object cookie) {
                 try {
                     networkScoreCache.clearScores();
                 } catch (RemoteException e) {
@@ -532,10 +776,11 @@
             final INetworkRecommendationProvider provider = getRecommendationProvider();
             if (provider != null) {
                 try {
-                    return mRequestRecommendationCaller.getRecommendationResult(provider, request);
+                    final RequestRecommendationCaller caller = mReqRecommendationCallerRef.get();
+                    return caller.getRecommendationResult(provider, request);
                 } catch (RemoteException | TimeoutException e) {
                     Log.w(TAG, "Failed to request a recommendation.", e);
-                    // TODO(jjoslin): 12/15/16 - Keep track of failures.
+                    // TODO: 12/15/16 - Keep track of failures.
                 }
             }
 
@@ -543,9 +788,9 @@
                 Log.d(TAG, "Returning the default network recommendation.");
             }
 
-            if (request != null && request.getCurrentSelectedConfig() != null) {
+            if (request != null && request.getDefaultWifiConfig() != null) {
                 return RecommendationResult.createConnectRecommendation(
-                        request.getCurrentSelectedConfig());
+                        request.getDefaultWifiConfig());
             }
             return RecommendationResult.createDoNotConnectRecommendation();
         } finally {
@@ -553,6 +798,56 @@
         }
     }
 
+    /**
+     * Request a recommendation for the best network to connect to
+     * taking into account the inputs from the {@link RecommendationRequest}.
+     *
+     * @param request a {@link RecommendationRequest} instance containing the details of the request
+     * @param remoteCallback a {@link IRemoteCallback} instance to invoke when the recommendation
+     *                       is available.
+     * @throws SecurityException if the caller is not the system
+     */
+    @Override
+    public void requestRecommendationAsync(RecommendationRequest request,
+            RemoteCallback remoteCallback) {
+        mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
+
+        final OneTimeCallback oneTimeCallback = new OneTimeCallback(remoteCallback);
+        final Pair<RecommendationRequest, OneTimeCallback> pair =
+                Pair.create(request, oneTimeCallback);
+        final Message timeoutMsg = mHandler.obtainMessage(
+                ServiceHandler.MSG_RECOMMENDATION_REQUEST_TIMEOUT, pair);
+        final INetworkRecommendationProvider provider = getRecommendationProvider();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (provider != null) {
+                try {
+                    mHandler.sendMessageDelayed(timeoutMsg, mRecommendationRequestTimeoutMs);
+                    provider.requestRecommendation(request, new IRemoteCallback.Stub() {
+                        @Override
+                        public void sendResult(Bundle data) throws RemoteException {
+                            // Remove the timeout message
+                            mHandler.removeMessages(timeoutMsg.what, pair);
+                            oneTimeCallback.sendResult(data);
+                        }
+                    }, 0 /*sequence*/);
+                    return;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to request a recommendation.", e);
+                    // TODO: 12/15/16 - Keep track of failures.
+                    // Remove the timeout message
+                    mHandler.removeMessages(timeoutMsg.what, pair);
+                    // Will fall through and send back the default recommendation.
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        // Else send back the default recommendation.
+        sendDefaultRecommendationResponse(request, oneTimeCallback);
+    }
+
     @Override
     public boolean requestScores(NetworkKey[] networks) {
         mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
@@ -562,12 +857,12 @@
             if (provider != null) {
                 try {
                     provider.requestScores(networks);
-                    // TODO(jjoslin): 12/15/16 - Consider pushing null scores into the cache to
+                    // TODO: 12/15/16 - Consider pushing null scores into the cache to
                     // prevent repeated requests for the same scores.
                     return true;
                 } catch (RemoteException e) {
                     Log.w(TAG, "Failed to request scores.", e);
-                    // TODO(jjoslin): 12/15/16 - Keep track of failures.
+                    // TODO: 12/15/16 - Keep track of failures.
                 }
             }
             return false;
@@ -579,32 +874,37 @@
     @Override
     protected void dump(final FileDescriptor fd, final PrintWriter writer, final String[] args) {
         mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
-        NetworkScorerAppData currentScorer = mNetworkScorerAppManager.getActiveScorer();
-        if (currentScorer == null) {
-            writer.println("Scoring is disabled.");
-            return;
-        }
-        writer.println("Current scorer: " + currentScorer.packageName);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            NetworkScorerAppData currentScorer = mNetworkScorerAppManager.getActiveScorer();
+            if (currentScorer == null) {
+                writer.println("Scoring is disabled.");
+                return;
+            }
+            writer.println("Current scorer: " + currentScorer.packageName);
 
-        sendCallback(new Consumer<INetworkScoreCache>() {
-            @Override
-            public void accept(INetworkScoreCache networkScoreCache) {
-                try {
-                  TransferPipe.dumpAsync(networkScoreCache.asBinder(), fd, args);
-                } catch (IOException | RemoteException e) {
-                  writer.println("Failed to dump score cache: " + e);
+            sendCacheUpdateCallback(new BiConsumer<INetworkScoreCache, Object>() {
+                @Override
+                public void accept(INetworkScoreCache networkScoreCache, Object cookie) {
+                    try {
+                        TransferPipe.dumpAsync(networkScoreCache.asBinder(), fd, args);
+                    } catch (IOException | RemoteException e) {
+                        writer.println("Failed to dump score cache: " + e);
+                    }
+                }
+            }, getScoreCacheLists());
+
+            synchronized (mServiceConnectionLock) {
+                if (mServiceConnection != null) {
+                    mServiceConnection.dump(fd, writer, args);
+                } else {
+                    writer.println("ScoringServiceConnection: null");
                 }
             }
-        }, getScoreCacheLists());
-
-        synchronized (mServiceConnectionLock) {
-            if (mServiceConnection != null) {
-                mServiceConnection.dump(fd, writer, args);
-            } else {
-                writer.println("ScoringServiceConnection: null");
-            }
+            writer.flush();
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
-        writer.flush();
     }
 
     /**
@@ -619,14 +919,15 @@
         }
     }
 
-    private void sendCallback(Consumer<INetworkScoreCache> consumer,
+    private void sendCacheUpdateCallback(BiConsumer<INetworkScoreCache, Object> consumer,
             Collection<RemoteCallbackList<INetworkScoreCache>> remoteCallbackLists) {
         for (RemoteCallbackList<INetworkScoreCache> callbackList : remoteCallbackLists) {
             synchronized (callbackList) { // Ensure only one active broadcast per RemoteCallbackList
                 final int count = callbackList.beginBroadcast();
                 try {
                     for (int i = 0; i < count; i++) {
-                        consumer.accept(callbackList.getBroadcastItem(i));
+                        consumer.accept(callbackList.getBroadcastItem(i),
+                                callbackList.getRegisteredCallbackCookie(i));
                     }
                 } finally {
                     callbackList.finishBroadcast();
@@ -651,6 +952,19 @@
         return null;
     }
 
+    @VisibleForTesting
+    public void refreshRecommendationRequestTimeoutMs() {
+        final ContentResolver cr = mContext.getContentResolver();
+        long timeoutMs = Settings.Global.getLong(cr,
+                Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, -1L /*default*/);
+        if (timeoutMs < 0) {
+            timeoutMs = TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS;
+        }
+        if (DBG) Log.d(TAG, "Updating the recommendation request timeout to " + timeoutMs + " ms");
+        mRecommendationRequestTimeoutMs = timeoutMs;
+        mReqRecommendationCallerRef.set(new RequestRecommendationCaller(timeoutMs));
+    }
+
     private static class ScoringServiceConnection implements ServiceConnection {
         private final ComponentName mComponentName;
         private final int mScoringAppUid;
@@ -756,4 +1070,83 @@
             return getResultTimed(sequence);
         }
     }
+
+    /**
+     * A wrapper around {@link RemoteCallback} that guarantees
+     * {@link RemoteCallback#sendResult(Bundle)} will be invoked at most once.
+     */
+    @VisibleForTesting
+    public static final class OneTimeCallback {
+        private final RemoteCallback mRemoteCallback;
+        private final AtomicBoolean mCallbackRun;
+
+        public OneTimeCallback(RemoteCallback remoteCallback) {
+            mRemoteCallback = remoteCallback;
+            mCallbackRun = new AtomicBoolean(false);
+        }
+
+        public void sendResult(Bundle data) {
+            if (mCallbackRun.compareAndSet(false, true)) {
+                mRemoteCallback.sendResult(data);
+            }
+        }
+    }
+
+    private static void sendDefaultRecommendationResponse(RecommendationRequest request,
+            OneTimeCallback remoteCallback) {
+        if (DBG) {
+            Log.d(TAG, "Returning the default network recommendation.");
+        }
+
+        final RecommendationResult result;
+        if (request != null && request.getDefaultWifiConfig() != null) {
+            result = RecommendationResult.createConnectRecommendation(
+                    request.getDefaultWifiConfig());
+        } else {
+            result = RecommendationResult.createDoNotConnectRecommendation();
+        }
+
+        final Bundle data = new Bundle();
+        data.putParcelable(EXTRA_RECOMMENDATION_RESULT, result);
+        remoteCallback.sendResult(data);
+    }
+
+    @VisibleForTesting
+    public final class ServiceHandler extends Handler {
+        public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT = 1;
+        public static final int MSG_RECOMMENDATIONS_ENABLED_CHANGED = 2;
+        public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED = 3;
+
+        public ServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final int what = msg.what;
+            switch (what) {
+                case MSG_RECOMMENDATION_REQUEST_TIMEOUT:
+                    if (DBG) {
+                        Log.d(TAG, "Network recommendation request timed out.");
+                    }
+                    final Pair<RecommendationRequest, OneTimeCallback> pair =
+                            (Pair<RecommendationRequest, OneTimeCallback>) msg.obj;
+                    final RecommendationRequest request = pair.first;
+                    final OneTimeCallback remoteCallback = pair.second;
+                    sendDefaultRecommendationResponse(request, remoteCallback);
+                    break;
+
+                case MSG_RECOMMENDATIONS_ENABLED_CHANGED:
+                    bindToScoringServiceIfNeeded();
+                    break;
+
+                case MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED:
+                    refreshRecommendationRequestTimeoutMs();
+                    break;
+
+                default:
+                    Log.w(TAG,"Unknown message: " + what);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index 2010e64..3c8c699 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -181,7 +181,7 @@
         }
 
         @Override // Binder call
-        public void rebootRecoveryWithCommand(String command, boolean update) {
+        public void rebootRecoveryWithCommand(String command) {
             if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
             synchronized (sRequestLock) {
                 if (!setupOrClearBcb(true, command)) {
@@ -190,10 +190,7 @@
 
                 // Having set up the BCB, go ahead and reboot.
                 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-                // PowerManagerService may additionally request uncrypting the package when it's
-                // to install an update (REBOOT_RECOVERY_UPDATE).
-                pm.reboot(update ? PowerManager.REBOOT_RECOVERY_UPDATE :
-                        PowerManager.REBOOT_RECOVERY);
+                pm.reboot(PowerManager.REBOOT_RECOVERY);
             }
         }
 
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
new file mode 100644
index 0000000..d51e96a
--- /dev/null
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Build;
+import android.os.RecoverySystem;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.format.DateUtils;
+import android.util.ExceptionUtils;
+import android.util.MathUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Utilities to help rescue the system from crash loops. Callers are expected to
+ * report boot events and persistent app crashes, and if they happen frequently
+ * enough this class will slowly escalate through several rescue operations
+ * before finally rebooting and prompting the user if they want to wipe data as
+ * a last resort.
+ *
+ * @hide
+ */
+public class RescueParty {
+    private static final String TAG = "RescueParty";
+
+    private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
+    private static final String PROP_RESCUE_LEVEL = "sys.rescue_level";
+    private static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
+    private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start";
+
+    private static final int LEVEL_NONE = 0;
+    private static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1;
+    private static final int LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 2;
+    private static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3;
+    private static final int LEVEL_FACTORY_RESET = 4;
+
+    private static final boolean DISABLE_RESET_SETTINGS = true;
+
+    /** Threshold for boot loops */
+    private static final Threshold sBoot = new BootThreshold();
+    /** Threshold for app crash loops */
+    private static SparseArray<Threshold> sApps = new SparseArray<>();
+
+    private static boolean isDisabled() {
+        return Build.IS_ENG || SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false);
+    }
+
+    /**
+     * Take note of a boot event. If we notice too many of these events
+     * happening in rapid succession, we'll send out a rescue party.
+     */
+    public static void noteBoot(Context context) {
+        if (isDisabled()) return;
+        if (sBoot.incrementAndTest()) {
+            sBoot.reset();
+            incrementRescueLevel(sBoot.uid);
+            executeRescueLevel(context);
+        }
+    }
+
+    /**
+     * Take note of a persistent app crash. If we notice too many of these
+     * events happening in rapid succession, we'll send out a rescue party.
+     */
+    public static void notePersistentAppCrash(Context context, int uid) {
+        if (isDisabled()) return;
+        Threshold t = sApps.get(uid);
+        if (t == null) {
+            t = new AppThreshold(uid);
+            sApps.put(uid, t);
+        }
+        if (t.incrementAndTest()) {
+            t.reset();
+            incrementRescueLevel(t.uid);
+            executeRescueLevel(context);
+        }
+    }
+
+    /**
+     * Check if we're currently attempting to reboot for a factory reset.
+     */
+    public static boolean isAttemptingFactoryReset() {
+        return SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) == LEVEL_FACTORY_RESET;
+    }
+
+    /**
+     * Escalate to the next rescue level. After incrementing the level you'll
+     * probably want to call {@link #executeRescueLevel(Context)}.
+     */
+    private static void incrementRescueLevel(int triggerUid) {
+        final int level = MathUtils.constrain(
+                SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
+                LEVEL_NONE, LEVEL_FACTORY_RESET);
+        SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level));
+
+        EventLogTags.writeRescueLevel(level, triggerUid);
+        Slog.w(TAG, "Incremented rescue level to " + levelToString(level));
+    }
+
+    /**
+     * Called when {@code SettingsProvider} has been published, which is a good
+     * opportunity to reset any settings depending on our rescue level.
+     */
+    public static void onSettingsProviderPublished(Context context) {
+        executeRescueLevel(context);
+    }
+
+    private static void executeRescueLevel(Context context) {
+        final int level = SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE);
+        if (level == LEVEL_NONE) return;
+
+        Slog.w(TAG, "Attempting rescue level " + levelToString(level));
+        try {
+            executeRescueLevelInternal(context, level);
+            EventLogTags.writeRescueSuccess(level);
+            Slog.d(TAG, "Finished rescue level " + levelToString(level));
+        } catch (Throwable t) {
+            EventLogTags.writeRescueFailure(level, ExceptionUtils.getCompleteMessage(t));
+            Slog.e(TAG, "Failed rescue level " + levelToString(level), t);
+        }
+    }
+
+    private static void executeRescueLevelInternal(Context context, int level) throws Exception {
+        switch (level) {
+            case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+                resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
+                break;
+            case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+                resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_CHANGES);
+                break;
+            case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+                resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS);
+                break;
+            case LEVEL_FACTORY_RESET:
+                RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
+                break;
+        }
+    }
+
+    private static void resetAllSettings(Context context, int mode) throws Exception {
+        if (DISABLE_RESET_SETTINGS) return;
+
+        // Try our best to reset all settings possible, and once finished
+        // rethrow any exception that we encountered
+        Exception res = null;
+        final ContentResolver resolver = context.getContentResolver();
+        try {
+            Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM);
+        } catch (Exception e) {
+            res = new RuntimeException("Failed to reset global settings", e);
+        }
+        for (int userId : getAllUserIds(context)) {
+            try {
+                Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
+            } catch (Exception e) {
+                res = new RuntimeException("Failed to reset secure settings for " + userId, e);
+            }
+        }
+        if (res != null) {
+            throw res;
+        }
+    }
+
+    /**
+     * Threshold that can be triggered if a number of events occur within a
+     * window of time.
+     */
+    private abstract static class Threshold {
+        public abstract int getCount();
+        public abstract void setCount(int count);
+        public abstract long getStart();
+        public abstract void setStart(long start);
+
+        private final int uid;
+        private final int triggerCount;
+        private final long triggerWindow;
+
+        public Threshold(int uid, int triggerCount, long triggerWindow) {
+            this.uid = uid;
+            this.triggerCount = triggerCount;
+            this.triggerWindow = triggerWindow;
+        }
+
+        public void reset() {
+            setCount(0);
+            setStart(0);
+        }
+
+        /**
+         * @return if this threshold has been triggered
+         */
+        public boolean incrementAndTest() {
+            final long now = SystemClock.elapsedRealtime();
+            final long window = now - getStart();
+            if (window > triggerWindow) {
+                setCount(1);
+                setStart(now);
+                return false;
+            } else {
+                int count = getCount() + 1;
+                setCount(count);
+                EventLogTags.writeRescueNote(uid, count, window);
+                Slog.w(TAG, "Noticed " + count + " events for UID " + uid + " in last "
+                        + (window / 1000) + " sec");
+                return (count >= triggerCount);
+            }
+        }
+    }
+
+    /**
+     * Specialization of {@link Threshold} for monitoring boot events. It stores
+     * counters in system properties for robustness.
+     */
+    private static class BootThreshold extends Threshold {
+        public BootThreshold() {
+            // We're interested in 5 events in any 300 second period; this
+            // window is super relaxed because booting can take a long time if
+            // forced to dexopt things.
+            super(android.os.Process.ROOT_UID, 5, 300 * DateUtils.SECOND_IN_MILLIS);
+        }
+
+        @Override
+        public int getCount() {
+            return SystemProperties.getInt(PROP_RESCUE_BOOT_COUNT, 0);
+        }
+
+        @Override
+        public void setCount(int count) {
+            SystemProperties.set(PROP_RESCUE_BOOT_COUNT, Integer.toString(count));
+        }
+
+        @Override
+        public long getStart() {
+            return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0);
+        }
+
+        @Override
+        public void setStart(long start) {
+            SystemProperties.set(PROP_RESCUE_BOOT_START, Long.toString(start));
+        }
+    }
+
+    /**
+     * Specialization of {@link Threshold} for monitoring app crashes. It stores
+     * counters in memory.
+     */
+    private static class AppThreshold extends Threshold {
+        private int count;
+        private long start;
+
+        public AppThreshold(int uid) {
+            // We're interested in 5 events in any 30 second period; apps crash
+            // pretty quickly so we can keep a tight leash on them.
+            super(uid, 5, 30 * DateUtils.SECOND_IN_MILLIS);
+        }
+
+        @Override public int getCount() { return count; }
+        @Override public void setCount(int count) { this.count = count; }
+        @Override public long getStart() { return start; }
+        @Override public void setStart(long start) { this.start = start; }
+    }
+
+    private static int[] getAllUserIds(Context context) {
+        int[] userIds = { UserHandle.USER_SYSTEM };
+        try {
+            final UserManager um = context.getSystemService(UserManager.class);
+            for (UserInfo user : um.getUsers()) {
+                if (user.id != UserHandle.USER_SYSTEM) {
+                    userIds = ArrayUtils.appendInt(userIds, user.id);
+                }
+            }
+        } catch (Throwable t) {
+            Slog.w(TAG, "Trouble discovering users", t);
+        }
+        return userIds;
+    }
+
+    private static String levelToString(int level) {
+        switch (level) {
+            case LEVEL_NONE: return "NONE";
+            case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: return "RESET_SETTINGS_UNTRUSTED_DEFAULTS";
+            case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return "RESET_SETTINGS_UNTRUSTED_CHANGES";
+            case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: return "RESET_SETTINGS_TRUSTED_DEFAULTS";
+            case LEVEL_FACTORY_RESET: return "FACTORY_RESET";
+            default: return Integer.toString(level);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 55d31c3..c07add0 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -33,6 +33,7 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.usage.StorageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -88,11 +89,13 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IMediaContainerService;
+import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
@@ -104,7 +107,7 @@
 import com.android.server.NativeDaemonConnector.Command;
 import com.android.server.NativeDaemonConnector.SensitiveArg;
 import com.android.server.pm.PackageManagerService;
-
+import com.android.server.storage.AppFuseBridge;
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
@@ -135,6 +138,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -337,6 +341,15 @@
 
     private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
 
+    /** Holding lock for AppFuse business */
+    private final Object mAppFuseLock = new Object();
+
+    @GuardedBy("mAppFuseLock")
+    private int mNextAppFuseName = 0;
+
+    @GuardedBy("mAppFuseLock")
+    private final SparseArray<Integer> mAppFusePids = new SparseArray<>();
+
     private VolumeInfo findVolumeByIdOrThrow(String id) {
         synchronized (mLock) {
             final VolumeInfo vol = mVolumes.get(id);
@@ -2067,9 +2080,6 @@
 
     @Override
     public String getPrimaryStorageUuid() {
-        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
-        waitForReady();
-
         synchronized (mLock) {
             return mPrimaryStorageUuid;
         }
@@ -2979,34 +2989,124 @@
         }
     }
 
-    @Override
-    public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
-        try {
-            final int uid = Binder.getCallingUid();
-            final int pid = Binder.getCallingPid();
-            final NativeDaemonEvent event =
-                    mConnector.execute("appfuse", "mount", uid, pid, name);
-            if (event.getFileDescriptors() == null) {
-                throw new RemoteException("AppFuse FD from vold is null.");
+    class CloseableHolder<T extends AutoCloseable> implements AutoCloseable {
+        @Nullable T mCloseable;
+
+        CloseableHolder(T closeable) {
+            mCloseable = closeable;
+        }
+
+        @Nullable T get() {
+            return mCloseable;
+        }
+
+        @Nullable T release() {
+            final T result = mCloseable;
+            mCloseable = null;
+            return result;
+        }
+
+        @Override
+        public void close() {
+            if (mCloseable != null) {
+                IoUtils.closeQuietly(mCloseable);
             }
-            return ParcelFileDescriptor.fromFd(
-                    event.getFileDescriptors()[0],
-                    mHandler,
-                    new ParcelFileDescriptor.OnCloseListener() {
-                        @Override
-                        public void onClose(IOException e) {
-                            try {
-                                final NativeDaemonEvent event = mConnector.execute(
-                                        "appfuse", "unmount", uid, pid, name);
-                            } catch (NativeDaemonConnectorException unmountException) {
-                                Log.e(TAG, "Failed to unmount appfuse.");
-                            }
-                        }
-                    });
-        } catch (NativeDaemonConnectorException e) {
+        }
+    }
+
+    class AppFuseMountScope implements AppFuseBridge.IMountScope {
+        final int mUid;
+        final int mName;
+        final ParcelFileDescriptor mDeviceFd;
+
+        AppFuseMountScope(int uid, int pid, int name) throws NativeDaemonConnectorException {
+            final NativeDaemonEvent event = mConnector.execute(
+                    "appfuse", "mount", uid, Process.myPid(), name);
+            mUid = uid;
+            mName = name;
+            synchronized (mLock) {
+                mAppFusePids.put(name, pid);
+            }
+            if (event.getFileDescriptors() != null &&
+                    event.getFileDescriptors().length > 0) {
+                mDeviceFd = new ParcelFileDescriptor(event.getFileDescriptors()[0]);
+            } else {
+                mDeviceFd = null;
+            }
+        }
+
+        @Override
+        public void close() throws NativeDaemonConnectorException {
+            try {
+                IoUtils.closeQuietly(mDeviceFd);
+                mConnector.execute(
+                        "appfuse", "unmount", mUid, Process.myPid(), mName);
+            } finally {
+                synchronized (mLock) {
+                    mAppFusePids.delete(mName);
+                }
+            }
+        }
+
+        @Override
+        public ParcelFileDescriptor getDeviceFileDescriptor() {
+            return mDeviceFd;
+        }
+    }
+
+    @Override
+    public AppFuseMount mountProxyFileDescriptorBridge() throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
+        final int name;
+        synchronized (mAppFuseLock) {
+            name = mNextAppFuseName++;
+        }
+        try (CloseableHolder<AppFuseMountScope> mountScope =
+                new CloseableHolder<>(new AppFuseMountScope(uid, pid, name))) {
+            if (mountScope.get().getDeviceFileDescriptor() == null) {
+                throw new RemoteException("Failed to obtain device FD");
+            }
+
+            // Create communication channel.
+            final ArrayBlockingQueue<Boolean> channel = new ArrayBlockingQueue<>(1);
+            final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair();
+            try (CloseableHolder<ParcelFileDescriptor> remote = new CloseableHolder<>(fds[0])) {
+                new Thread(
+                        new AppFuseBridge(mountScope.release(), fds[1], channel),
+                        AppFuseBridge.TAG).start();
+                if (!channel.take()) {
+                    throw new RemoteException("Failed to init AppFuse mount point");
+                }
+
+                return new AppFuseMount(name, remote.release());
+            }
+        } catch (NativeDaemonConnectorException e){
             throw e.rethrowAsParcelableException();
-        } catch (IOException e) {
-            throw new RemoteException(e.getMessage());
+        } catch (IOException | InterruptedException error) {
+            throw new RemoteException(error.getMessage());
+        }
+    }
+
+    @Override
+    public ParcelFileDescriptor openProxyFileDescriptor(int mountId, int fileId, int mode) {
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
+        try {
+            synchronized (mAppFuseLock) {
+                final int expectedPid = mAppFusePids.get(mountId, -1);
+                if (expectedPid == -1) {
+                    Slog.i(TAG, "The mount point has already been unmounted");
+                    return null;
+                }
+                if (expectedPid != pid) {
+                    throw new SecurityException("Mount point was not created by this process.");
+                }
+            }
+            return AppFuseBridge.openFile(uid, mountId, fileId, mode);
+        } catch (FileNotFoundException error) {
+            Slog.e(TAG, "Failed to openProxyFileDescriptor", error);
+            return null;
         }
     }
 
@@ -3168,6 +3268,29 @@
         }
     }
 
+    @Override
+    public long getCacheQuotaBytes(String volumeUuid, int uid) {
+        if (uid != Binder.getCallingUid()) {
+            mContext.enforceCallingPermission(android.Manifest.permission.STORAGE_INTERNAL, TAG);
+        }
+        // TODO: wire up to cache quota once merged
+        return 64 * TrafficStats.MB_IN_BYTES;
+    }
+
+    @Override
+    public long getCacheSizeBytes(String volumeUuid, int uid) {
+        if (uid != Binder.getCallingUid()) {
+            mContext.enforceCallingPermission(android.Manifest.permission.STORAGE_INTERNAL, TAG);
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return mContext.getSystemService(StorageStatsManager.class)
+                    .queryStatsForUid(volumeUuid, uid).getCacheBytes();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     private void addObbStateLocked(ObbState obbState) throws RemoteException {
         final IBinder binder = obbState.getBinder();
         List<ObbState> obbStates = mObbMounts.get(binder);
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index d879919..3f97d4f 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -36,7 +36,6 @@
     private final Context mContext;
     private boolean mSafeMode;
     private boolean mRuntimeRestarted;
-    private boolean mFirstBoot;
 
     // Services that should receive lifecycle events.
     private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
@@ -260,16 +259,6 @@
         mRuntimeRestarted = runtimeRestarted;
     }
 
-    /**
-     * @return true if it's first boot after OTA
-     */
-    public boolean isFirstBoot() {
-        return mFirstBoot;
-    }
-
-    void setFirstBoot(boolean firstBoot) {
-        mFirstBoot = firstBoot;
-    }
 
     /**
      * Outputs the state of this manager to the System log.
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 62f4f19..0834eb8 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1360,6 +1360,7 @@
         }
 
         Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         Bundle data = new Bundle();
         state.fillInNotifierBundle(data);
         intent.putExtras(data);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 440ac90..8f99127 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -45,12 +45,16 @@
 import android.service.dreams.Sandman;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.view.WindowManagerInternal;
 import android.view.WindowManagerPolicy;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
 
 import com.android.internal.R;
 import com.android.internal.app.DisableCarModeActivity;
@@ -343,6 +347,28 @@
         }
 
         @Override
+        public String[] getAvailableThemes() {
+            if (getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_THEME_OVERLAY)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Slog.e(TAG, "getAvailableThemes requires MODIFY_THEME_OVERLAY permission");
+                return null;
+            }
+            String def = SystemProperties.get("ro.boot.vendor.overlay.theme");
+            if (TextUtils.isEmpty(def)) {
+                def = null;
+            }
+            String[] fileList = new File("/vendor/overlay").list();
+            if (fileList == null) return new String[0];
+            ArrayList<String> options = new ArrayList(fileList.length + 1);
+            Collections.addAll(options, fileList);
+            if (!options.contains(def)) {
+                options.add(0, def);
+            }
+            return options.toArray(new String[options.size()]);
+        }
+
+        @Override
         public int getNightMode() {
             synchronized (mLock) {
                 return mNightMode;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 11e1a9d..bfa3f04 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -63,8 +63,8 @@
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteStatement;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -76,6 +76,7 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -111,15 +112,16 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
-import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -174,17 +176,18 @@
     private static final String PRE_N_DATABASE_NAME = "accounts.db";
     private static final Intent ACCOUNTS_CHANGED_INTENT;
 
+    private static final int SIGNATURE_CHECK_MISMATCH = 0;
+    private static final int SIGNATURE_CHECK_MATCH = 1;
+    private static final int SIGNATURE_CHECK_UID_MATCH = 2;
+
     static {
         ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
         ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
     }
 
-
     private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
     private final AtomicInteger mNotificationIds = new AtomicInteger(1);
 
-    private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
-
     static class UserAccounts {
         private final int userId;
         final AccountsDb accountsDb;
@@ -204,6 +207,11 @@
         /** protected by the {@link #cacheLock} */
         private final TokenCache accountTokenCaches = new TokenCache();
 
+        /** protected by the {@link #cacheLock} */
+        // TODO use callback to set up the map.
+        private final Map<String, LinkedHashSet<String>> mApplicationAccountRequestMappings =
+                new HashMap<>();
+
         /**
          * protected by the {@link #cacheLock}
          *
@@ -261,8 +269,6 @@
 
         sThis.set(this);
 
-        addRequestsForPreInstalledApplications();
-
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
@@ -285,7 +291,7 @@
                         @Override
                         public void run() {
                             purgeOldGrantsAll();
-                            // TODO remove visibility entries.
+                            // Notify authenticator about removed app?
                         }
                     };
                     mHandler.post(purgingRunnable);
@@ -294,29 +300,6 @@
             }
         }, intentFilter);
 
-        IntentFilter packageAddedOrChangedFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageAddedOrChangedFilter.addDataScheme("package");
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context1, Intent intent) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        int uidOfInstalledApplication =
-                                intent.getIntExtra(Intent.EXTRA_UID, -1);
-                        if(uidOfInstalledApplication != -1) {
-                            registerAccountTypesSupported(
-                                    uidOfInstalledApplication,
-                                    getUserAccounts(
-                                    UserHandle.getUserId(uidOfInstalledApplication)));
-                        }
-                    }
-                });
-            }
-        }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
-
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
@@ -380,11 +363,13 @@
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     for (String packageName : packageNames) {
-                        if (mPackageManager.checkPermission(
-                                Manifest.permission.GET_ACCOUNTS, packageName)
-                                        != PackageManager.PERMISSION_GRANTED) {
-                            continue;
-                        }
+                                // if app asked for permission we need to cancel notification even
+                                // for O+ applications.
+                                if (mPackageManager.checkPermission(
+                                        Manifest.permission.GET_ACCOUNTS,
+                                        packageName) != PackageManager.PERMISSION_GRANTED) {
+                                    continue;
+                                }
 
                         if (accounts == null) {
                             accounts = getAccountsAsUser(null, userId, "android");
@@ -443,106 +428,376 @@
     }
 
     @Override
-    public boolean addAccountExplicitlyWithVisibility(Account account, String password, Bundle extras,
-            Map uidToVisibility) {
-        // TODO implementation
-        return false;
+    public boolean addAccountExplicitlyWithVisibility(Account account, String password,
+            Bundle extras, Map uidToVisibility) {
+        Bundle.setDefusable(extras, true);
+
+        final int callingUid = Binder.getCallingUid();
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + callingUid
+                    + ", pid " + Binder.getCallingPid());
+        }
+        Preconditions.checkNotNull(account, "account cannot be null");
+        int userId = UserHandle.getCallingUserId();
+        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+            String msg = String.format("uid %s cannot explicitly add accounts of type: %s",
+                    callingUid, account.type);
+            throw new SecurityException(msg);
+        }
+        /*
+         * Child users are not allowed to add accounts. Only the accounts that are shared by the
+         * parent profile can be added to child profile.
+         *
+         * TODO: Only allow accounts that were shared to be added by a limited user.
+         */
+        // fails if the account already exists
+        long identityToken = clearCallingIdentity();
+        try {
+            UserAccounts accounts = getUserAccounts(userId);
+            return addAccountInternal(accounts, account, password, extras, callingUid,
+                    (Map<Integer, Integer>) uidToVisibility);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
     }
 
     @Override
     public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
             String accountType) {
-        // TODO Implement.
-        return new HashMap<Account, Integer>();
+        int callingUid = Binder.getCallingUid();
+        boolean isSystemUid = UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
+        List<String> managedTypes =
+                getTypesForCaller(callingUid, UserHandle.getUserId(callingUid), isSystemUid);
+
+        if ((accountType != null && !managedTypes.contains(accountType))
+                || (accountType == null && !isSystemUid)) {
+            throw new SecurityException(
+                    "getAccountsAndVisibilityForPackage() called from unauthorized uid "
+                            + callingUid + " with packageName=" + packageName);
+        }
+        if (accountType != null) {
+            managedTypes = new ArrayList<String>();
+            managedTypes.add(accountType);
+        }
+
+        return getAccountsAndVisibilityForPackage(packageName, managedTypes, callingUid,
+                getUserAccounts(UserHandle.getUserId(callingUid)));
+    }
+
+    /*
+     * accountTypes may not be null
+     */
+    private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
+            List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
+        int uid = 0;
+        try {
+            uid = mPackageManager.getPackageUidAsUser(packageName,
+                    UserHandle.getUserId(callingUid));
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found " + e.getMessage());
+            return new HashMap<>();
+        }
+
+        Map<Account, Integer> result = new HashMap<>();
+        for (String accountType : accountTypes) {
+            synchronized (accounts.cacheLock) {
+                final Account[] accountsOfType = accounts.accountCache.get(accountType);
+                if (accountsOfType != null) {
+                    for (Account account : accountsOfType) {
+                        result.put(account,
+                                resolveAccountVisibility(account, uid, packageName, accounts));
+                    }
+                }
+            }
+        }
+        return filterSharedAccounts(accounts, result, callingUid, packageName);
     }
 
     @Override
-    public int[] getRequestingUidsForType(String accountType) {
+    public Map<Integer, Integer> getUidsAndVisibilityForAccount(Account account) {
+        if (account == null) throw new IllegalArgumentException("account is null");
         int callingUid = Binder.getCallingUid();
-        if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
-            String msg = String.format(
-                    "uid %s cannot get secrets for accounts of type: %s",
-                    callingUid,
-                    accountType);
+        int userId = UserHandle.getUserId(callingUid);
+        UserAccounts accounts = getUserAccounts(userId);
+        if (!isAccountManagedByCaller(account.type, callingUid, userId)
+                && !isSystemUid(callingUid)) {
+            String msg =
+                    String.format("uid %s cannot get secrets for account %s", callingUid, account);
             throw new SecurityException(msg);
         }
-        // TODO Implement.
-        return new int[]{};
+        return getUidsAndVisibilityForAccount(account, accounts);
+    }
+
+    /**
+     * Returns all UIDs and visibility values, which were set for given account
+     *
+     * @param account account
+     * @param accounts UserAccount that currently hosts the account and application
+     *
+     * @return Map from uid to visibility.
+     */
+    private Map<Integer, Integer> getUidsAndVisibilityForAccount(Account account,
+            UserAccounts accounts) {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            return accounts.accountsDb.findAllVisibilityValuesForAccount(account);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+
     }
 
     @Override
     public int getAccountVisibility(Account a, int uid) {
-        // TODO Implement.
-        return 0;
+        if (a == null) throw new IllegalArgumentException("account is null");
+        int callingUid = Binder.getCallingUid();
+        if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))
+            && !isSystemUid(callingUid)) {
+            String msg = String.format(
+                    "uid %s cannot get secrets for accounts of type: %s",
+                    callingUid,
+                    a.type);
+            throw new SecurityException(msg);
+        }
+        return getAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
+    }
+
+    /**
+     * Method gets visibility for given account and UID from the database
+     *
+     * @param account The account to check visibility of
+     * @param uid UID to check visibility of
+     * @param accounts UserAccount that currently hosts the account and application
+     *
+     * @return Visibility value, AccountManager.VISIBILITY_UNDEFINED if no value was stored.
+     *
+     */
+    private int getAccountVisibility(Account account, int uid, UserAccounts accounts) {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            Integer visibility = accounts.accountsDb.findAccountVisibility(account, uid);
+            return visibility != null ? visibility : AccountManager.VISIBILITY_UNDEFINED;
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    /**
+     * Method which handles default values for Account visibility.
+     *
+     * @param account The account to check visibility.
+     * @param uid UID to check visibility.
+     * @param packageName Package name to check visibility - the method assumes that it has the same
+     *        uid as specified in the parameter.
+     * @param accounts UserAccount that currently hosts the account and application
+     *
+     * @return Visibility value, the method never returns AccountManager.VISIBILITY_UNDEFINED
+     *
+     */
+    private Integer resolveAccountVisibility(Account account, int uid, String packageName,
+            UserAccounts accounts) {
+        if (packageName == null) {
+            packageName = getPackageNameForUid(uid);
+        }
+        // System visibility can not be restricted.
+        if (UserHandle.isSameApp(uid, Process.SYSTEM_UID)) {
+            return AccountManager.VISIBILITY_VISIBLE;
+        }
+
+        int signatureCheckResult =
+                checkPackageSignature(account.type, uid, accounts.userId);
+
+        // Authenticator can not restrict visibility to itself.
+        if (signatureCheckResult == SIGNATURE_CHECK_UID_MATCH) {
+            return AccountManager.VISIBILITY_VISIBLE; // Authenticator can always see the account
+        }
+
+        // Return stored value if it was set.
+        int visibility = getAccountVisibility(account, uid, accounts);
+
+        if (AccountManager.VISIBILITY_UNDEFINED != visibility) {
+            return visibility;
+        }
+
+        if (isPermittedForPackage(packageName, accounts.userId,
+                Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) {
+            return AccountManager.VISIBILITY_VISIBLE;
+
+        }
+        // Profile owner gets visibility by default.
+        if(isProfileOwner(uid)) {
+            return AccountManager.VISIBILITY_VISIBLE;
+        }
+        // Apps with READ_CONTACTS permission get visibility by default even post O.
+        boolean canReadContacts = checkReadContactsPermission(packageName, accounts.userId);
+
+        boolean preO = isPreOApplication(packageName);
+        if ((signatureCheckResult != SIGNATURE_CHECK_MISMATCH)
+                || (preO && checkGetAccountsPermission(packageName, accounts.userId))
+                || canReadContacts) {
+            // Use legacy for preO apps with GET_ACCOUNTS permission or pre/postO with signature
+            // match.
+            visibility = getAccountVisibility(account,
+                    AccountManager.UID_KEY_DEFAULT_LEGACY_VISIBILITY, accounts);
+            if (AccountManager.VISIBILITY_UNDEFINED == visibility) {
+                visibility = AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
+            }
+        } else {
+            visibility = getAccountVisibility(account, AccountManager.UID_KEY_DEFAULT_VISIBILITY,
+                    accounts);
+            if (AccountManager.VISIBILITY_UNDEFINED == visibility) {
+                visibility = AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE;
+            }
+        }
+        return visibility;
+    }
+
+    /**
+     * Checks targetSdk for a package;
+     *
+     * @param packageName Package Name
+     *
+     * @return True if package's target SDK is below {@link android.os.Build.VERSION_CODES#O}, or
+     *         undefined
+     */
+    private boolean isPreOApplication(String packageName) {
+        try {
+            long identityToken = clearCallingIdentity();
+            ApplicationInfo applicationInfo;
+            try {
+                applicationInfo = mPackageManager.getApplicationInfo(packageName, 0);
+            } finally {
+                restoreCallingIdentity(identityToken);
+            }
+
+            if (applicationInfo != null) {
+                int version = applicationInfo.targetSdkVersion;
+                return version < android.os.Build.VERSION_CODES.O;
+            }
+            return true;
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found " + e.getMessage());
+            return true;
+        }
     }
 
     @Override
-    public boolean setAccountVisibility(Account a, int uid, int visibility) {
-        // TODO Implement.
-        return false;
-    }
-
-    /**
-     * Registers the requested login account types requested by all the applications already
-     * installed on the device.
-     */
-    private void addRequestsForPreInstalledApplications() {
-        List<PackageInfo> allInstalledPackages = mPackageManager.getInstalledPackages(0);
-        for(PackageInfo pi : allInstalledPackages) {
-            int currentUid = pi.applicationInfo.uid;
-            if(currentUid != -1) {
-                registerAccountTypesSupported(currentUid,
-                        getUserAccounts(UserHandle.getUserId(currentUid)));
-            }
+    public boolean setAccountVisibility(Account a, int uid, int newVisibility) {
+        if (a == null) throw new IllegalArgumentException("account is null");
+        int callingUid = Binder.getCallingUid();
+        if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))
+            && !isSystemUid(callingUid)) {
+            String msg = String.format(
+                    "uid %s cannot get secrets for accounts of type: %s",
+                    callingUid,
+                    a.type);
+            throw new SecurityException(msg);
         }
+        return setAccountVisibility(a, uid, newVisibility, true /* notify */,
+                getUserAccounts(UserHandle.getUserId(callingUid)));
     }
 
     /**
-     * Registers an application, represented by a UID, to support account types detailed in the
-     * applications manifest as well as allowing it to opt for notifications.
+     * Gives a certain UID, represented a application, access to an account. This method
+     * is called indirectly by the Authenticator.
      *
-     * @param uid UID of application
-     * @param ua UserAccount that currently hosts the account and application
+     * @param account Account to update visibility
+     * @param uid to add visibility of the Account
+     * @param newVisibility new visibility
+     * @param notify if the flag is set applications will get notification about visibility change
+     * @param accounts UserAccount that currently hosts the account and application
+     *
+     * @return True if account visibility was changed.
      */
-    private void registerAccountTypesSupported(int uid, UserAccounts ua) {
-        /* Account types supported are drawn from the Android Manifest of the Application */
-        String interestedPackages = null;
-        try {
-            String[] allPackages = mPackageManager.getPackagesForUid(uid);
-            if (allPackages != null) {
-                for (String aPackage : allPackages) {
-                    ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
-                            PackageManager.GET_META_DATA);
-                    Bundle b = ai.metaData;
-                    if (b == null) {
-                        return;
+    private boolean setAccountVisibility(Account account, int uid, int newVisibility,
+            boolean notify, UserAccounts accounts) {
+        synchronized (accounts.cacheLock) {
+            LinkedHashSet<String> interestedPackages;
+            if (notify) {
+                if (uid < 0) {
+                    interestedPackages = getRequestingPackageNames(account.type, accounts);
+                } else {
+                    interestedPackages = new LinkedHashSet<>();
+                    String[] subPackages = mPackageManager.getPackagesForUid(uid);
+                    if (subPackages != null) {
+                        Collections.addAll(interestedPackages, subPackages);
                     }
-                    interestedPackages = b.getString(AccountManager.SUPPORTED_ACCOUNT_TYPES);
+                }
+            } else {
+                // Notifications will not be send.
+                interestedPackages = new LinkedHashSet<>();
+            }
+            Integer[] interestedPackagesVisibility = new Integer[interestedPackages.size()];
+
+            final long accountId = accounts.accountsDb.findDeAccountId(account);
+            if (accountId < 0) {
+                return false;
+            }
+            int index = 0;
+            for (String packageName : interestedPackages) {
+                interestedPackagesVisibility[index++] =
+                        resolveAccountVisibility(account, uid, packageName, accounts);
+            }
+
+            final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+            try {
+                if (!accounts.accountsDb.setAccountVisibility(accountId, uid, newVisibility)) {
+                    return false;
+                }
+            } finally {
+                StrictMode.setThreadPolicy(oldPolicy);
+            }
+
+            index = 0;
+            for (String packageName : interestedPackages) {
+                int visibility = resolveAccountVisibility(account, uid, packageName, accounts);
+                if (visibility != interestedPackagesVisibility[index++]) {
+                        sendNotification(packageName, account, accounts.userId);
                 }
             }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.d("NameNotFoundException", e.getMessage());
-        }
-        if (interestedPackages != null) {
-            // TODO request visibility
-            // requestAccountVisibility(interestedPackages.split(";"), uid, ua);
+            if (notify) {
+                sendAccountsChangedBroadcast(accounts.userId);
+            }
+            return true;
         }
     }
 
     /**
      * Sends a direct intent to a package, notifying it of a visible account change.
      *
-     * @param desiredPackage to send Account to
-     * @param visibleAccount to send to package
+     * @param packageName to send Account to
+     * @param account to send to package
+     * @param userId User
      */
-    private void sendNotification(String desiredPackage, Account visibleAccount) {
-        Intent intent = new Intent();
-        intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
-        intent.setAction(AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED);
-        intent.setPackage(desiredPackage);
-        // TODO update documentation, add account extra if new account became visible
-        // intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
-        mContext.sendBroadcast(intent);
+    private void sendNotification(String packageName, Account account, int userId) {
+        // TODO send notification so apps subscribed in runtime.
+    }
+
+    private void sendNotification(Account account, UserAccounts accounts) {
+        LinkedHashSet<String> interestedPackages = getRequestingPackageNames(account.type,
+                accounts);
+        for (String packageName : interestedPackages) {
+            try {
+                final int uid = mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
+                int visibility = resolveAccountVisibility(account, uid, packageName, accounts);
+                if (visibility != AccountManager.VISIBILITY_NOT_VISIBLE) {
+                    sendNotification(packageName, account, accounts.userId);
+                }
+            } catch (NameNotFoundException e) {
+                // ignore
+            }
+        }
+    }
+
+    LinkedHashSet<String> getRequestingPackageNames(String accountType, UserAccounts accouns) {
+        // TODO return packages registered to get notifications.
+        return new LinkedHashSet<String>();
+    }
+
+    private void sendAccountsChangedBroadcast(int userId) {
+        Log.i(TAG, "the accounts changed, sending broadcast of "
+                + ACCOUNTS_CHANGED_INTENT.getAction());
+        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
     }
 
     @Override
@@ -682,6 +937,12 @@
                         accounts.userDataCache.remove(account);
                         accounts.authTokenCache.remove(account);
                         accounts.accountTokenCaches.remove(account);
+                        LinkedHashSet<String> interestedPackages =
+                                getRequestingPackageNames(account.type, accounts);
+                        for (String packageName : interestedPackages) {
+                            sendNotification(packageName, null, accounts.userId);
+                        }
+
                     } else {
                         ArrayList<String> accountNames = accountNamesByType.get(account.type);
                         if (accountNames == null) {
@@ -786,7 +1047,7 @@
                     AccountsDb.TABLE_ACCOUNTS);
 
             for (Account account : accountsToRemove) {
-                removeAccountInternal(accounts, account, Process.myUid());
+                removeAccountInternal(accounts, account, Process.SYSTEM_UID);
             }
         }
     }
@@ -1043,7 +1304,7 @@
 
     private boolean isCrossUser(int callingUid, int userId) {
         return (userId != UserHandle.getCallingUserId()
-                && callingUid != Process.myUid()
+                && callingUid != Process.SYSTEM_UID
                 && mContext.checkCallingOrSelfPermission(
                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                                 != PackageManager.PERMISSION_GRANTED);
@@ -1051,42 +1312,7 @@
 
     @Override
     public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
-        Bundle.setDefusable(extras, true);
-        // clears the visible list functionality for this account because this method allows
-        // default account access to all applications for account.
-
-        final int callingUid = Binder.getCallingUid();
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "addAccountExplicitly: " + account
-                    + ", caller's uid " + callingUid
-                    + ", pid " + Binder.getCallingPid());
-        }
-        if (account == null) throw new IllegalArgumentException("account is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
-            String msg = String.format(
-                    "uid %s cannot explicitly add accounts of type: %s",
-                    callingUid,
-                    account.type);
-            throw new SecurityException(msg);
-        }
-
-        /*
-         * Child users are not allowed to add accounts. Only the accounts that are
-         * shared by the parent profile can be added to child profile.
-         *
-         * TODO: Only allow accounts that were shared to be added by
-         *     a limited user.
-         */
-
-        // fails if the account already exists
-        long identityToken = clearCallingIdentity();
-        try {
-            UserAccounts accounts = getUserAccounts(userId);
-            return addAccountInternal(accounts, account, password, extras, callingUid);
-        } finally {
-            restoreCallingIdentity(identityToken);
-        }
+        return addAccountExplicitlyWithVisibility(account, password, extras, null);
     }
 
     @Override
@@ -1227,6 +1453,8 @@
                     // TODO: Anything to do if if succedded?
                     // TODO: If it failed: Show error notification? Should we remove the shadow
                     // account to avoid retries?
+                    // TODO: what we do with the visibility?
+
                     super.onResult(result);
                 }
 
@@ -1244,7 +1472,7 @@
     }
 
     private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
-            Bundle extras, int callingUid) {
+            Bundle extras, int callingUid, Map<Integer, Integer> uidToVisibility) {
         Bundle.setDefusable(extras, true);
         if (account == null) {
             return false;
@@ -1284,10 +1512,17 @@
                         }
                     }
                 }
+
+                if (uidToVisibility != null) {
+                    for (Entry<Integer, Integer> entry : uidToVisibility.entrySet()) {
+                        setAccountVisibility(account, entry.getKey() /* uid */,
+                                entry.getValue() /* visibility */, false /* notify */, accounts);
+                    }
+                }
                 accounts.accountsDb.setTransactionSuccessful();
 
-                logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
-                        accountId, accounts, callingUid);
+                logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS, accountId,
+                        accounts, callingUid);
 
                 insertAccountIntoCacheLocked(accounts, account);
             } finally {
@@ -1298,8 +1533,10 @@
             addAccountToLinkedRestrictedUsers(account, accounts.userId);
         }
 
+        sendNotification(account, accounts);
         // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
         sendAccountsChangedBroadcast(accounts.userId);
+
         return true;
     }
 
@@ -1476,6 +1713,10 @@
         synchronized (accounts.cacheLock) {
             accounts.accountsDb.beginTransaction();
             Account renamedAccount = new Account(newName, accountToRename.type);
+            if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) {
+                Log.e(TAG, "renameAccount failed - account with new name already exists");
+                return null;
+            }
             try {
                 final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
                 if (accountId >= 0) {
@@ -1487,6 +1728,9 @@
                         Log.e(TAG, "renameAccount failed");
                         return null;
                     }
+                } else {
+                    Log.e(TAG, "renameAccount failed - old account does not exist");
+                    return null;
                 }
             } finally {
                 accounts.accountsDb.endTransaction();
@@ -1529,6 +1773,9 @@
                     }
                 }
             }
+
+            // Notify authenticator.
+            sendNotification(resultAccount, accounts);
             sendAccountsChangedBroadcast(accounts.userId);
         }
         return resultAccount;
@@ -1727,6 +1974,24 @@
                     + " is still locked. CE data will be removed later");
         }
         synchronized (accounts.cacheLock) {
+            LinkedHashSet<String> interestedPackages =
+                    accounts.mApplicationAccountRequestMappings.get(account.type);
+            if (interestedPackages == null) {
+                interestedPackages = new LinkedHashSet<>();
+            }
+            int[] visibilityForInterestedPackages = new int[interestedPackages.size()];
+            int index = 0;
+            for (String packageName : interestedPackages) {
+                int visibility = AccountManager.VISIBILITY_NOT_VISIBLE;
+                try {
+                    final int uid = mPackageManager.getPackageUidAsUser(packageName,
+                            UserHandle.getUserId(callingUid));
+                    visibility = resolveAccountVisibility(account, uid, packageName, accounts);
+                } catch (NameNotFoundException e) {
+                    // ignore
+                }
+                visibilityForInterestedPackages[index++] = visibility;
+            }
             accounts.accountsDb.beginTransaction();
             // Set to a dummy value, this will only be used if the database
             // transaction succeeds.
@@ -1750,6 +2015,17 @@
             }
             if (isChanged) {
                 removeAccountFromCacheLocked(accounts, account);
+                index = 0;
+                for (String packageName : interestedPackages) {
+                    if ((visibilityForInterestedPackages[index]
+                            != AccountManager.VISIBILITY_NOT_VISIBLE)
+                            && (visibilityForInterestedPackages[index]
+                                    != AccountManager.VISIBILITY_UNDEFINED)) {
+                        sendNotification(packageName, account, accounts.userId);
+                    }
+                    ++index;
+                }
+
                 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
                 sendAccountsChangedBroadcast(accounts.userId);
                 String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
@@ -2016,18 +2292,13 @@
                 accounts.accountsDb.endTransaction();
                 if (isChanged) {
                     // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
+                    sendNotification(account, accounts);
                     sendAccountsChangedBroadcast(accounts.userId);
                 }
             }
         }
     }
 
-    private void sendAccountsChangedBroadcast(int userId) {
-        Log.i(TAG, "the accounts changed, sending broadcast of "
-                + ACCOUNTS_CHANGED_INTENT.getAction());
-        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
-    }
-
     @Override
     public void clearPassword(Account account) {
         final int callingUid = Binder.getCallingUid();
@@ -2406,8 +2677,10 @@
                             checkKeyIntent(
                                     Binder.getCallingUid(),
                                     intent);
-                            doNotification(mAccounts,
-                                    account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
+                            doNotification(
+                                    mAccounts,
+                                    account,
+                                    result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
                                     intent, "android", accounts.userId);
                         }
                     }
@@ -3292,7 +3565,8 @@
         if (response == null) throw new IllegalArgumentException("response is null");
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
+        if (!isAccountManagedByCaller(accountType, callingUid, userId)
+                && !isSystemUid(callingUid)) {
             String msg = String.format(
                     "uid %s cannot edit authenticator properites for account type: %s",
                     callingUid,
@@ -3335,23 +3609,50 @@
         Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
 
         try {
-            final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+            int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
             return hasAccountAccess(account, packageName, uid);
         } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found " + e.getMessage());
             return false;
         }
     }
 
+    // Returns package with oldest target SDK for given UID.
+    private String getPackageNameForUid(int uid) {
+        String[] packageNames = mPackageManager.getPackagesForUid(uid);
+        if (ArrayUtils.isEmpty(packageNames)) {
+            return null;
+        }
+        // For app op checks related to permissions all packages in the UID
+        // have the same app op state, so doesn't matter which one we pick.
+        // Update: due to visibility changes we want to use package with oldest target SDK,
+
+        String packageName = packageNames[0];
+        int oldestVersion = Integer.MAX_VALUE;
+        for (String name : packageNames) {
+            try {
+                ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(name, 0);
+                if (applicationInfo != null) {
+                    int version = applicationInfo.targetSdkVersion;
+                    if (version < oldestVersion) {
+                        oldestVersion = version;
+                        packageName = name;
+                    }
+                }
+            } catch (NameNotFoundException e) {
+                // skip
+            }
+        }
+        return packageName;
+    }
+
     private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
             int uid) {
         if (packageName == null) {
-            String[] packageNames = mPackageManager.getPackagesForUid(uid);
-            if (ArrayUtils.isEmpty(packageNames)) {
+            packageName = getPackageNameForUid(uid);
+            if (packageName == null) {
                 return false;
             }
-            // For app op checks related to permissions all packages in the UID
-            // have the same app op state, so doesn't matter which one we pick.
-            packageName = packageNames[0];
         }
 
         // Use null token which means any token. Having a token means the package
@@ -3360,27 +3661,12 @@
             return true;
         }
         // In addition to the permissions required to get an auth token we also allow
-        // the account to be accessed by holders of the get accounts permissions.
-        return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
-                || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
-    }
+        // the account to be accessed by apps for which user or authenticator granted visibility.
 
-    private boolean checkUidPermission(String permission, int uid, String opPackageName) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            IPackageManager pm = ActivityThread.getPackageManager();
-            if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
-                return false;
-            }
-            final int opCode = AppOpsManager.permissionToOpCode(permission);
-            return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
-                    opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
-        } catch (RemoteException e) {
-            /* ignore - local call */
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        return false;
+        int visibility = resolveAccountVisibility(account, uid, packageName,
+                getUserAccounts(UserHandle.getUserId(uid)));
+        return (visibility == AccountManager.VISIBILITY_VISIBLE
+                || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
     }
 
     @Override
@@ -3476,21 +3762,28 @@
         private volatile ArrayList<Account> mAccountsWithFeatures = null;
         private volatile int mCurrentAccount = 0;
         private final int mCallingUid;
+        private final String mPackageName;
 
-        public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
-                IAccountManagerResponse response, String type, String[] features, int callingUid) {
+        public GetAccountsByTypeAndFeatureSession(
+                UserAccounts accounts,
+                IAccountManagerResponse response,
+                String type,
+                String[] features,
+                int callingUid,
+                String packageName) {
             super(accounts, response, type, false /* expectActivityLaunch */,
                     true /* stripAuthTokenFromResult */, null /* accountName */,
                     false /* authDetailsRequired */);
             mCallingUid = callingUid;
             mFeatures = features;
+            mPackageName = packageName;
         }
 
         @Override
         public void run() throws RemoteException {
             synchronized (mAccounts.cacheLock) {
                 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
-                        null);
+                        mPackageName, false /* include managed not visible*/);
             }
             // check whether each account matches the requested features
             mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
@@ -3563,7 +3856,6 @@
             }
         }
 
-
         @Override
         protected String toDebugString(long now) {
             return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
@@ -3589,8 +3881,9 @@
             return getAccountsInternal(
                     accounts,
                     callingUid,
-                    null,  // packageName
-                    visibleAccountTypes);
+                    opPackageName,
+                    visibleAccountTypes,
+                    false /* includeUserManagedNotVisible */);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -3632,7 +3925,9 @@
             if (userAccounts == null) continue;
             synchronized (userAccounts.cacheLock) {
                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
-                        Binder.getCallingUid(), null);
+                        Binder.getCallingUid(),
+                        null,
+                        false /* incl managed not visible*/);
                 for (int a = 0; a < accounts.length; a++) {
                     runningAccounts.add(new AccountAndUser(accounts[a], userId));
                 }
@@ -3646,7 +3941,9 @@
     @Override
     @NonNull
     public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
-        return getAccountsAsUser(type, userId, null, -1, opPackageName);
+        // Need calling package
+        return getAccountsAsUser(type, userId, null /* callingPackage */, -1, opPackageName,
+               false /* includeUserManagedNotVisible */);
     }
 
     @NonNull
@@ -3655,11 +3952,12 @@
             int userId,
             String callingPackage,
             int packageUid,
-            String opPackageName) {
+            String opPackageName,
+            boolean includeUserManagedNotVisible) {
         int callingUid = Binder.getCallingUid();
         // Only allow the system process to read accounts of other users
         if (userId != UserHandle.getCallingUserId()
-                && callingUid != Process.myUid()
+                && callingUid != Process.SYSTEM_UID
                 && mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -3672,9 +3970,15 @@
                     + ", caller's uid " + Binder.getCallingUid()
                     + ", pid " + Binder.getCallingPid());
         }
-        // If the original calling app was using the framework account chooser activity, we'll
-        // be passed in the original caller's uid here, which is what should be used for filtering.
-        if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
+
+        // If the original calling app was using account choosing activity
+        // provided by the framework or authenticator we'll passing in
+        // the original caller's uid here, which is what should be used for filtering.
+        List<String> managedTypes =
+                getTypesManagedByCaller(callingUid, UserHandle.getUserId(callingUid));
+        if (packageUid != -1 &&
+                ((UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
+                || (type != null && managedTypes.contains(type))))) {
             callingUid = packageUid;
             opPackageName = callingPackage;
         }
@@ -3682,7 +3986,7 @@
                 opPackageName);
         if (visibleAccountTypes.isEmpty()
                 || (type != null && !visibleAccountTypes.contains(type))) {
-            return new Account[0];
+            return new Account[]{};
         } else if (visibleAccountTypes.contains(type)) {
             // Prune the list down to just the requested type.
             visibleAccountTypes = new ArrayList<>();
@@ -3697,7 +4001,8 @@
                     accounts,
                     callingUid,
                     callingPackage,
-                    visibleAccountTypes);
+                    visibleAccountTypes,
+                    includeUserManagedNotVisible);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -3708,12 +4013,14 @@
             UserAccounts userAccounts,
             int callingUid,
             String callingPackage,
-            List<String> visibleAccountTypes) {
+            List<String> visibleAccountTypes,
+            boolean includeUserManagedNotVisible) {
         synchronized (userAccounts.cacheLock) {
             ArrayList<Account> visibleAccounts = new ArrayList<>();
             for (String visibleType : visibleAccountTypes) {
                 Account[] accountsForType = getAccountsFromCacheLocked(
-                        userAccounts, visibleType, callingUid, callingPackage);
+                        userAccounts, visibleType, callingUid, callingPackage,
+                        includeUserManagedNotVisible);
                 if (accountsForType != null) {
                     visibleAccounts.addAll(Arrays.asList(accountsForType));
                 }
@@ -3804,29 +4111,34 @@
     @NonNull
     public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
         int callingUid = Binder.getCallingUid();
-        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
+        if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)) {
             throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
                     + callingUid + " with uid=" + uid);
         }
         return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
-                opPackageName);
+                opPackageName, true /* includeUserManagedNotVisible */);
     }
 
     @Override
     @NonNull
     public Account[] getAccountsByTypeForPackage(String type, String packageName,
             String opPackageName) {
+        int callingUid =  Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
         int packageUid = -1;
         try {
-            packageUid = AppGlobals.getPackageManager().getPackageUid(
-                    packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
-                    UserHandle.getCallingUserId());
-        } catch (RemoteException re) {
+            packageUid = mPackageManager.getPackageUidAsUser(packageName, userId);
+        } catch (NameNotFoundException re) {
             Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
             return new Account[0];
         }
-        return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
-                packageUid, opPackageName);
+        if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
+                && !isAccountManagedByCaller(type, callingUid, userId)) {
+            return new Account[0];
+        }
+
+        return getAccountsAsUser(type, userId,
+                packageName, packageUid, opPackageName, true /* includeUserManagedNotVisible */);
     }
 
     @Override
@@ -3866,7 +4178,8 @@
             if (features == null || features.length == 0) {
                 Account[] accounts;
                 synchronized (userAccounts.cacheLock) {
-                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
+                    accounts = getAccountsFromCacheLocked(
+                            userAccounts, type, callingUid, opPackageName, false);
                 }
                 Bundle result = new Bundle();
                 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
@@ -3878,7 +4191,8 @@
                     response,
                     type,
                     features,
-                    callingUid).bind();
+                    callingUid,
+                    opPackageName).bind();
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -3897,6 +4211,7 @@
                 if (Objects.equals(account.getAccessId(), token)) {
                     // An app just accessed the account. At this point it knows about
                     // it and there is not need to hide this account from the app.
+                    // Do we need to update account visibility here?
                     if (!hasAccountAccess(account, null, uid)) {
                         updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
                                 uid, true);
@@ -4423,7 +4738,7 @@
                 userAccounts.accountsDb.dumpDeAccountsTable(fout);
             } else {
                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
-                        Process.myUid(), null);
+                        Process.SYSTEM_UID, null, false);
                 fout.println("Accounts: " + accounts.length);
                 for (Account account : accounts) {
                     fout.println("  " + account);
@@ -4516,6 +4831,24 @@
         }
     }
 
+    private boolean isPermittedForPackage(String packageName, int userId, String... permissions) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            IPackageManager pm = ActivityThread.getPackageManager();
+            for (String perm : permissions) {
+                if (pm.checkPermission(perm, packageName, userId)
+                        == PackageManager.PERMISSION_GRANTED) {
+                    return true;
+                }
+            }
+        } catch (RemoteException e) {
+            /* ignore - local call */
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return false;
+    }
+
     private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
         for (String perm : permissions) {
             if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
@@ -4550,6 +4883,7 @@
             userPackageManager = mContext.createPackageContextAsUser(
                     "android", 0, new UserHandle(callingUserId)).getPackageManager();
         } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found " + e.getMessage());
             return false;
         }
 
@@ -4563,6 +4897,7 @@
                     return true;
                 }
             } catch (PackageManager.NameNotFoundException e) {
+                Log.d(TAG, "Package not found " + e.getMessage());
                 return false;
             }
         }
@@ -4617,6 +4952,53 @@
         }
     }
 
+    // Method checks visibility for applications targeing API level below {@link
+    // android.os.Build.VERSION_CODES#O},
+    // returns true if the the app has GET_ACCOUNTS or GET_ACCOUNTS_PRIVELEGED permission.
+    private boolean checkGetAccountsPermission(String packageName, int userId) {
+        return isPermittedForPackage(packageName, userId, Manifest.permission.GET_ACCOUNTS,
+                Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
+    }
+
+    private boolean checkReadContactsPermission(String packageName, int userId) {
+        return isPermittedForPackage(packageName, userId, Manifest.permission.READ_CONTACTS);
+    }
+
+    /**
+     * Method checks package uid and signature with Authenticator which manages accountType.
+     *
+     * @return SIGNATURE_CHECK_UID_MATCH for uid match, SIGNATURE_CHECK_MATCH for signature match,
+     *         SIGNATURE_CHECK_MISMATCH otherwise.
+     */
+    private int checkPackageSignature(String accountType, int callingUid, int userId) {
+        if (accountType == null) {
+            return SIGNATURE_CHECK_MISMATCH;
+        }
+
+        long identityToken = Binder.clearCallingIdentity();
+        Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
+        try {
+            serviceInfos = mAuthenticatorCache.getAllServices(userId);
+        } finally {
+            Binder.restoreCallingIdentity(identityToken);
+        }
+        // Check for signature match with Authenticator.
+        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo
+                : serviceInfos) {
+            if (accountType.equals(serviceInfo.type.type)) {
+                if (serviceInfo.uid == callingUid) {
+                    return SIGNATURE_CHECK_UID_MATCH;
+                }
+                final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
+                if (sigChk == PackageManager.SIGNATURE_MATCH) {
+                    return SIGNATURE_CHECK_MATCH;
+                }
+            }
+        }
+        return SIGNATURE_CHECK_MISMATCH;
+    }
+
+    // returns true for applications with the same signature as authenticator.
     private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
         if (accountType == null) {
             return false;
@@ -4627,10 +5009,7 @@
 
     private List<String> getTypesVisibleToCaller(int callingUid, int userId,
             String opPackageName) {
-        boolean isPermitted =
-                isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
-                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
-        return getTypesForCaller(callingUid, userId, isPermitted);
+        return getTypesForCaller(callingUid, userId, true /* isOtherwisePermitted*/);
     }
 
     private List<String> getTypesManagedByCaller(int callingUid, int userId) {
@@ -4649,8 +5028,8 @@
         }
         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
                 serviceInfos) {
-            final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
-            if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
+            if (isOtherwisePermitted || (mPackageManager.checkSignatures(serviceInfo.uid,
+                    callingUid) == PackageManager.SIGNATURE_MATCH)) {
                 managedAccountTypes.add(serviceInfo.type.type);
             }
         }
@@ -4732,7 +5111,7 @@
                                     != 0) {
                         return true;
                     }
-                } catch (PackageManager.NameNotFoundException e) {
+                } catch (NameNotFoundException e) {
                     Log.w(TAG, String.format("Could not find package [%s]", name), e);
                 }
             }
@@ -4923,15 +5302,41 @@
         return newAccountsForType[oldLength];
     }
 
-    private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
-            int callingUid, String callingPackage) {
+    private Account[] filterAccounts(UserAccounts accounts, Account[] unfiltered, int callingUid,
+            String callingPackage, boolean includeManagedNotVisible) {
+        // filter based on visibility.
+        Map<Account, Integer> firstPass = new HashMap<>();
+        for (Account account : unfiltered) {
+            int visibility =
+                    resolveAccountVisibility(account, callingUid, callingPackage, accounts);
+            if ((visibility == AccountManager.VISIBILITY_VISIBLE
+                    || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE)
+                    || (includeManagedNotVisible
+                            && (visibility
+                                    == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE))) {
+                firstPass.put(account, visibility);
+            }
+        }
+        Map<Account, Integer> secondPass =
+                filterSharedAccounts(accounts, firstPass, callingUid, callingPackage);
+
+        Account[] filtered = new Account[secondPass.size()];
+        filtered = secondPass.keySet().toArray(filtered);
+        return filtered;
+    }
+
+    private Map<Account, Integer> filterSharedAccounts(UserAccounts userAccounts,
+            Map<Account, Integer> unfiltered, int callingUid, String callingPackage) {
+        // first part is to filter shared accounts.
+        // unfiltered type check is not necessary.
         if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
-                || callingUid == Process.myUid()) {
+                || callingUid == Process.SYSTEM_UID) {
             return unfiltered;
         }
         UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
         if (user != null && user.isRestricted()) {
-            String[] packages = mPackageManager.getPackagesForUid(callingUid);
+            String[] packages =
+                    mPackageManager.getPackagesForUid(callingUid);
             // If any of the packages is a visible listed package, return the full set,
             // otherwise return non-shared accounts only.
             // This might be a temporary way to specify a visible list
@@ -4942,9 +5347,10 @@
                     return unfiltered;
                 }
             }
-            ArrayList<Account> allowed = new ArrayList<>();
             Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
-            if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
+            if (ArrayUtils.isEmpty(sharedAccounts)) {
+                return unfiltered;
+            }
             String requiredAccountType = "";
             try {
                 // If there's an explicit callingPackage specified, check if that package
@@ -4964,11 +5370,14 @@
                         }
                     }
                 }
-            } catch (NameNotFoundException nnfe) {
+            } catch (NameNotFoundException e) {
+                Log.d(TAG, "Package not found " + e.getMessage());
             }
-            for (Account account : unfiltered) {
+            Map<Account, Integer> filtered = new HashMap<>();
+            for (Map.Entry<Account, Integer> entry : unfiltered.entrySet()) {
+                Account account = entry.getKey();
                 if (account.type.equals(requiredAccountType)) {
-                    allowed.add(account);
+                    filtered.put(account, entry.getValue());
                 } else {
                     boolean found = false;
                     for (Account shared : sharedAccounts) {
@@ -4978,12 +5387,10 @@
                         }
                     }
                     if (!found) {
-                        allowed.add(account);
+                        filtered.put(account, entry.getValue());
                     }
                 }
             }
-            Account[] filtered = new Account[allowed.size()];
-            allowed.toArray(filtered);
             return filtered;
         } else {
             return unfiltered;
@@ -4995,14 +5402,14 @@
      * that the package is not allowed to access.
      */
     protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
-            int callingUid, String callingPackage) {
+            int callingUid, String callingPackage, boolean includeManagedNotVisible) {
         if (accountType != null) {
             final Account[] accounts = userAccounts.accountCache.get(accountType);
             if (accounts == null) {
                 return EMPTY_ACCOUNT_ARRAY;
             } else {
-                return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
-                        callingUid, callingPackage);
+                return filterAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
+                        callingUid, callingPackage, includeManagedNotVisible);
             }
         } else {
             int totalLength = 0;
@@ -5019,7 +5426,8 @@
                         accountsOfType.length);
                 totalLength += accountsOfType.length;
             }
-            return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
+            return filterAccounts(userAccounts, accounts, callingUid, callingPackage,
+                    includeManagedNotVisible);
         }
     }
 
@@ -5247,19 +5655,21 @@
             if (userId == 0) {
                 // Migrate old file, if it exists, to the new location.
                 // Make sure the new file doesn't already exist. A dummy file could have been
-                // accidentally created in the old location, causing the new one to become corrupted
-                // as well.
+                // accidentally created in the old location,
+                // causing the new one to become corrupted as well.
                 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
                 if (oldFile.exists() && !databaseFile.exists()) {
                     // Check for use directory; create if it doesn't exist, else renameTo will fail
                     File userDir = Environment.getUserSystemDirectory(userId);
                     if (!userDir.exists()) {
                         if (!userDir.mkdirs()) {
-                            throw new IllegalStateException("User dir cannot be created: " + userDir);
+                            throw new IllegalStateException(
+                                    "User dir cannot be created: " + userDir);
                         }
                     }
                     if (!oldFile.renameTo(databaseFile)) {
-                        throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
+                        throw new IllegalStateException(
+                                "User dir cannot be migrated: " + databaseFile);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 5ca74711..85e4b5f 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -355,7 +355,7 @@
     boolean deleteAuthtokensByAccountIdAndType(long accountId, String authtokenType) {
         SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
         return db.delete(CE_TABLE_AUTHTOKENS,
-                AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+                AUTHTOKENS_ACCOUNTS_ID + "=?" + " AND " + AUTHTOKENS_TYPE + "=?",
                 new String[]{String.valueOf(accountId), authtokenType}) > 0;
     }
 
@@ -946,12 +946,13 @@
     /**
      * Returns a map from uid to visibility value.
      */
-    Map<Integer, Integer> findAccountVisibilityForAccountId(long accountId) {
+    Map<Integer, Integer> findAllVisibilityValuesForAccount(Account account) {
         SQLiteDatabase db = mDeDatabase.getReadableDatabase();
         Map<Integer, Integer> result = new HashMap<>();
-        final Cursor cursor = db.query(TABLE_VISIBILITY,
-                new String[] {VISIBILITY_UID, VISIBILITY_VALUE}, VISIBILITY_ACCOUNTS_ID + "=? ",
-                new String[] {String.valueOf(accountId)}, null, null, null);
+        final Cursor cursor =
+                db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_UID, VISIBILITY_VALUE},
+                        SELECTION_ACCOUNTS_ID_BY_ACCOUNT,
+                        new String[] {account.name, account.type}, null, null, null);
         try {
             while (cursor.moveToNext()) {
                 result.put(cursor.getInt(0), cursor.getInt(1));
@@ -1306,4 +1307,4 @@
         return new AccountsDb(deDatabaseHelper, context, preNDatabaseFile);
     }
 
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7661127..5655dc5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -305,6 +305,7 @@
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
+            int id, Notification notification,
             int callingPid, int callingUid, String callingPackage, final int userId)
             throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
@@ -324,7 +325,6 @@
             callerFg = true;
         }
 
-
         ServiceLookupResult res =
             retrieveServiceLocked(service, resolvedType, callingPackage,
                     callingPid, callingUid, userId, true, callerFg, false);
@@ -343,19 +343,27 @@
             return null;
         }
 
-        if (!r.startRequested) {
+        // Non-null notification means this is a start directly into the foreground
+        if (!r.startRequested && notification == null) {
             final long token = Binder.clearCallingIdentity();
             try {
-                // Before going further -- if this app is not allowed to run in the
+                // Before going further -- if this app is not allowed to start services in the
                 // background, then at this point we aren't going to let it period.
-                final int allowed = mAm.checkAllowBackgroundLocked(
-                        r.appInfo.uid, r.packageName, callingPid, false);
+                final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
+                        r.appInfo.targetSdkVersion, callingPid, false, false);
                 if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                     Slog.w(TAG, "Background start not allowed: service "
                             + service + " to " + r.name.flattenToShortString()
                             + " from pid=" + callingPid + " uid=" + callingUid
                             + " pkg=" + callingPackage);
-                    return null;
+                    if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
+                        // In this case we are silently disabling the app, to disrupt as
+                        // little as possible existing apps.
+                        return null;
+                    }
+                    // This app knows it is in the new model where this operation is not
+                    // allowed, so tell it what has happened.
+                    return new ComponentName("?", "app is in background");
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -450,7 +458,11 @@
             }
         }
 
-        return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
+        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
+        if (notification != null) {
+            setServiceForegroundInnerLocked(r, callingUid, notification, 0);
+        }
+        return cmp;
     }
 
     private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
@@ -595,9 +607,9 @@
             for (int i=services.mServicesByName.size()-1; i>=0; i--) {
                 ServiceRecord service = services.mServicesByName.valueAt(i);
                 if (service.appInfo.uid == uid && service.startRequested) {
-                    if (service.appInfo.isEphemeralApp() ||
-                            mAm.mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
-                            uid, service.packageName) != AppOpsManager.MODE_ALLOWED) {
+                    if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName,
+                            service.appInfo.targetSdkVersion, -1, false, false)
+                            != ActivityManager.APP_START_MODE_NORMAL) {
                         if (stopping == null) {
                             stopping = new ArrayList<>();
                             stopping.add(service);
@@ -696,50 +708,55 @@
         try {
             ServiceRecord r = findServiceLocked(className, token, userId);
             if (r != null) {
-                if (id != 0) {
-                    if (notification == null) {
-                        throw new IllegalArgumentException("null notification");
-                    }
-                    if (r.foregroundId != id) {
-                        cancelForegroudNotificationLocked(r);
-                        r.foregroundId = id;
-                    }
-                    notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
-                    r.foregroundNoti = notification;
-                    r.isForeground = true;
-                    r.postNotification();
-                    if (r.app != null) {
-                        updateServiceForegroundLocked(r.app, true);
-                    }
-                    getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
-                    mAm.notifyPackageUse(r.serviceInfo.packageName,
-                                         PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
-                } else {
-                    if (r.isForeground) {
-                        r.isForeground = false;
-                        if (r.app != null) {
-                            mAm.updateLruProcessLocked(r.app, false, null);
-                            updateServiceForegroundLocked(r.app, true);
-                        }
-                    }
-                    if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
-                        cancelForegroudNotificationLocked(r);
-                        r.foregroundId = 0;
-                        r.foregroundNoti = null;
-                    } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
-                        r.stripForegroundServiceFlagFromNotification();
-                        if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
-                            r.foregroundId = 0;
-                            r.foregroundNoti = null;
-                        }
-                    }
-                }
+                setServiceForegroundInnerLocked(r, id, notification, flags);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
     }
 
+    private void setServiceForegroundInnerLocked(ServiceRecord r, int id,
+            Notification notification, int flags) {
+        if (id != 0) {
+            if (notification == null) {
+                throw new IllegalArgumentException("null notification");
+            }
+            if (r.foregroundId != id) {
+                cancelForegroudNotificationLocked(r);
+                r.foregroundId = id;
+            }
+            notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+            r.foregroundNoti = notification;
+            r.isForeground = true;
+            r.postNotification();
+            if (r.app != null) {
+                updateServiceForegroundLocked(r.app, true);
+            }
+            getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
+            mAm.notifyPackageUse(r.serviceInfo.packageName,
+                                 PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
+        } else {
+            if (r.isForeground) {
+                r.isForeground = false;
+                if (r.app != null) {
+                    mAm.updateLruProcessLocked(r.app, false, null);
+                    updateServiceForegroundLocked(r.app, true);
+                }
+            }
+            if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
+                cancelForegroudNotificationLocked(r);
+                r.foregroundId = 0;
+                r.foregroundNoti = null;
+            } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
+                r.stripForegroundServiceFlagFromNotification();
+                if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
+                    r.foregroundId = 0;
+                    r.foregroundNoti = null;
+                }
+            }
+        }
+    }
+
     private void cancelForegroudNotificationLocked(ServiceRecord r) {
         if (r.foregroundId != 0) {
             // First check to see if this app has any other active foreground services
@@ -1444,6 +1461,8 @@
             // If service is not currently running, can't yet bind.
             return false;
         }
+        if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
+                + " rebind=" + rebind);
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
                 bumpServiceExecutingLocked(r, execInFg, "bind");
@@ -1907,6 +1926,7 @@
                     mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
                             si.getUriPermissionsLocked());
                 }
+                // TODO b/34123112; Insert ephemeral grant here
                 bumpServiceExecutingLocked(r, execInFg, "start");
                 if (!oomAdjusted) {
                     oomAdjusted = true;
@@ -2014,6 +2034,7 @@
                         bumpServiceExecutingLocked(r, false, "bring down unbind");
                         mAm.updateOomAdjLocked(r.app);
                         ibr.hasBound = false;
+                        ibr.requested = false;
                         r.app.thread.scheduleUnbindService(r,
                                 ibr.intent.getIntent());
                     } catch (Exception e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
new file mode 100644
index 0000000..3c90f93
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+
+/**
+ * Settings constants that can modify the activity manager's behavior.
+ */
+final class ActivityManagerConstants extends ContentObserver {
+    // Key names stored in the settings value.
+    private static final String KEY_ENFORCE_BG_CHECK = "enforce_bg_check";
+    private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
+
+    private static final boolean DEFAULT_ENFORCE_BG_CHECK = SystemProperties.getBoolean(
+            "debug.bgcheck", false);
+    private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
+
+    // Enforce background check on apps targeting O?
+    public boolean ENFORCE_BG_CHECK = DEFAULT_ENFORCE_BG_CHECK;
+
+    // Maximum number of cached processes we will allow.
+    public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
+
+    private final ActivityManagerService mService;
+    private ContentResolver mResolver;
+    private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+    private int mOverrideMaxCachedProcesses = -1;
+
+    // The maximum number of cached processes we will keep around before killing them.
+    // NOTE: this constant is *only* a control to not let us go too crazy with
+    // keeping around processes on devices with large amounts of RAM.  For devices that
+    // are tighter on RAM, the out of memory killer is responsible for killing background
+    // processes as RAM is needed, and we should *never* be relying on this limit to
+    // kill them.  Also note that this limit only applies to cached background processes;
+    // we have no limit on the number of service, visible, foreground, or other such
+    // processes and the number of those processes does not count against the cached
+    // process limit.
+    public int CUR_MAX_CACHED_PROCESSES;
+
+    // The maximum number of empty app processes we will let sit around.
+    public int CUR_MAX_EMPTY_PROCESSES;
+
+    // The number of empty apps at which we don't consider it necessary to do
+    // memory trimming.
+    public int CUR_TRIM_EMPTY_PROCESSES;
+
+    // The number of cached at which we don't consider it necessary to do
+    // memory trimming.
+    public int CUR_TRIM_CACHED_PROCESSES;
+
+    public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
+        super(handler);
+        mService = service;
+        updateMaxCachedProcesses();
+    }
+
+    public void start(ContentResolver resolver) {
+        mResolver = resolver;
+        mResolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.ACTIVITY_MANAGER_CONSTANTS), false, this);
+        updateConstants();
+    }
+
+    public void setOverrideMaxCachedProcesses(int value) {
+        mOverrideMaxCachedProcesses = value;
+        updateMaxCachedProcesses();
+    }
+
+    public int getOverrideMaxCachedProcesses() {
+        return mOverrideMaxCachedProcesses;
+    }
+
+    public static int computeEmptyProcessLimit(int totalProcessLimit) {
+        return totalProcessLimit/2;
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        updateConstants();
+    }
+
+    private void updateConstants() {
+        synchronized (mService) {
+            try {
+                mParser.setString(Settings.Global.getString(mResolver,
+                        Settings.Global.ACTIVITY_MANAGER_CONSTANTS));
+            } catch (IllegalArgumentException e) {
+                // Failed to parse the settings string, log this and move on
+                // with defaults.
+                Slog.e("ActivityManagerConstants", "Bad activity manager config settings", e);
+            }
+
+            ENFORCE_BG_CHECK = mParser.getBoolean(KEY_ENFORCE_BG_CHECK, DEFAULT_ENFORCE_BG_CHECK);
+            MAX_CACHED_PROCESSES = mParser.getInt(KEY_MAX_CACHED_PROCESSES,
+                    DEFAULT_MAX_CACHED_PROCESSES);
+            updateMaxCachedProcesses();
+        }
+    }
+
+    private void updateMaxCachedProcesses() {
+        CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
+                ? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses;
+        CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES);
+
+        // Note the trim levels do NOT depend on the override process limit, we want
+        // to consider the same level the point where we do trimming regardless of any
+        // additional enforced limit.
+        final int rawMaxEmptyProcesses = computeEmptyProcessLimit(MAX_CACHED_PROCESSES);
+        CUR_TRIM_EMPTY_PROCESSES = rawMaxEmptyProcesses/2;
+        CUR_TRIM_CACHED_PROCESSES = (MAX_CACHED_PROCESSES-rawMaxEmptyProcesses)/3;
+    }
+
+    void dump(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
+                + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":");
+
+        pw.print("  "); pw.print(KEY_ENFORCE_BG_CHECK); pw.print("=");
+        pw.println(ENFORCE_BG_CHECK);
+
+        pw.print("  "); pw.print(KEY_MAX_CACHED_PROCESSES); pw.print("=");
+        pw.println(MAX_CACHED_PROCESSES);
+
+        pw.println();
+        if (mOverrideMaxCachedProcesses >= 0) {
+            pw.print("  mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);
+        }
+        pw.print("  CUR_MAX_CACHED_PROCESSES="); pw.println(CUR_MAX_CACHED_PROCESSES);
+        pw.print("  CUR_MAX_EMPTY_PROCESSES="); pw.println(CUR_MAX_EMPTY_PROCESSES);
+        pw.print("  CUR_TRIM_EMPTY_PROCESSES="); pw.println(CUR_TRIM_EMPTY_PROCESSES);
+        pw.print("  CUR_TRIM_CACHED_PROCESSES="); pw.println(CUR_TRIM_CACHED_PROCESSES);
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 43bb5ee..88e0d03 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -48,6 +48,7 @@
     static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_ANR = false;
     static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_BACKGROUND_CHECK = DEBUG_ALL || false;
     static final boolean DEBUG_BACKUP = DEBUG_ALL || false;
     static final boolean DEBUG_BROADCAST = DEBUG_ALL || false;
     static final boolean DEBUG_BROADCAST_BACKGROUND = DEBUG_BROADCAST || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7fd91cb..b88c436 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22,6 +22,7 @@
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
@@ -62,6 +63,7 @@
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
@@ -119,6 +121,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS;
+import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
@@ -151,7 +154,6 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityManager.TaskThumbnailInfo;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.PictureInPictureArguments;
 import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
@@ -182,6 +184,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.PictureInPictureArgs;
 import android.app.ProfilerInfo;
 import android.app.RemoteAction;
 import android.app.WaitResult;
@@ -229,7 +232,6 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.location.LocationManager;
@@ -310,7 +312,6 @@
 
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
-
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.AssistUtils;
@@ -340,7 +341,9 @@
 import com.android.server.IntentResolver;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
+import com.android.server.RescueParty;
 import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.Watchdog;
@@ -384,10 +387,10 @@
 import java.util.concurrent.atomic.AtomicLong;
 
 import dalvik.system.VMRuntime;
+
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 public class ActivityManagerService extends IActivityManager.Stub
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
 
@@ -758,6 +761,18 @@
     ProcessRecord mHeavyWeightProcess = null;
 
     /**
+     * Non-persistent app uid whitelist for background restrictions
+     */
+    int[] mBackgroundUidWhitelist = new int[] {
+            Process.BLUETOOTH_UID
+    };
+
+    /**
+     * Broadcast actions that will always be deliverable to unlaunched/background apps
+     */
+    final ArraySet<String> mBackgroundLaunchBroadcasts;
+
+    /**
      * All of the processes we currently have running organized by pid.
      * The keys are the pid running the application.
      *
@@ -1135,6 +1150,16 @@
     DeviceIdleController.LocalService mLocalDeviceIdleController;
 
     /**
+     * Set of app ids that are whitelisted for device idle and thus background check.
+     */
+    int[] mDeviceIdleWhitelist = new int[0];
+
+    /**
+     * Set of app ids that are temporarily allowed to escape bg check due to high-pri message
+     */
+    int[] mDeviceIdleTempWhitelist = new int[0];
+
+    /**
      * Information about and control over application operations
      */
     final AppOpsService mAppOpsService;
@@ -1485,9 +1510,6 @@
      */
     boolean mBooted = false;
 
-    int mProcessLimit = ProcessList.MAX_CACHED_APPS;
-    int mProcessLimitOverride = -1;
-
     WindowManagerService mWindowManager;
     final ActivityThread mSystemThread;
 
@@ -1609,6 +1631,8 @@
     final MainHandler mHandler;
     final UiHandler mUiHandler;
 
+    final ActivityManagerConstants mConstants;
+
     PackageManagerInternal mPackageManagerInt;
 
     // VoiceInteraction session ID that changes for each new request except when
@@ -2590,12 +2614,26 @@
         mPermissionReviewRequired = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_permissionReviewRequired);
 
+        mBackgroundLaunchBroadcasts = SystemConfig.getInstance().getAllowImplicitBroadcasts();
+        if (DEBUG_BACKGROUND_CHECK) {
+            Slog.d(TAG, "Enforcing O+ bg restrictions: " + mConstants.ENFORCE_BG_CHECK);
+            StringBuilder sb = new StringBuilder(200);
+            sb.append("  ");
+            for (String a : mBackgroundLaunchBroadcasts) {
+                sb.append(' '); sb.append(a);
+            }
+            Slog.d(TAG, "Background implicit broadcasts:");
+            Slog.d(TAG, sb.toString());
+        }
+
         mHandlerThread = new ServiceThread(TAG,
                 android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
         mHandlerThread.start();
         mHandler = new MainHandler(mHandlerThread.getLooper());
         mUiHandler = new UiHandler();
 
+        mConstants = new ActivityManagerConstants(this, mHandler);
+
         /* static; one-time init here */
         if (sKillHandler == null) {
             sKillThread = new ServiceThread(TAG + ":kill",
@@ -4190,7 +4228,7 @@
                             validateUid = mValidateUids.get(item.uid);
                             if (validateUid == null && change != UidRecord.CHANGE_GONE
                                     && change != UidRecord.CHANGE_GONE_IDLE) {
-                                validateUid = new UidRecord(item.uid);
+                                validateUid = new UidRecord(item.uid, false);
                                 mValidateUids.put(item.uid, validateUid);
                             }
                         }
@@ -4894,7 +4932,7 @@
     }
 
     @Override
-    public void crashApplication(int uid, int initialPid, String packageName,
+    public void crashApplication(int uid, int initialPid, String packageName, int userId,
             String message) {
         if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -4907,7 +4945,7 @@
         }
 
         synchronized(this) {
-            mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, message);
+            mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId, message);
         }
     }
 
@@ -5504,6 +5542,7 @@
 
                     final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
                             Uri.fromParts("package", packageName, null));
+                    intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                     intent.putExtra(Intent.EXTRA_UID, pkgUidF);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(pkgUidF));
                     broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
@@ -5524,11 +5563,9 @@
                     removeUriPermissionsForPackageLocked(packageName, userId, true);
                 }
 
-                // Remove all zen rules created by this package; revoke it's zen access.
+                // Reset notification settings.
                 INotificationManager inm = NotificationManager.getService();
-                inm.removeAutomaticZenRules(packageName);
-                inm.setNotificationPolicyAccessGranted(packageName, false);
-
+                inm.clearData(packageName, pkgUidF);
             } catch (RemoteException e) {
             }
         } finally {
@@ -6279,10 +6316,13 @@
         }
         UidRecord uidRec = mActiveUids.get(proc.uid);
         if (uidRec == null) {
-            uidRec = new UidRecord(proc.uid);
+            uidRec = new UidRecord(proc.uid, proc.persistent);
             // This is the first appearance of the uid, report it now!
             if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                     "Creating new process uid: " + uidRec);
+            if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0) {
+                uidRec.setWhitelist = uidRec.curWhitelist = true;
+            }
             mActiveUids.put(proc.uid, uidRec);
             noteUidProcessState(uidRec.uid, uidRec.curProcState);
             enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
@@ -7231,27 +7271,29 @@
 
     /**
      * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
-     *
-     * <p>{@code callerUid} must be allowed to request such whitelist by calling
-     * {@link #addTempPowerSaveWhitelistGrantorUid(int)}.
      */
     void tempWhitelistAppForPowerSave(int callerPid, int callerUid, int targetUid, long duration) {
         if (DEBUG_WHITELISTS) {
             Slog.d(TAG, "tempWhitelistAppForPowerSave(" + callerPid + ", " + callerUid + ", "
                     + targetUid + ", " + duration + ")");
         }
-        synchronized (mPidsSelfLocked) {
-            final ProcessRecord pr = mPidsSelfLocked.get(callerPid);
-            if (pr == null) {
-                Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid " + callerPid);
-                return;
-            }
-            if (!pr.whitelistManager) {
-                if (DEBUG_WHITELISTS) {
-                    Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid + ": pid "
-                            + callerPid + " is not allowed");
+
+        if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
+                != PackageManager.PERMISSION_GRANTED) {
+            synchronized (mPidsSelfLocked) {
+                final ProcessRecord pr = mPidsSelfLocked.get(callerPid);
+                if (pr == null) {
+                    Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid "
+                            + callerPid);
+                    return;
                 }
-                return;
+                if (!pr.whitelistManager) {
+                    if (DEBUG_WHITELISTS) {
+                        Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid
+                                + ": pid " + callerPid + " is not allowed");
+                    }
+                    return;
+                }
             }
         }
 
@@ -7420,8 +7462,7 @@
         enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
                 "setProcessLimit()");
         synchronized (this) {
-            mProcessLimit = max < 0 ? ProcessList.MAX_CACHED_APPS : max;
-            mProcessLimitOverride = max;
+            mConstants.setOverrideMaxCachedProcesses(max);
         }
         trimApplications();
     }
@@ -7429,7 +7470,7 @@
     @Override
     public int getProcessLimit() {
         synchronized (this) {
-            return mProcessLimitOverride;
+            return mConstants.getOverrideMaxCachedProcesses();
         }
     }
 
@@ -7556,45 +7597,30 @@
     }
 
     @Override
-    public void enterPictureInPictureMode(IBinder token) {
-        enterPictureInPictureMode(token, DEFAULT_DISPLAY, -1f /* aspectRatio */,
-                false /* checkAspectRatio */);
-    }
-
-    @Override
-    public void enterPictureInPictureModeWithAspectRatio(IBinder token, float aspectRatio) {
-        enterPictureInPictureMode(token, DEFAULT_DISPLAY, aspectRatio, true /* checkAspectRatio */);
-    }
-
-    @Override
-    public void enterPictureInPictureModeOnMoveToBackground(IBinder token,
-            boolean enterPictureInPictureOnMoveToBg) {
+    public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureArgs args) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "enterPictureInPictureModeOnMoveToBackground", token, -1f /* aspectRatio */,
-                        false /* checkAspectRatio */, false /* checkActivityVisibility */);
+                final ActivityRecord r = ensureValidPictureInPictureActivityArgsLocked(
+                        "enterPictureInPictureMode", token, args);
 
-                r.supportsPipOnMoveToBackground = enterPictureInPictureOnMoveToBg;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
+                // Activity supports picture-in-picture, now check that we can enter PiP at this
+                // point, if it is
+                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode")) {
+                    return false;
+                }
 
-    private void enterPictureInPictureMode(IBinder token, int displayId, float aspectRatio,
-            boolean checkAspectRatio) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "enterPictureInPictureMode", token, aspectRatio, checkAspectRatio,
-                        true /* checkActivityVisibility */);
                 final Runnable enterPipRunnable = () -> {
-                    r.pictureInPictureArgs.aspectRatio = aspectRatio;
-                    enterPictureInPictureModeLocked(r, displayId, r.pictureInPictureArgs,
-                            true /* moveHomeStackToFront */, "enterPictureInPictureMode");
+                    // Only update the saved args from the args that are set
+                    r.pictureInPictureArgs.copyOnlySet(args);
+                    final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+                    final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
+                    final Rect bounds = isValidPictureInPictureAspectRatio(aspectRatio)
+                            ? mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY, aspectRatio)
+                            : mWindowManager.getPictureInPictureDefaultBounds(DEFAULT_DISPLAY);
+                    mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode",
+                            bounds, true /* moveHomeStackToFront */);
+                    mStackSupervisor.getStack(PINNED_STACK_ID).setPictureInPictureActions(actions);
                 };
 
                 if (isKeyguardLocked()) {
@@ -7625,35 +7651,7 @@
                     // Enter picture in picture immediately otherwise
                     enterPipRunnable.run();
                 }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    void enterPictureInPictureModeLocked(ActivityRecord r, int displayId,
-            PictureInPictureArguments pipArgs, boolean moveHomeStackToFront, String reason) {
-        final Rect bounds = isValidPictureInPictureAspectRatio(pipArgs.aspectRatio)
-                ? mWindowManager.getPictureInPictureBounds(displayId, pipArgs.aspectRatio)
-                : mWindowManager.getPictureInPictureDefaultBounds(displayId);
-        mStackSupervisor.moveActivityToPinnedStackLocked(r, reason, bounds, moveHomeStackToFront);
-        mWindowManager.setPictureInPictureActions(pipArgs.userActions);
-    }
-
-    @Override
-    public void setPictureInPictureAspectRatio(IBinder token, float aspectRatio) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "setPictureInPictureAspectRatio", token, aspectRatio,
-                        true /* checkAspectRatio */, false /* checkActivityVisibility */);
-
-                r.pictureInPictureArgs.aspectRatio = aspectRatio;
-                if (r.getStack().getStackId() == PINNED_STACK_ID) {
-                    // If the activity is already in picture-in-picture, update the pinned stack now
-                    mWindowManager.setPictureInPictureAspectRatio(aspectRatio);
-                }
+                return true;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -7661,26 +7659,20 @@
     }
 
     @Override
-    public void setPictureInPictureActions(IBinder token, ParceledListSlice actionsList) {
+    public void setPictureInPictureArgs(IBinder token, final PictureInPictureArgs args) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "setPictureInPictureActions", token, -1 /* aspectRatio */,
-                        false /* checkAspectRatio */, false /* checkActivityVisibility */);
+                final ActivityRecord r = ensureValidPictureInPictureActivityArgsLocked(
+                        "setPictureInPictureArgs", token, args);
 
-                final List<RemoteAction> actions = actionsList.getList();
-                if (actions.size() > ActivityManager.getMaxNumPictureInPictureActions()) {
-                    throw new IllegalArgumentException("setPictureInPictureActions: Invalid number"
-                            + " of picture-in-picture actions.  Only a maximum of "
-                            + ActivityManager.getMaxNumPictureInPictureActions()
-                            + " actions allowed");
-                }
-
-                r.pictureInPictureArgs.userActions = actions;
-                if (r.getStack().getStackId() == PINNED_STACK_ID) {
+                // Only update the saved args from the args that are set
+                r.pictureInPictureArgs.copyOnlySet(args);
+                final ActivityStack stack = r.getStack();
+                if (stack.getStackId() == PINNED_STACK_ID) {
                     // If the activity is already in picture-in-picture, update the pinned stack now
-                    mWindowManager.setPictureInPictureActions(actions);
+                    stack.setPictureInPictureAspectRatio(r.pictureInPictureArgs.getAspectRatio());
+                    stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
                 }
             }
         } finally {
@@ -7696,14 +7688,10 @@
      * Checks the state of the system and the activity associated with the given {@param token} to
      * verify that picture-in-picture is supported for that activity.
      *
-     * @param checkAspectRatio whether or not to check {@param aspectRatio} is within a valid range
-     * @param checkActivityVisibility whether or not to enforce that the activity is currently
-     *                                visible
-     *
      * @return the activity record for the given {@param token} if all the checks pass.
      */
-    private ActivityRecord ensureValidPictureInPictureActivityLocked(String caller, IBinder token,
-            float aspectRatio, boolean checkAspectRatio, boolean checkActivityVisibility) {
+    private ActivityRecord ensureValidPictureInPictureActivityArgsLocked(String caller,
+            IBinder token, PictureInPictureArgs args) {
         if (!mSupportsPictureInPicture) {
             throw new IllegalStateException(caller
                     + ": Device doesn't support picture-in-picture mode.");
@@ -7715,10 +7703,9 @@
                     + ": Can't find activity for token=" + token);
         }
 
-        if (!r.canEnterPictureInPicture(checkActivityVisibility)) {
-            throw new IllegalArgumentException(caller
-                    + ": Current activity does not support picture-in-picture or is not "
-                    + "visible r=" + r);
+        if (!r.supportsPictureInPicture()) {
+            throw new IllegalStateException(caller
+                    + ": Current activity does not support picture-in-picture.");
         }
 
         if (r.getStack().isHomeStack()) {
@@ -7726,12 +7713,20 @@
                     + ": Activities on the home stack not supported");
         }
 
-        if (checkAspectRatio && !isValidPictureInPictureAspectRatio(aspectRatio)) {
+        if (args.hasSetAspectRatio()
+                && !isValidPictureInPictureAspectRatio(args.getAspectRatio())) {
             throw new IllegalArgumentException(String.format(caller
                     + ": Aspect ratio is too extreme (must be between %f and %f).",
                             mMinPipAspectRatio, mMaxPipAspectRatio));
         }
 
+        if (args.hasSetActions()
+                && args.getActions().size() > ActivityManager.getMaxNumPictureInPictureActions()) {
+            throw new IllegalArgumentException(String.format(caller + ": Invalid number of"
+                    + "picture-in-picture actions.  Only a maximum of %d actions allowed",
+                            ActivityManager.getMaxNumPictureInPictureActions()));
+        }
+
         return r;
     }
 
@@ -8020,15 +8015,80 @@
         return readMet && writeMet;
     }
 
-    public int getAppStartMode(int uid, String packageName) {
+    public boolean isAppStartModeDisabled(int uid, String packageName) {
         synchronized (this) {
-            return checkAllowBackgroundLocked(uid, packageName, -1, false);
+            return getAppStartModeLocked(uid, packageName, 0, -1, false, true)
+                    == ActivityManager.APP_START_MODE_DISABLED;
         }
     }
 
-    int checkAllowBackgroundLocked(int uid, String packageName, int callingPid,
-            boolean alwaysRestrict) {
+    // Unified app-op and target sdk check
+    int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
+        // Apps that target O+ are always subject to background check
+        if (mConstants.ENFORCE_BG_CHECK && packageTargetSdk >= Build.VERSION_CODES.O) {
+            if (DEBUG_BACKGROUND_CHECK) {
+                Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
+            }
+            return ActivityManager.APP_START_MODE_DELAYED_RIGID;
+        }
+        // ...and legacy apps get an AppOp check
+        int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
+                uid, packageName);
+        if (DEBUG_BACKGROUND_CHECK) {
+            Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
+        }
+        switch (appop) {
+            case AppOpsManager.MODE_ALLOWED:
+                return ActivityManager.APP_START_MODE_NORMAL;
+            case AppOpsManager.MODE_IGNORED:
+                return ActivityManager.APP_START_MODE_DELAYED;
+            default:
+                return ActivityManager.APP_START_MODE_DELAYED_RIGID;
+        }
+    }
+
+    // Service launch is available to apps with run-in-background exemptions but
+    // some other background operations are not.  If we're doing a check
+    // of service-launch policy, allow those callers to proceed unrestricted.
+    int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
+        // Persistent app?  NB: expects that persistent uids are always active.
+        final UidRecord uidRec = mActiveUids.get(uid);
+        if (uidRec != null && uidRec.persistent) {
+            if (DEBUG_BACKGROUND_CHECK) {
+                Slog.i(TAG, "App " + uid + "/" + packageName
+                        + " is persistent; not restricted in background");
+            }
+            return ActivityManager.APP_START_MODE_NORMAL;
+        }
+
+        // Non-persistent but background whitelisted?
+        if (uidOnBackgroundWhitelist(uid)) {
+            if (DEBUG_BACKGROUND_CHECK) {
+                Slog.i(TAG, "App " + uid + "/" + packageName
+                        + " on background whitelist; not restricted in background");
+            }
+            return ActivityManager.APP_START_MODE_NORMAL;
+        }
+
+        // Is this app on the battery whitelist?
+        if (isOnDeviceIdleWhitelistLocked(uid)) {
+            if (DEBUG_BACKGROUND_CHECK) {
+                Slog.i(TAG, "App " + uid + "/" + packageName
+                        + " on idle whitelist; not restricted in background");
+            }
+            return ActivityManager.APP_START_MODE_NORMAL;
+        }
+
+        // None of the service-policy criteria apply, so we apply the common criteria
+        return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
+    }
+
+    int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
+            int callingPid, boolean alwaysRestrict, boolean disabledOnly) {
         UidRecord uidRec = mActiveUids.get(uid);
+        if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+                + packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
+                + (uidRec != null ? uidRec.idle : false));
         if (uidRec == null || alwaysRestrict || uidRec.idle) {
             boolean ephemeral;
             if (uidRec == null) {
@@ -8042,27 +8102,49 @@
                 // We are hard-core about ephemeral apps not running in the background.
                 return ActivityManager.APP_START_MODE_DISABLED;
             } else {
-                if (callingPid >= 0) {
-                    ProcessRecord proc;
-                    synchronized (mPidsSelfLocked) {
-                        proc = mPidsSelfLocked.get(callingPid);
-                    }
-                    if (proc != null && proc.curProcState
-                            < ActivityManager.PROCESS_STATE_RECEIVER) {
-                        // Whoever is instigating this is in the foreground, so we will allow it
-                        // to go through.
-                        return ActivityManager.APP_START_MODE_NORMAL;
+                if (disabledOnly) {
+                    // The caller is only interested in whether app starts are completely
+                    // disabled for the given package (that is, it is an instant app).  So
+                    // we don't need to go further, which is all just seeing if we should
+                    // apply a "delayed" mode for a regular app.
+                    return ActivityManager.APP_START_MODE_NORMAL;
+                }
+                final int startMode = (alwaysRestrict)
+                        ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
+                        : appServicesRestrictedInBackgroundLocked(uid, packageName,
+                                packageTargetSdk);
+                if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid
+                        + " pkg=" + packageName + " startMode=" + startMode
+                        + " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid));
+                if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
+                    // This is an old app that has been forced into a "compatible as possible"
+                    // mode of background check.  To increase compatibility, we will allow other
+                    // foreground apps to cause its services to start.
+                    if (callingPid >= 0) {
+                        ProcessRecord proc;
+                        synchronized (mPidsSelfLocked) {
+                            proc = mPidsSelfLocked.get(callingPid);
+                        }
+                        if (proc != null && proc.curProcState
+                                < ActivityManager.PROCESS_STATE_RECEIVER) {
+                            // Whoever is instigating this is in the foreground, so we will allow it
+                            // to go through.
+                            return ActivityManager.APP_START_MODE_NORMAL;
+                        }
                     }
                 }
-                if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid,
-                        packageName) != AppOpsManager.MODE_ALLOWED) {
-                    return ActivityManager.APP_START_MODE_DELAYED;
-                }
+                return startMode;
             }
         }
         return ActivityManager.APP_START_MODE_NORMAL;
     }
 
+    boolean isOnDeviceIdleWhitelistLocked(int uid) {
+        final int appId = UserHandle.getAppId(uid);
+        return Arrays.binarySearch(mDeviceIdleWhitelist, appId) >= 0
+                || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0;
+    }
+
     private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) {
         ProviderInfo pi = null;
         ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
@@ -8079,6 +8161,12 @@
         return pi;
     }
 
+    void grantEphemeralAccessLocked(int userId, Intent intent,
+            int targetAppId, int ephemeralAppId) {
+        getPackageManagerInternalLocked().
+                grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
+    }
+
     private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
         final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
         if (targetUris != null) {
@@ -9242,7 +9330,7 @@
         if (tr.mBounds != null) {
             rti.bounds = new Rect(tr.mBounds);
         }
-        rti.isDockable = tr.canGoInDockedStack();
+        rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreen();
         rti.resizeMode = tr.mResizeMode;
 
         ActivityRecord base = null;
@@ -9659,15 +9747,17 @@
         enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
         final long ident = Binder.clearCallingIdentity();
         try {
+            final TaskRecord task;
             synchronized (this) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+                task = mStackSupervisor.anyTaskForIdLocked(
                         taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
                 if (task == null) {
                     Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
                     return null;
                 }
-                return task.getSnapshot();
             }
+            // Don't call this while holding the lock as this operation might hit the disk.
+            return task.getSnapshot();
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -10120,7 +10210,9 @@
             synchronized (this) {
                 if (animate) {
                     if (stackId == PINNED_STACK_ID) {
-                        mWindowManager.animateResizePinnedStack(bounds, animationDuration);
+                        final ActivityStack pinnedStack =
+                                mStackSupervisor.getStack(PINNED_STACK_ID);
+                        pinnedStack.animateResizePinnedStack(bounds, animationDuration);
                     } else {
                         throw new IllegalArgumentException("Stack: " + stackId
                                 + " doesn't support animated resize.");
@@ -11458,9 +11550,14 @@
             mSystemThread.installSystemProviders(providers);
         }
 
+        mConstants.start(mContext.getContentResolver());
         mCoreSettingsObserver = new CoreSettingsObserver(this);
         mFontScaleSettingObserver = new FontScaleSettingObserver();
 
+        // Now that the settings provider is published we can consider sending
+        // in a rescue party.
+        RescueParty.onSettingsProviderPublished(mContext);
+
         //mUsageStatsService.monitorPackages();
     }
 
@@ -11651,6 +11748,16 @@
         return r;
     }
 
+    private boolean uidOnBackgroundWhitelist(final int uid) {
+        final int N = mBackgroundUidWhitelist.length;
+        for (int i = 0; i < N; i++) {
+            if (uid == mBackgroundUidWhitelist[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
             String abiOverride) {
         ProcessRecord app;
@@ -11929,14 +12036,12 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (mUserController.shouldConfirmCredentials(userId)) {
-                    final int currentUserId = mUserController.getCurrentUserIdLocked();
-                    if (!mKeyguardController.isKeyguardLocked()) {
-                        // If the device is not locked, we will prompt for credentials immediately.
-                        mStackSupervisor.lockAllProfileTasks(userId);
-                    } else {
+                    if (mKeyguardController.isKeyguardLocked()) {
                         // Showing launcher to avoid user entering credential twice.
+                        final int currentUserId = mUserController.getCurrentUserIdLocked();
                         startHomeActivityLocked(currentUserId, "notifyLockedProfile");
                     }
+                    mStackSupervisor.lockAllProfileTasks(userId);
                 }
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -11945,12 +12050,12 @@
     }
 
     @Override
-    public void startConfirmDeviceCredentialIntent(Intent intent) {
+    public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
         enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mActivityStarter.startConfirmCredentialIntent(intent);
+                mActivityStarter.startConfirmCredentialIntent(intent, options);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -13289,13 +13394,12 @@
             if (supportsMultiWindow || forceResizable) {
                 mSupportsMultiWindow = true;
                 mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
-                mSupportsPictureInPicture = supportsPictureInPicture || forceResizable;
             } else {
                 mSupportsMultiWindow = false;
                 mSupportsFreeformWindowManagement = false;
-                mSupportsPictureInPicture = false;
             }
             mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
+            mSupportsPictureInPicture = supportsPictureInPicture;
             mWindowManager.setForceResizableTasks(mForceResizableActivities);
             mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
             // This happens before any activities are started, so we can change global configuration
@@ -14381,6 +14485,10 @@
                 synchronized (this) {
                     dumpAssociationsLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
                 }
+            } else if ("settings".equals(cmd)) {
+                synchronized (this) {
+                    mConstants.dump(pw);
+                }
             } else if ("services".equals(cmd) || "s".equals(cmd)) {
                 if (dumpClient) {
                     ActiveServices.ServiceDumper dumper;
@@ -14421,6 +14529,11 @@
         } else if (dumpClient) {
             ActiveServices.ServiceDumper sdumper;
             synchronized (this) {
+                mConstants.dump(pw);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
                 dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                 pw.println();
                 if (dumpAll) {
@@ -14479,6 +14592,11 @@
 
         } else {
             synchronized (this) {
+                mConstants.dump(pw);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
                 dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                 pw.println();
                 if (dumpAll) {
@@ -14977,6 +15095,8 @@
                     }
                 }
             }
+            pw.println("  mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
+            pw.println("  mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist));
         }
         if (dumpPackage == null) {
             pw.println("  mWakefulness="
@@ -17284,7 +17404,8 @@
 
     @Override
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType, String callingPackage, int userId)
+            String resolvedType, int id, Notification notification,
+            String callingPackage, int userId)
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
@@ -17303,7 +17424,8 @@
             final int callingUid = Binder.getCallingUid();
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = mServices.startServiceLocked(caller, service,
-                    resolvedType, callingPid, callingUid, callingPackage, userId);
+                    resolvedType, id, notification,
+                    callingPid, callingUid, callingPackage, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -17317,7 +17439,7 @@
                     "startServiceInPackage: " + service + " type=" + resolvedType);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = mServices.startServiceLocked(null, service,
-                    resolvedType, -1, uid, callingPackage, userId);
+                    resolvedType, 0, null, -1, uid, callingPackage, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -17637,6 +17759,7 @@
                 // Not backing this app up any more; reset its OOM adjustment
                 final ProcessRecord proc = mBackupTarget.app;
                 updateOomAdjLocked(proc);
+                proc.inFullBackup = false;
 
                 // If the app crashed during backup, 'thread' will be null here
                 if (proc.thread != null) {
@@ -18176,6 +18299,13 @@
         }
 
         if (action != null) {
+            if (mBackgroundLaunchBroadcasts.contains(action)) {
+                if (DEBUG_BACKGROUND_CHECK) {
+                    Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
+                }
+                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+            }
+
             switch (action) {
                 case Intent.ACTION_UID_REMOVED:
                 case Intent.ACTION_PACKAGE_REMOVED:
@@ -19300,7 +19430,8 @@
                 UserHandle.USER_ALL);
         if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
             intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
             if (initLocale || !mProcessesReady) {
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             }
@@ -19467,7 +19598,7 @@
 
     /** Helper method that requests bounds from WM and applies them to stack. */
     private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
-        final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
+        final Rect newBounds = mStackSupervisor.getStack(stackId).getBoundsForNewConfiguration();
         mStackSupervisor.resizeStackLocked(
                 stackId, newBounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
                 false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
@@ -19488,7 +19619,8 @@
                                    && config.navigation == Configuration.NAVIGATION_NONAV);
         int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
         final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
-                && !(modeType == Configuration.UI_MODE_TYPE_WATCH && "user".equals(Build.TYPE)));
+                && !(modeType == Configuration.UI_MODE_TYPE_WATCH && "user".equals(Build.TYPE))
+                && modeType != Configuration.UI_MODE_TYPE_TELEVISION);
         return inputMethodExists && uiModeSupportsDialogs && !inVrMode;
     }
 
@@ -21266,17 +21398,8 @@
         mNewNumServiceProcs = 0;
         mNewNumAServiceProcs = 0;
 
-        final int emptyProcessLimit;
-        final int cachedProcessLimit;
-        if (mProcessLimit <= 0) {
-            emptyProcessLimit = cachedProcessLimit = 0;
-        } else if (mProcessLimit == 1) {
-            emptyProcessLimit = 1;
-            cachedProcessLimit = 0;
-        } else {
-            emptyProcessLimit = ProcessList.computeEmptyProcessLimit(mProcessLimit);
-            cachedProcessLimit = mProcessLimit - emptyProcessLimit;
-        }
+        final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
+        final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit;
 
         // Let's determine how many processes we have running vs.
         // how many slots we have for background processes; we may want
@@ -21384,7 +21507,7 @@
                         }
                         break;
                     case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
-                        if (numEmpty > ProcessList.TRIM_EMPTY_APPS
+                        if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
                                 && app.lastActivityTime < oldTime) {
                             app.kill("empty for "
                                     + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
@@ -21437,8 +21560,8 @@
         // memory they want.
         final int numCachedAndEmpty = numCached + numEmpty;
         int memFactor;
-        if (numCached <= ProcessList.TRIM_CACHED_APPS
-                && numEmpty <= ProcessList.TRIM_EMPTY_APPS) {
+        if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
+                && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
             if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                 memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
             } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
@@ -21623,12 +21746,18 @@
         for (int i=mActiveUids.size()-1; i>=0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
             int uidChange = UidRecord.CHANGE_PROCSTATE;
-            if (uidRec.setProcState != uidRec.curProcState) {
+            if (uidRec.setProcState != uidRec.curProcState
+                    || uidRec.setWhitelist != uidRec.curWhitelist) {
                 if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                         "Changes in " + uidRec + ": proc state from " + uidRec.setProcState
-                        + " to " + uidRec.curProcState);
-                if (ActivityManager.isProcStateBackground(uidRec.curProcState)) {
-                    if (!ActivityManager.isProcStateBackground(uidRec.setProcState)) {
+                        + " to " + uidRec.curProcState + ", whitelist from " + uidRec.setWhitelist
+                        + " to " + uidRec.curWhitelist);
+                if (ActivityManager.isProcStateBackground(uidRec.curProcState)
+                        && !uidRec.curWhitelist) {
+                    // UID is now in the background (and not on the temp whitelist).  Was it
+                    // previously in the foreground (or on the temp whitelist)?
+                    if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
+                            || uidRec.setWhitelist) {
                         uidRec.lastBackgroundTime = nowElapsed;
                         if (!mHandler.hasMessages(IDLE_UIDS_MSG)) {
                             // Note: the background settle time is in elapsed realtime, while
@@ -21646,6 +21775,7 @@
                     uidRec.lastBackgroundTime = 0;
                 }
                 uidRec.setProcState = uidRec.curProcState;
+                uidRec.setWhitelist = uidRec.curWhitelist;
                 enqueueUidChangeLocked(uidRec, -1, uidChange);
                 noteUidProcessState(uidRec.uid, uidRec.curProcState);
             }
@@ -21789,6 +21919,20 @@
         enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
     }
 
+    final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
+        boolean changed = false;
+        for (int i=mActiveUids.size()-1; i>=0; i--) {
+            final UidRecord uidRec = mActiveUids.valueAt(i);
+            if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) {
+                uidRec.curWhitelist = onWhitelist;
+                changed = true;
+            }
+        }
+        if (changed) {
+            updateOomAdjLocked();
+        }
+    }
+
     final void trimApplications() {
         synchronized (this) {
             int i;
@@ -22494,6 +22638,21 @@
         }
 
         @Override
+        public void setDeviceIdleWhitelist(int[] appids) {
+            synchronized (ActivityManagerService.this) {
+                mDeviceIdleWhitelist = appids;
+            }
+        }
+
+        @Override
+        public void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId, boolean adding) {
+            synchronized (ActivityManagerService.this) {
+                mDeviceIdleTempWhitelist = appids;
+                setAppIdTempWhitelistStateLocked(changingAppId, adding);
+            }
+        }
+
+        @Override
         public void updatePersistentConfigurationForUser(@NonNull Configuration values,
                 int userId) {
             Preconditions.checkNotNull(values, "Configuration must not be null");
@@ -22776,6 +22935,52 @@
         return mUserController.restartUser(userId, /* foreground */ false);
     }
 
+    @Override
+    public void scheduleApplicationInfoChanged(List<String> packageNames, int userId) {
+        enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+                "scheduleApplicationInfoChanged()");
+
+        synchronized (this) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                updateApplicationInfoLocked(packageNames, userId);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
+        final boolean updateFrameworkRes = packagesToUpdate.contains("android");
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord app = mLruProcesses.get(i);
+            if (app.thread == null) {
+                continue;
+            }
+
+            if (userId != UserHandle.USER_ALL && app.userId != userId) {
+                continue;
+            }
+
+            final int packageCount = app.pkgList.size();
+            for (int j = 0; j < packageCount; j++) {
+                final String packageName = app.pkgList.keyAt(j);
+                if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
+                    try {
+                        final ApplicationInfo ai = mPackageManagerInt.getApplicationInfo(
+                                packageName, app.userId);
+                        if (ai != null) {
+                            app.thread.scheduleApplicationInfoChanged(ai);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
+                                    packageName, app));
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Attach an agent to the specified process (proces name or PID)
      */
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 29a4781..df8dd6b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -167,11 +167,13 @@
                     return runBugReport(pw);
                 case "force-stop":
                     return runForceStop(pw);
+                case "crash":
+                    return runCrash(pw);
                 case "kill":
                     return runKill(pw);
                 case "kill-all":
                     return runKillAll(pw);
-                case "make-idle":
+                case "make-uid-idle":
                     return runMakeIdle(pw);
                 case "monitor":
                     return runMonitor(pw);
@@ -235,6 +237,8 @@
                     return runSupportsMultiwindow(pw);
                 case "supports-split-screen-multi-window":
                     return runSupportsSplitScreenMultiwindow(pw);
+                case "update-appinfo":
+                    return runUpdateApplicationInfo(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -490,7 +494,7 @@
         pw.println("Starting service: " + intent);
         pw.flush();
         ComponentName cn = mInterface.startService(null, intent, intent.getType(),
-                SHELL_PACKAGE_NAME, mUserId);
+                -1, null, SHELL_PACKAGE_NAME, mUserId);
         if (cn == null) {
             err.println("Error: Not found; no service started.");
             return -1;
@@ -500,6 +504,9 @@
         } else if (cn.getPackageName().equals("!!")) {
             err.println("Error: " + cn.getClassName());
             return -1;
+        } else if (cn.getPackageName().equals("?")) {
+            err.println("Error: " + cn.getClassName());
+            return -1;
         }
         return 0;
     }
@@ -846,6 +853,32 @@
         return 0;
     }
 
+    int runCrash(PrintWriter pw) throws RemoteException {
+        int userId = UserHandle.USER_ALL;
+
+        String opt;
+        while ((opt=getNextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                getErrPrintWriter().println("Error: Unknown option: " + opt);
+                return -1;
+            }
+        }
+
+        int pid = -1;
+        String packageName = null;
+        final String arg = getNextArgRequired();
+        // The argument is either a pid or a package name
+        try {
+            pid = Integer.parseInt(arg);
+        } catch (NumberFormatException e) {
+            packageName = arg;
+        }
+        mInterface.crashApplication(-1, pid, packageName, userId, "shell-induced crash");
+        return 0;
+    }
+
     int runKill(PrintWriter pw) throws RemoteException {
         int userId = UserHandle.USER_ALL;
 
@@ -2323,6 +2356,19 @@
         return 0;
     }
 
+    int runUpdateApplicationInfo(PrintWriter pw) throws RemoteException {
+        int userid = UserHandle.parseUserArg(getNextArgRequired());
+        ArrayList<String> packages = new ArrayList<>();
+        packages.add(getNextArgRequired());
+        String packageName;
+        while ((packageName = getNextArg()) != null) {
+            packages.add(packageName);
+        }
+        mInternal.scheduleApplicationInfoChanged(packages, userid);
+        pw.println("Packages updated with most recent ApplicationInfos.");
+        return 0;
+    }
+
     private Resources getResources(PrintWriter pw) throws RemoteException {
         // system resources does not contain all the device configuration, construct it manually.
         Configuration config = mInterface.getConfiguration();
@@ -2360,6 +2406,7 @@
             pw.println("    provider [COMP_SPEC]: provider client-side state");
             pw.println("    s[ervices] [COMP_SPEC ...]: service state");
             pw.println("    as[sociations]: tracked app associations");
+            pw.println("    settings: currently applied config settings");
             pw.println("    service [COMP_SPEC]: service client-side state");
             pw.println("    package [PACKAGE_NAME]: all state related to given package");
             pw.println("    all: dump all activities");
@@ -2462,10 +2509,15 @@
             pw.println("     --telephony: will dump only telephony sections.");
             pw.println("  force-stop [--user <USER_ID> | all | current] <PACKAGE>");
             pw.println("      Completely stop the given application package.");
+            pw.println("  crash [--user <USER_ID>] <PACKAGE|PID>");
+            pw.println("      Induce a VM crash in the specified package or process");
             pw.println("  kill [--user <USER_ID> | all | current] <PACKAGE>");
             pw.println("      Kill all processes associated with the given application.");
             pw.println("  kill-all");
             pw.println("      Kill all processes that are safe to kill (cached, etc).");
+            pw.println("  make-uid-idle [--user <USER_ID> | all | current] <PACKAGE>");
+            pw.println("      If the given application's uid is in the background and waiting to");
+            pw.println("      become idle (not allowing background services), do that now.");
             pw.println("  monitor [--gdb <port>]");
             pw.println("      Start monitoring for crashes or ANRs.");
             pw.println("      --gdb: start gdbserv on the given port at crash/ANR");
@@ -2584,6 +2636,9 @@
             pw.println("           Test command for sizing <TASK_ID> by <STEP_SIZE>");
             pw.println("           increments within the screen applying the optional [DELAY_MS] between");
             pw.println("           each step.");
+            pw.println("  update-appinfo <USER_ID> <PACKAGE_NAME> [<PACKAGE_NAME>...]");
+            pw.println("      Update the ApplicationInfo objects of the listed packages for <USER_ID>");
+            pw.println("      without restarting any processes.");
             pw.println("  write");
             pw.println("      Write all pending state to storage.");
             pw.println();
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index e46d204..65b8554 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -3,9 +3,7 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -17,7 +15,7 @@
 import android.os.SystemClock;
 import android.util.Slog;
 
-import com.android.internal.logging.LogBuilder;
+import android.metrics.LogMaker;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
@@ -172,7 +170,7 @@
         MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
                 (int) (SystemClock.uptimeMillis() / 1000));
 
-        LogBuilder builder = new LogBuilder(MetricsEvent.APP_TRANSITION);
+        LogMaker builder = new LogMaker(MetricsEvent.APP_TRANSITION);
         builder.addTaggedData(MetricsEvent.APP_TRANSITION_COMPONENT_NAME, componentName);
         builder.addTaggedData(MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0);
         builder.addTaggedData(MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 47c3e6f..2a849b6 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -22,6 +22,8 @@
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
@@ -37,7 +39,6 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.os.Build.VERSION_CODES.HONEYCOMB;
@@ -63,9 +64,9 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager.TaskDescription;
-import android.app.ActivityManagerInternal.PictureInPictureArguments;
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
+import android.app.PictureInPictureArgs;
 import android.app.ResultInfo;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -149,6 +150,7 @@
     AppWindowContainerController mWindowContainerController;
     final ActivityInfo info; // all about me
     final ApplicationInfo appInfo; // information about activity's app
+    final int launchedFromPid; // always the pid who started the activity.
     final int launchedFromUid; // always the uid who started the activity.
     final String launchedFromPackage; // always the package who started the activity.
     final int userId;          // Which user is this running for?
@@ -229,13 +231,10 @@
     boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
     boolean immersive;      // immersive mode (don't interrupt if possible)
     boolean forceNewConfig; // force re-create with new config next time
-    boolean supportsPipOnMoveToBackground;   // Supports automatically entering picture-in-picture
-        // when this activity is hidden. This flag is requested by the activity.
-    private boolean enterPipOnMoveToBackground; // Flag to enter picture in picture when this
-        // activity is made invisible. This flag is set specifically when another task is being
-        // launched or moved to the front which may cause this activity to try and enter PiP
-        // when it is next made invisible.
-    PictureInPictureArguments pictureInPictureArgs = new PictureInPictureArguments();  // The PiP
+    boolean supportsPictureInPictureWhilePausing;  // This flag is set by the system to indicate
+        // that the activity can enter picture in picture while pausing (ie. only when another
+        // task is brought to front or started)
+    PictureInPictureArgs pictureInPictureArgs = new PictureInPictureArgs();  // The PiP
         // arguments used when deferring the entering of picture-in-picture.
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last launch of this activity
@@ -452,13 +451,10 @@
         }
         if (info != null) {
             pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
+            pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
         }
-        if (supportsPipOnMoveToBackground) {
-            pw.println(prefix + "supportsPipOnMoveToBackground=1");
-            pw.println(prefix + "enterPipOnMoveToBackground=" +
-                    (enterPipOnMoveToBackground ? 1 : 0));
-            pictureInPictureArgs.dump(pw, prefix);
-        }
+        pw.println(prefix + "supportsPictureInPictureWhilePausing: "
+                + supportsPictureInPictureWhilePausing);
     }
 
     private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
@@ -595,7 +591,7 @@
         return ResolverActivity.class.getName().equals(realActivity.getClassName());
     }
 
-    ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
+    ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromPid,
             int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
             ActivityInfo aInfo, Configuration _configuration,
             ActivityRecord _resultTo, String _resultWho, int _reqCode,
@@ -605,6 +601,7 @@
         service = _service;
         appToken = new Token(this);
         info = aInfo;
+        launchedFromPid = _launchedFromPid;
         launchedFromUid = _launchedFromUid;
         launchedFromPackage = _launchedFromPackage;
         userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
@@ -819,23 +816,6 @@
     }
 
     /**
-     * If this activity has requested that it auto-enter picture-in-picture and we can actually do
-     * this, then mark it to enter picture in picture at that point.
-     */
-    void setEnterPipOnMoveToBackground(boolean enterPipOnInvisible) {
-        if (supportsPipOnMoveToBackground) {
-            enterPipOnMoveToBackground = enterPipOnInvisible;
-        }
-    }
-
-    /**
-     * @return whether to enter PiP when this activity is made invisible.
-     */
-    public boolean shouldEnterPictureInPictureOnInvisible() {
-        return enterPipOnMoveToBackground;
-    }
-
-    /**
      * @return Stack value from current task, null if there is no task.
      */
     ActivityStack getStack() {
@@ -897,49 +877,96 @@
     }
 
     boolean isResizeable() {
-        return ActivityInfo.isResizeableMode(info.resizeMode);
+        return ActivityInfo.isResizeableMode(info.resizeMode) || info.supportsPictureInPicture();
     }
 
-    boolean isResizeableOrForced() {
-        return !isHomeActivity() && (isResizeable() || service.mForceResizableActivities);
-    }
-
-    boolean isNonResizableOrForced() {
+    /**
+     * @return whether this activity is non-resizeable or forced to be resizeable
+     */
+    boolean isNonResizableOrForcedResizable() {
         return info.resizeMode != RESIZE_MODE_RESIZEABLE
-                && info.resizeMode != RESIZE_MODE_RESIZEABLE_AND_PIPABLE
                 && info.resizeMode != RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
     }
 
     /**
-     * @return whether this activity's resize mode supports PIP.
+     * @return whether this activity supports PiP multi-window and can be put in the pinned stack.
      */
     boolean supportsPictureInPicture() {
-        return !isHomeActivity() && info.resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+        return service.mSupportsPictureInPicture && !isHomeActivity()
+                && info.supportsPictureInPicture();
     }
 
     /**
-     * @return whether this activity is currently allowed to enter PIP, if
-     * {@param checkActivityVisibility} is set, then the current activity visibility is taken into
-     * account.
+     * @return whether this activity supports split-screen multi-window and can be put in the docked
+     *         stack.
      */
-    boolean canEnterPictureInPicture(boolean checkActivityVisibility) {
-        if (!checkActivityVisibility) {
-            return supportsPictureInPicture();
-        }
-
-        if (supportsPictureInPicture()) {
-            switch (state) {
-                case RESUMED:
-                case PAUSING:
-                case PAUSED:
-                    return true;
-            }
-        }
-        return false;
+    boolean supportsSplitScreen() {
+        // An activity can not be docked even if it is considered resizeable because it only
+        // supports picture-in-picture mode but has a non-resizeable resizeMode
+        return service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
     }
 
-    boolean canGoInDockedStack() {
-        return !isHomeActivity() && isResizeableOrForced();
+    /**
+     * @return whether this activity supports freeform multi-window and can be put in the freeform
+     *         stack.
+     */
+    boolean supportsFreeform() {
+        return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
+    }
+
+    /**
+     * @return whether this activity supports non-PiP multi-window.
+     */
+    private boolean supportsResizeableMultiWindow() {
+        return service.mSupportsMultiWindow && !isHomeActivity()
+                && (ActivityInfo.isResizeableMode(info.resizeMode)
+                        || service.mForceResizableActivities);
+    }
+
+    /**
+     * @return whether this activity is currently allowed to enter PIP, throwing an exception if
+     *         the activity is not currently visible.
+     */
+    boolean checkEnterPictureInPictureState(String caller) {
+        boolean isKeyguardLocked = service.isKeyguardLocked();
+        boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null;
+        switch (state) {
+            case RESUMED:
+                // When visible, allow entering PiP if not on the lockscreen.  If there is another
+                // PiP activity, the logic to handle that comes later in enterPictureInPictureMode()
+                return !isKeyguardLocked;
+            case PAUSING:
+            case PAUSED:
+                // When pausing, only allow enter PiP if not on the lockscreen and there is not
+                // already an existing PiP activity
+                return !isKeyguardLocked && !hasPinnedStack && supportsPictureInPictureWhilePausing
+                        && checkEnterPictureInPictureOnHideAppOpsState();
+            case STOPPING:
+                // When stopping in a valid state, then only allow enter PiP as in the pause state.
+                // Otherwise, fall through to throw an exception if the caller is trying to enter
+                // PiP in an invalid stopping state.
+                if (supportsPictureInPictureWhilePausing) {
+                    return !isKeyguardLocked && !hasPinnedStack
+                            && checkEnterPictureInPictureOnHideAppOpsState();
+                }
+            default:
+                throw new IllegalStateException(caller
+                        + ": Current activity is not visible (state=" + state.name() + ") "
+                        + "r=" + this);
+        }
+    }
+
+    /**
+     * @return Whether AppOps allows this package to enter picture-in-picture when it is hidden.
+     */
+    private boolean checkEnterPictureInPictureOnHideAppOpsState() {
+        try {
+            return service.getAppOpsService().checkOperation(OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE,
+                    appInfo.uid, packageName) == MODE_ALLOWED;
+        } catch (RemoteException e) {
+            // Local call
+        }
+        return false;
     }
 
     boolean isAlwaysFocusable() {
@@ -1802,6 +1829,9 @@
     }
 
     void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
+        if (mWindowContainerController == null) {
+            return;
+        }
         final CompatibilityInfo compatInfo =
                 service.compatibilityInfoForPackageLocked(info.applicationInfo);
         final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
@@ -2203,9 +2233,11 @@
             throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent +
                     " resolvedType=" + resolvedType);
         }
-        final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid,
-                launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(),
-                null, null, 0, componentSpecified, false, stackSupervisor, null, null, null);
+        final ActivityRecord r = new ActivityRecord(service, null /* caller */,
+                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
+                aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
+                0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
+                stackSupervisor, null /* container */, null /* options */, null /* sourceRecord */);
 
         r.persistentState = persistentState;
         r.taskDescription = taskDescription;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4df0cb1..98b5835c 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -62,7 +62,6 @@
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
 import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
@@ -73,7 +72,6 @@
 import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
 import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
 import static java.lang.Integer.MAX_VALUE;
-import static java.lang.Integer.MIN_VALUE;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -82,6 +80,7 @@
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityController;
+import android.app.RemoteAction;
 import android.app.ResultInfo;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -105,6 +104,7 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -115,6 +115,8 @@
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
+import com.android.server.wm.StackWindowController;
+import com.android.server.wm.StackWindowListener;
 import com.android.server.wm.WindowManagerService;
 
 import java.io.FileDescriptor;
@@ -129,7 +131,7 @@
 /**
  * State and management of a single stack of activities.
  */
-final class ActivityStack extends ConfigurationContainer {
+final class ActivityStack extends ConfigurationContainer implements StackWindowListener {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
@@ -195,6 +197,12 @@
         return mActivityContainer.mActivityDisplay;
     }
 
+    @Override
+    void onParentChanged() {
+        super.onParentChanged();
+        mStackSupervisor.updateUIDsPresentOnDisplay();
+    }
+
     enum ActivityState {
         INITIALIZING,
         RESUMED,
@@ -235,6 +243,7 @@
 
     final ActivityManagerService mService;
     private final WindowManagerService mWindowManager;
+    private StackWindowController mWindowContainerController;
     private final RecentTasks mRecentTasks;
 
     /**
@@ -323,7 +332,7 @@
     private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>();
     private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
     private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
-    private final Rect tempRect2 = new Rect();
+    private final Rect mTmpRect2 = new Rect();
 
     /** Run all ActivityStacks through this */
     private final ActivityStackSupervisor mStackSupervisor;
@@ -436,7 +445,7 @@
     }
 
     ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
-            RecentTasks recentTasks) {
+            RecentTasks recentTasks, boolean onTop) {
         mActivityContainer = activityContainer;
         mStackSupervisor = activityContainer.getOuter();
         mService = mStackSupervisor.mService;
@@ -447,13 +456,25 @@
         mRecentTasks = recentTasks;
         mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
                 ? new LaunchingTaskPositioner() : null;
+        final ActivityStackSupervisor.ActivityDisplay display = mActivityContainer.mActivityDisplay;
+        mTmpRect2.setEmpty();
+        mWindowContainerController = new StackWindowController(mStackId, this,
+                display.mDisplayId, onTop, mTmpRect2);
+        activityContainer.mStack = this;
+        mStackSupervisor.mActivityContainers.put(mStackId, activityContainer);
+        postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
+    }
+
+    StackWindowController getWindowContainerController() {
+        return mWindowContainerController;
     }
 
     /** Adds the stack to specified display and calls WindowManager to do the same. */
-    void addToDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
-        final Rect bounds = mWindowManager.addStackToDisplay(mStackId, activityDisplay.mDisplayId,
-                onTop);
-        postAddToDisplay(activityDisplay, bounds);
+    void reparent(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
+        removeFromDisplay();
+        mTmpRect2.setEmpty();
+        mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2);
+        postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
     }
 
     /**
@@ -462,10 +483,10 @@
      * @param bounds Updated bounds.
      */
     private void postAddToDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay,
-            Rect bounds) {
+            Rect bounds, boolean onTop) {
         mDisplayId = activityDisplay.mDisplayId;
         mStacks = activityDisplay.mStacks;
-        mBounds = bounds;
+        mBounds = bounds != null ? new Rect(bounds) : null;
         mFullscreen = mBounds == null;
         if (mTaskPositioner != null) {
             mTaskPositioner.setDisplay(activityDisplay.mDisplay);
@@ -473,6 +494,7 @@
         }
         onParentChanged();
 
+        activityDisplay.attachStack(this, onTop);
         if (mStackId == DOCKED_STACK_ID) {
             // If we created a docked stack we want to resize it so it resizes all other stacks
             // in the system.
@@ -482,16 +504,6 @@
     }
 
     /**
-     * Moves the stack to specified display.
-     * @param activityDisplay Target display to move the stack to.
-     */
-    void moveToDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay) {
-        removeFromDisplay();
-        final Rect bounds = mWindowManager.moveStackToDisplay(mStackId, activityDisplay.mDisplayId);
-        postAddToDisplay(activityDisplay, bounds);
-    }
-
-    /**
      * Updates the inner state of the stack to remove it from its current parent, so it can be
      * either destroyed completely or re-parented.
      */
@@ -513,7 +525,8 @@
     void remove() {
         removeFromDisplay();
         mStackSupervisor.deleteActivityContainerRecord(mStackId);
-        mWindowManager.removeStack(mStackId);
+        mWindowContainerController.removeContainer();
+        mWindowContainerController = null;
         onParentChanged();
     }
 
@@ -521,6 +534,43 @@
         mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
     }
 
+    void animateResizePinnedStack(Rect bounds, int animationDuration) {
+        mWindowContainerController.animateResizePinnedStack(bounds, animationDuration);
+    }
+
+    void setPictureInPictureAspectRatio(float aspectRatio) {
+        mWindowContainerController.setPictureInPictureAspectRatio(aspectRatio);
+    }
+
+    void getStackDockedModeBounds(Rect outBounds, boolean ignoreVisibility) {
+        mWindowContainerController.getStackDockedModeBounds(outBounds, ignoreVisibility);
+    }
+
+    void prepareFreezingTaskBounds() {
+        mWindowContainerController.prepareFreezingTaskBounds();
+    }
+
+    void setPictureInPictureActions(List<RemoteAction> actions) {
+        mWindowContainerController.setPictureInPictureActions(actions);
+    }
+
+    void getWindowContainerBounds(Rect outBounds) {
+        if (mWindowContainerController != null) {
+            mWindowContainerController.getBounds(outBounds);
+            return;
+        }
+        outBounds.setEmpty();
+    }
+
+    Rect getBoundsForNewConfiguration() {
+        return mWindowContainerController.getBoundsForNewConfiguration();
+    }
+
+    void positionChildWindowContainerAtTop(TaskRecord child) {
+        mWindowContainerController.positionChildAtTop(child.getWindowContainerController(),
+                true /* includingParents */);
+    }
+
     /**
      * Defers updating the bounds of the stack. If the stack was resized/repositioned while
      * deferring, the bounds will update in {@link #continueUpdateBounds()}.
@@ -541,8 +591,7 @@
         final boolean wasDeferred = mUpdateBoundsDeferred;
         mUpdateBoundsDeferred = false;
         if (wasDeferred && mUpdateBoundsDeferredCalled) {
-            mStackSupervisor.resizeStackUncheckedLocked(this,
-                    mDeferredBounds.isEmpty() ? null : mDeferredBounds,
+            resize(mDeferredBounds.isEmpty() ? null : mDeferredBounds,
                     mDeferredTaskBounds.isEmpty() ? null : mDeferredTaskBounds,
                     mDeferredTaskInsetBounds.isEmpty() ? null : mDeferredTaskInsetBounds);
         }
@@ -680,6 +729,27 @@
         return null;
     }
 
+    boolean isInStackLocked(TaskRecord task) {
+        return mTaskHistory.contains(task);
+    }
+
+    /** Checks if there are tasks with specific UID in the stack. */
+    boolean isUidPresent(int uid) {
+        for (TaskRecord task : mTaskHistory) {
+            if (task.effectiveUid == uid) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Get all UIDs that are present in the stack. */
+    void getPresentUIDs(IntArray presentUIDs) {
+        for (TaskRecord task : mTaskHistory) {
+            presentUIDs.add(task.effectiveUid);
+        }
+    }
+
     final boolean updateLRUListLocked(ActivityRecord r) {
         final boolean hadit = mLRUActivities.remove(r);
         mLRUActivities.add(r);
@@ -739,7 +809,8 @@
 
         task = topTask();
         if (task != null) {
-            task.moveWindowContainerToTop(true /* includingParents */);
+            mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
+                    true /* includingParents */);
         }
     }
 
@@ -758,7 +829,7 @@
             mTaskHistory.remove(task);
             mTaskHistory.add(0, task);
             updateTaskMovement(task, false);
-            task.moveWindowContainerToBottom();
+            mWindowContainerController.positionChildAtBottom(task.getWindowContainerController());
         }
     }
 
@@ -1517,7 +1588,7 @@
             // home task even though it's not resizable.
             final ActivityRecord r = focusedStack.topRunningActivityLocked();
             final TaskRecord task = r != null ? r.task : null;
-            return task == null || task.canGoInDockedStack() || task.isHomeTask() ? STACK_VISIBLE
+            return task == null || task.supportsSplitScreen() || task.isHomeTask() ? STACK_VISIBLE
                     : STACK_INVISIBLE;
         }
 
@@ -1698,9 +1769,7 @@
                                 + stackInvisible + " behindFullscreenActivity="
                                 + behindFullscreenActivity + " mLaunchTaskBehind="
                                 + r.mLaunchTaskBehind);
-                        if (!enterPictureInPictureOnActivityInvisible(r)) {
-                            makeInvisible(r, visibleBehind);
-                        }
+                        makeInvisible(r, visibleBehind);
                     }
                 }
                 if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
@@ -1769,6 +1838,12 @@
         }
     }
 
+    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            mTaskHistory.get(taskNdx).addStartingWindowsForVisibleActivities(taskSwitch);
+        }
+    }
+
     /**
      * @return true if the top visible activity wants to occlude the Keyguard, false otherwise
      */
@@ -1859,35 +1934,6 @@
         return false;
     }
 
-    /**
-     * Attempts to enter picture-in-picture if the activity that is being made invisible supports
-     * it.  If not, then
-     *
-     * @return whether or not picture-in-picture mode was entered.
-     */
-    private boolean enterPictureInPictureOnActivityInvisible(ActivityRecord r) {
-        final boolean hasPinnedStack =
-                mStackSupervisor.getStack(PINNED_STACK_ID) != null;
-        final boolean isKeyguardLocked = mService.isKeyguardLocked();
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, " enterPictureInPictureOnInvisible="
-                + r.shouldEnterPictureInPictureOnInvisible()
-                + " hasPinnedStack=" + hasPinnedStack
-                + " isKeyguardLocked=" + isKeyguardLocked);
-        if (!hasPinnedStack && !isKeyguardLocked && r.visible &&
-                r.shouldEnterPictureInPictureOnInvisible()) {
-            r.setEnterPipOnMoveToBackground(false);
-
-            // Enter picture in picture, but don't move the home stack to the front
-            // since it will affect the focused stack's visibility and occlude
-            // starting activities
-            mService.enterPictureInPictureModeLocked(r, r.getDisplayId(),
-                    r.pictureInPictureArgs, false /* moveHomeStackToFront */,
-                    "ensureActivitiesVisibleLocked");
-            return true;
-        }
-        return false;
-    }
-
     private void makeInvisible(ActivityRecord r, ActivityRecord visibleBehind) {
         if (!r.visible) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
@@ -1906,6 +1952,10 @@
                                 "Scheduling invisibility: " + r);
                         r.app.thread.scheduleWindowVisibility(r.appToken, false);
                     }
+
+                    // Reset the flag indicating that an app can enter picture-in-picture once the
+                    // activity is hidden
+                    r.supportsPictureInPictureWhilePausing = false;
                     break;
 
                 case INITIALIZING:
@@ -2187,15 +2237,16 @@
 
         mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
 
-        // We need to start pausing the current activity so the top one can be resumed...
-        final boolean dontWaitForPause = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
-        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, dontWaitForPause);
+        // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity
+        // to be paused, while at the same time resuming the new resume activity
+        final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
+        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
         if (mResumedActivity != null) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Pausing " + mResumedActivity);
-            pausing |= startPausingLocked(userLeaving, false, next, dontWaitForPause);
+            pausing |= startPausingLocked(userLeaving, false, next, false);
         }
-        if (pausing) {
+        if (pausing && !resumeWhilePausing) {
             if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
                     "resumeTopActivityLocked: Skip resume: need to start pausing");
             // At this point we want to put the upcoming activity's process
@@ -2572,7 +2623,8 @@
         position = getAdjustedPositionForTask(task, position, null /* starting */);
         mTaskHistory.remove(task);
         mTaskHistory.add(position, task);
-        task.positionWindowContainerAt(position);
+        mWindowContainerController.positionChildAt(task.getWindowContainerController(), position,
+                task.mBounds, task.getOverrideConfiguration());
         updateTaskMovement(task, true);
     }
 
@@ -2584,7 +2636,8 @@
         final int position = getAdjustedPositionForTask(task, mTaskHistory.size(), starting);
         mTaskHistory.add(position, task);
         updateTaskMovement(task, true);
-        task.moveWindowContainerToTop(true /* includingParents */);
+        mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
+                true /* includingParents */);
     }
 
     private void updateTaskReturnToForTopInsertion(TaskRecord task) {
@@ -2612,7 +2665,7 @@
                 // This also makes sure that non-home activities are visible under a transparent
                 // non-home activity.
                 task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
-            } else if (!isHomeOrRecentsStack() && (fromHomeOrRecents || topTask != task)) {
+            } else if (!isHomeOrRecentsStack() && (fromHomeOrRecents || topTask() != task)) {
                 // If it's a last task over home - we default to keep its return to type not to
                 // make underlying task focused when this one will be finished.
                 int returnToType = isLastTaskOverHome
@@ -2696,10 +2749,10 @@
                     if (r.mLaunchTaskBehind) {
                         transit = TRANSIT_TASK_OPEN_BEHIND;
                     } else {
-                        // If a new task is being launched, then mark the existing top activity to
-                        // enter picture-in-picture if it supports auto-entering PiP
+                        // If a new task is being launched, then mark the existing top activity as
+                        // supporting picture-in-picture while pausing
                         if (focusedTopActivity != null) {
-                            focusedTopActivity.setEnterPipOnMoveToBackground(true);
+                            focusedTopActivity.supportsPictureInPictureWhilePausing = true;
                         }
                         transit = TRANSIT_TASK_OPEN;
                     }
@@ -2862,7 +2915,8 @@
                     targetTask.addActivityAtBottom(p);
                 }
 
-                targetTask.moveWindowContainerToBottom();
+                mWindowContainerController.positionChildAtBottom(
+                        targetTask.getWindowContainerController());
                 replyChainEnd = -1;
             } else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
                 // If the activity should just be removed -- either
@@ -2998,7 +3052,8 @@
                         if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Pulling activity " + p
                                 + " from " + srcPos + " in to resetting task " + task);
                     }
-                    task.moveWindowContainerToTop(true /* includingParents */);
+                    mWindowContainerController.positionChildAtTop(
+                            task.getWindowContainerController(), true /* includingParents */);
 
                     // Now we've moved it in to place...  but what if this is
                     // a singleTop activity and we have put it on top of another
@@ -4245,10 +4300,10 @@
         } else {
             updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
         }
-        // If a new task is moved to the front, then mark the existing top activity to enter
-        // picture-in-picture if it supports auto-entering PiP
+        // If a new task is moved to the front, then mark the existing top activity as supporting
+        // picture-in-picture while paused
         if (focusedTopActivity != null) {
-            focusedTopActivity.setEnterPipOnMoveToBackground(true);
+            focusedTopActivity.supportsPictureInPictureWhilePausing = true;
         }
 
         mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -4367,7 +4422,7 @@
         }
 
         mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
-        tr.moveWindowContainerToBottom();
+        mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController());
 
         final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
         if (prevIsHome || (task == tr && canGoHome) || (numTasks <= 1 && isOnHomeDisplay())) {
@@ -4430,11 +4485,24 @@
         }
     }
 
-    /** Update override configurations of all tasks in the stack. */
-    void updateOverrideConfiguration(Rect stackBounds, Rect tempTaskBounds,
-            Rect tempTaskInsetBounds) {
+    // TODO: Figure-out a way to consolidate with resize() method below.
+    @Override
+    public void requestResize(Rect bounds) {
+        mService.resizeStack(mStackId, bounds, true /* allowResizeInDockedMode */,
+                false /* preserveWindows */, false /* animate */, -1 /* animationDuration */);
+    }
 
-        final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : stackBounds;
+    // TODO: Can only be called from special methods in ActivityStackSupervisor.
+    // Need to consolidate those calls points into this resize method so anyone can call directly.
+    void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) {
+        bounds = TaskRecord.validateBounds(bounds);
+
+        if (!updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
+            return;
+        }
+
+        // Update override configurations of all tasks in the stack.
+        final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
         final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
 
         mTmpBounds.clear();
@@ -4448,9 +4516,9 @@
                     // For freeform stack we don't adjust the size of the tasks to match that
                     // of the stack, but we do try to make sure the tasks are still contained
                     // with the bounds of the stack.
-                    tempRect2.set(task.mBounds);
-                    fitWithinBounds(tempRect2, stackBounds);
-                    task.updateOverrideConfiguration(tempRect2);
+                    mTmpRect2.set(task.mBounds);
+                    fitWithinBounds(mTmpRect2, bounds);
+                    task.updateOverrideConfiguration(mTmpRect2);
                 } else {
                     task.updateOverrideConfiguration(taskBounds, insetBounds);
                 }
@@ -4463,11 +4531,9 @@
             }
         }
 
-        // We might trigger a configuration change. Save the current task bounds for freezing.
-        mWindowManager.prepareFreezingTaskBounds(mStackId);
-        mFullscreen = mWindowManager.resizeStack(mStackId, stackBounds, mTmpConfigs, mTmpBounds,
+        mFullscreen = mWindowContainerController.resize(bounds, mTmpConfigs, mTmpBounds,
                 mTmpInsetBounds);
-        setBounds(stackBounds);
+        setBounds(bounds);
     }
 
 
@@ -4657,7 +4723,7 @@
             }
             ci.numActivities = numActivities;
             ci.numRunning = numRunning;
-            ci.isDockable = task.canGoInDockedStack();
+            ci.supportsSplitScreenMultiWindow = task.supportsSplitScreen();
             ci.resizeMode = task.mResizeMode;
             list.add(ci);
         }
@@ -4891,7 +4957,8 @@
         addTask(task, toTop ? MAX_VALUE : 0, reason);
         if (toTop) {
             // TODO: figure-out a way to remove this call.
-            task.moveWindowContainerToTop(true /* includingParents */);
+            mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
+                    true /* includingParents */);
         }
     }
 
@@ -4904,13 +4971,13 @@
         final boolean toTop = position >= mTaskHistory.size();
         final ActivityStack prevStack = preAddTask(task, reason, toTop);
 
+        mTaskHistory.add(position, task);
         task.setStack(this);
 
         if (toTop) {
             updateTaskReturnToForTopInsertion(task);
         }
 
-        mTaskHistory.add(position, task);
         updateTaskMovement(task, toTop);
 
         postAddTask(task, prevStack);
@@ -4927,8 +4994,8 @@
 
         final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
         final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
-        task.setStack(this);
         insertTaskAtPosition(task, index);
+        task.setStack(this);
         postAddTask(task, null /* prevStack */);
 
         if (wasResumed) {
@@ -4966,8 +5033,8 @@
         }
     }
 
-    void moveToFrontAndResumeStateIfNeeded(
-            ActivityRecord r, boolean moveToFront, boolean setResume, String reason) {
+    void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
+            boolean setPause, String reason) {
         if (!moveToFront) {
             return;
         }
@@ -4978,6 +5045,10 @@
         if (setResume) {
             mResumedActivity = r;
         }
+        // If the activity was previously pausing, then ensure we transfer that as well
+        if (setPause) {
+            mPausingActivity = r;
+        }
         // Move the stack in which we are placing the activity to the front. The call will also
         // make sure the activity focus is set.
         moveToFront(reason);
@@ -4998,6 +5069,7 @@
         final boolean wasFocused = mStackSupervisor.isFocusedStack(prevStack)
                 && (mStackSupervisor.topRunningActivityLocked() == r);
         final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
+        final boolean wasPaused = prevStack.mPausingActivity == r;
 
         final TaskRecord task = createTaskRecord(
                 mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
@@ -5005,10 +5077,14 @@
         r.setTask(task, null);
         task.addActivityToTop(r);
         mStackSupervisor.scheduleReportPictureInPictureModeChangedIfNeeded(task, prevStack);
-        moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+        moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, wasPaused,
+                "moveActivityToStack");
         if (wasResumed) {
             prevStack.mResumedActivity = null;
         }
+        if (wasPaused) {
+            prevStack.mPausingActivity = null;
+        }
     }
 
     public int getStackId() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4fe8939..29032f8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -90,7 +90,6 @@
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
 import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static java.lang.Integer.MAX_VALUE;
-import static java.lang.Integer.MIN_VALUE;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -125,6 +124,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.VirtualDisplay;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerInternal;
@@ -154,6 +154,7 @@
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
+import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -375,12 +376,15 @@
 
     // TODO: Add listener for removal of references.
     /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
-    private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<>();
+    SparseArray<ActivityContainer> mActivityContainers = new SparseArray<>();
 
     /** Mapping from displayId to display current state */
     private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
 
-    InputManagerInternal mInputManagerInternal;
+    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
+    private DisplayManagerInternal mDisplayManagerInternal;
+    private InputManagerInternal mInputManagerInternal;
 
     /** The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
      * may be finished until there is only one entry left. If this is empty the system is not
@@ -580,6 +584,7 @@
             mDisplayManager =
                     (DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
             mDisplayManager.registerDisplayListener(this, null);
+            mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
 
             Display[] displays = mDisplayManager.getDisplays();
             for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
@@ -1452,7 +1457,7 @@
             ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) {
         final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
                 callingUid);
-        if (startAnyPerm ==  PERMISSION_GRANTED) {
+        if (startAnyPerm == PERMISSION_GRANTED) {
             return true;
         }
         final int componentRestriction = getComponentRestrictionForCallingPackage(
@@ -1519,25 +1524,76 @@
             // Check if someone tries to launch an activity on a private display with a different
             // owner.
             final int launchDisplayId = options.getLaunchDisplayId();
-            if (launchDisplayId != INVALID_DISPLAY) {
-                final ActivityDisplay activityDisplay = mActivityDisplays.get(launchDisplayId);
-                if (activityDisplay != null
-                        && (activityDisplay.mDisplay.getFlags() & FLAG_PRIVATE) != 0) {
-                    if (activityDisplay.mDisplay.getOwnerUid() != callingUid) {
-                        final String msg = "Permission Denial: starting " + intent.toString()
-                                + " from " + callerApp + " (pid=" + callingPid
-                                + ", uid=" + callingUid + ") with launchDisplayId="
-                                + launchDisplayId;
-                        Slog.w(TAG, msg);
-                        throw new SecurityException(msg);
-                    }
-                }
+            if (launchDisplayId != INVALID_DISPLAY
+                    && !isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, launchDisplayId)) {
+                final String msg = "Permission Denial: starting " + intent.toString()
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ") with launchDisplayId="
+                        + launchDisplayId;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
             }
         }
 
         return true;
     }
 
+    /** Check if caller is allowed to launch activities on specified display. */
+    boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId) {
+        if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: displayId=" + launchDisplayId
+                + " callingPid=" + callingPid + " callingUid=" + callingUid);
+
+        final ActivityDisplay activityDisplay = mActivityDisplays.get(launchDisplayId);
+        if (activityDisplay == null) {
+            Slog.w(TAG, "Launch on display check: display not found");
+            return false;
+        }
+
+        if (!activityDisplay.isPrivate()) {
+            // Anyone can launch on a public display.
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                    + " allow launch on public display");
+            return true;
+        }
+
+        // Check if the caller is the owner of the display.
+        if (activityDisplay.mDisplay.getOwnerUid() == callingUid) {
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                    + " allow launch for owner of the display");
+            return true;
+        }
+
+        // Check if caller is present on display
+        if (activityDisplay.isUidPresent(callingUid)) {
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                    + " allow launch for caller present on the display");
+            return true;
+        }
+
+        // Check if the caller can launch anything.
+        final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
+                callingUid);
+        if (startAnyPerm == PERMISSION_GRANTED) {
+            if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                    + " allow launch any on display");
+            return true;
+        }
+
+        Slog.w(TAG, "Launch on display check: denied");
+        return false;
+    }
+
+    /** Update lists of UIDs that are present on displays and have access to them. */
+    void updateUIDsPresentOnDisplay() {
+        mDisplayAccessUIDs.clear();
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+            mDisplayAccessUIDs.append(activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
+        }
+        // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
+        mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
+    }
+
     UserInfo getUserInfo(int userId) {
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -2142,7 +2198,7 @@
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
         mWindowManager.deferSurfaceLayout();
         try {
-            resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds);
+            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
             if (!deferResume) {
                 stack.ensureVisibleActivitiesConfigurationLocked(
                         stack.topRunningActivityLocked(), preserveWindows);
@@ -2180,17 +2236,6 @@
         mResizingTasksDuringAnimation.clear();
     }
 
-    void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
-            Rect tempTaskInsetBounds) {
-        bounds = TaskRecord.validateBounds(bounds);
-
-        if (!stack.updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
-            return;
-        }
-
-        stack.updateOverrideConfiguration(bounds, tempTaskBounds, tempTaskInsetBounds);
-    }
-
     void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) {
         final ActivityStack stack = getStack(fromStackId);
         if (stack == null) {
@@ -2284,8 +2329,7 @@
             // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
             mAllowDockedStackResize = false;
             ActivityRecord r = stack.topRunningActivityLocked();
-            resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds,
-                    tempDockedTaskInsetBounds);
+            stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds);
 
             // TODO: Checking for isAttached might not be needed as if the user passes in null
             // dockedBounds then they want the docked stack to be dismissed.
@@ -2302,10 +2346,10 @@
                 // static stacks need to be adjusted so they don't overlap with the docked stack.
                 // We get the bounds to use from window manager which has been adjusted for any
                 // screen controls and is also the same for all stacks.
-                mWindowManager.getStackDockedModeBounds(
-                        HOME_STACK_ID, tempRect, true /* ignoreVisibility */);
                 for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                    if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
+                    final ActivityStack current = getStack(i);
+                    if (current != null && StackId.isResizeableByDockedStack(i)) {
+                        current.getStackDockedModeBounds(tempRect, true /* ignoreVisibility */);
                         resizeStackLocked(i, tempRect, tempOtherTaskBounds,
                                 tempOtherTaskInsetBounds, preserveWindows,
                                 true /* allowResizeInDockedMode */, deferResume);
@@ -2338,8 +2382,7 @@
         mWindowManager.deferSurfaceLayout();
         try {
             ActivityRecord r = stack.topRunningActivityLocked();
-            resizeStackUncheckedLocked(stack, pinnedBounds, tempPinnedTaskBounds,
-                    null);
+            stack.resize(pinnedBounds, tempPinnedTaskBounds, null);
             stack.ensureVisibleActivitiesConfigurationLocked(r, false);
         } finally {
             mWindowManager.continueSurfaceLayout();
@@ -2348,14 +2391,13 @@
     }
 
     ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) {
-        ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+        final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
         if (activityDisplay == null) {
             return null;
         }
 
-        ActivityContainer activityContainer = new ActivityContainer(stackId);
-        mActivityContainers.put(stackId, activityContainer);
-        activityContainer.addToDisplayLocked(activityDisplay, onTop);
+        final ActivityContainer activityContainer =
+                new ActivityContainer(stackId, activityDisplay, onTop);
         return activityContainer.mStack;
     }
 
@@ -2519,7 +2561,7 @@
             // This means that tasks that were on external displays will be restored on the
             // primary display.
             stackId = task.getLaunchStackId();
-        } else if (stackId == DOCKED_STACK_ID && !task.canGoInDockedStack()) {
+        } else if (stackId == DOCKED_STACK_ID && !task.supportsSplitScreen()) {
             // Preferred stack is the docked stack, but the task can't go in the docked stack.
             // Put it in the fullscreen stack.
             stackId = FULLSCREEN_WORKSPACE_STACK_ID;
@@ -2617,6 +2659,7 @@
         final ActivityStack prevStack = task.getStack();
         final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
         final boolean wasResumed = prevStack.mResumedActivity == r;
+        final boolean wasPaused = prevStack.mPausingActivity == r;
         // In some cases the focused stack isn't the front stack. E.g. pinned stack.
         // Whenever we are moving the top activity from the front stack we want to make sure to move
         // the stack to the front.
@@ -2641,10 +2684,19 @@
         task.mTemporarilyUnresizable = false;
         task.reparent(stack.mStackId, toTop ? MAX_VALUE : 0, reason);
 
+        // Reset the resumed activity on the previous stack
+        if (wasResumed) {
+            prevStack.mResumedActivity = null;
+        }
+        // Reset the paused activity on the previous stack
+        if (wasPaused) {
+            prevStack.mPausingActivity = null;
+        }
+
         // If the task had focus before (or we're requested to move focus),
         // move focus to the new stack by moving the stack to the front.
-        stack.moveToFrontAndResumeStateIfNeeded(
-                r, forceFocus || wasFocused || wasFront, wasResumed, reason);
+        stack.moveToFrontAndResumeStateIfNeeded(r, forceFocus || wasFocused || wasFront, wasResumed,
+                wasPaused, reason);
 
         return stack;
     }
@@ -2704,7 +2756,8 @@
             }
 
             // We might trigger a configuration change. Save the current task bounds for freezing.
-            mWindowManager.prepareFreezingTaskBounds(stack.mStackId);
+            // TODO: Should this call be moved inside the resize method in WM?
+            stack.prepareFreezingTaskBounds();
 
             // Make sure the task has the appropriate bounds/size for the stack it is in.
             if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
@@ -2773,7 +2826,11 @@
 
     void moveActivityToPinnedStackLocked(ActivityRecord r, String reason, Rect bounds,
             boolean moveHomeStackToFront) {
+
         mWindowManager.deferSurfaceLayout();
+        // Need to make sure the pinned stack exist so we can resize it below...
+        final ActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+
         try {
             final TaskRecord task = r.task;
 
@@ -2783,9 +2840,6 @@
                 requestVisibleBehindLocked(r, false);
             }
 
-            // Need to make sure the pinned stack exist so we can resize it below...
-            final ActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
-
             // Resize the pinned stack to match the current size of the task the activity we are
             // going to be moving is currently contained in. We do this to have the right starting
             // animation bounds for the pinned stack to the desired bounds the caller wants.
@@ -2795,8 +2849,12 @@
 
             if (task.mActivities.size() == 1) {
                 // There is only one activity in the task. So, we can just move the task over to
-                // the stack without re-parenting the activity in a different task.
-                if (moveHomeStackToFront && task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
+                // the stack without re-parenting the activity in a different task.  We don't
+                // move the home stack forward if we are currently entering picture-in-picture
+                // while pausing because that changes the focused stack and may prevent the new
+                // starting activity from resuming.
+                if (moveHomeStackToFront && task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE
+                        && !r.supportsPictureInPictureWhilePausing) {
                     // Move the home stack forward if the task we just moved to the pinned stack
                     // was launched from home so home should be visible behind it.
                     moveHomeStackToFront(reason);
@@ -2808,6 +2866,10 @@
                 // reveal/leave the other activities in their original task
                 stack.moveActivityToStack(r);
             }
+
+            // Reset the state that indicates it can enter PiP while pausing after we've moved it
+            // to the pinned stack
+            r.supportsPictureInPictureWhilePausing = false;
         } finally {
             mWindowManager.continueSurfaceLayout();
         }
@@ -2817,7 +2879,7 @@
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
 
-        mWindowManager.animateResizePinnedStack(bounds, -1);
+        stack.animateResizePinnedStack(bounds, -1);
         mService.mTaskChangeNotificationController.notifyActivityPinned();
     }
 
@@ -3154,6 +3216,17 @@
         }
     }
 
+    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+            final int topStackNdx = stacks.size() - 1;
+            for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                stack.addStartingWindowsForVisibleActivities(taskSwitch);
+            }
+        }
+    }
+
     void invalidateTaskLayers() {
         mTaskLayersChanged = true;
     }
@@ -3271,7 +3344,7 @@
                 stack.switchUserLocked(userId);
                 TaskRecord task = stack.topTask();
                 if (task != null) {
-                    task.moveWindowContainerToTop(true /* includingParents */);
+                    stack.positionChildWindowContainerAtTop(task);
                 }
             }
         }
@@ -3709,7 +3782,7 @@
     private StackInfo getStackInfoLocked(ActivityStack stack) {
         final ActivityDisplay display = mActivityDisplays.get(DEFAULT_DISPLAY);
         StackInfo info = new StackInfo();
-        mWindowManager.getStackBounds(stack.mStackId, info.bounds);
+        stack.getWindowContainerBounds(info.bounds);
         info.displayId = DEFAULT_DISPLAY;
         info.stackId = stack.mStackId;
         info.userId = stack.mCurrentUser;
@@ -3809,14 +3882,14 @@
         }
 
         final ActivityRecord topActivity = task.getTopActivity();
-        if (!task.canGoInDockedStack() || forceNonResizable) {
+        if (!task.supportsSplitScreen() || forceNonResizable) {
             // Display a warning toast that we tried to put a non-dockable task in the docked stack.
             mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
 
             // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
             // we need to move it to top of fullscreen stack, otherwise it will be covered.
             moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
-        } else if (topActivity != null && topActivity.isNonResizableOrForced()
+        } else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
                 && !topActivity.noDisplay) {
             String packageName = topActivity.appInfo.packageName;
             mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
@@ -4235,7 +4308,7 @@
                 FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION;
         final int mStackId;
         IActivityContainerCallback mCallback = null;
-        final ActivityStack mStack;
+        ActivityStack mStack;
         ActivityRecord mParentActivity = null;
         String mIdString;
 
@@ -4249,10 +4322,11 @@
         final static int CONTAINER_STATE_FINISHING = 2;
         int mContainerState = CONTAINER_STATE_HAS_SURFACE;
 
-        ActivityContainer(int stackId) {
+        ActivityContainer(int stackId, ActivityDisplay activityDisplay, boolean onTop) {
             synchronized (mService) {
                 mStackId = stackId;
-                mStack = new ActivityStack(this, mRecentTasks);
+                mActivityDisplay = activityDisplay;
+                new ActivityStack(this, mRecentTasks, onTop);
                 mIdString = "ActivtyContainer{" + mStackId + "}";
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this);
             }
@@ -4260,21 +4334,18 @@
 
         /**
          * Adds the stack to specified display. Also calls WindowManager to do the same from
-         * {@link ActivityStack#addToDisplay(ActivityDisplay, boolean)}.
+         * {@link ActivityStack#reparent(ActivityDisplay, boolean)}.
          * @param activityDisplay The display to add the stack to.
-         * @param onTop If true the stack will be place at the top of the display, else at the
-         *              bottom.
          */
-        void addToDisplayLocked(ActivityDisplay activityDisplay, boolean onTop) {
+        void addToDisplayLocked(ActivityDisplay activityDisplay) {
             if (DEBUG_STACK) Slog.d(TAG_STACK, "addToDisplayLocked: " + this
-                    + " to display=" + activityDisplay + " onTop=" + onTop);
+                    + " to display=" + activityDisplay);
             if (mActivityDisplay != null) {
                 throw new IllegalStateException("ActivityContainer is already attached, " +
                         "displayId=" + mActivityDisplay.mDisplayId);
             }
             mActivityDisplay = activityDisplay;
-            mStack.addToDisplay(activityDisplay, onTop);
-            activityDisplay.attachActivities(mStack, onTop);
+            mStack.reparent(activityDisplay, true /* onTop */);
         }
 
         @Override
@@ -4284,7 +4355,7 @@
                 if (activityDisplay == null) {
                     return;
                 }
-                addToDisplayLocked(activityDisplay, true /* onTop */);
+                addToDisplayLocked(activityDisplay);
             }
         }
 
@@ -4361,7 +4432,7 @@
             if (DEBUG_STACK) Slog.d(TAG_STACK, "removeFromDisplayLocked: " + this
                     + " current displayId=" + mActivityDisplay.mDisplayId);
 
-            mActivityDisplay.detachActivitiesLocked(mStack);
+            mActivityDisplay.detachStack(mStack);
             mActivityDisplay = null;
         }
 
@@ -4377,8 +4448,7 @@
             removeFromDisplayLocked();
 
             mActivityDisplay = activityDisplay;
-            mStack.moveToDisplay(activityDisplay);
-            activityDisplay.attachActivities(mStack, ON_TOP);
+            mStack.reparent(activityDisplay, ON_TOP);
         }
 
         @Override
@@ -4468,7 +4538,8 @@
         boolean mDrawn = false;
 
         VirtualActivityContainer(ActivityRecord parent, IActivityContainerCallback callback) {
-            super(getNextStackId());
+            super(getNextStackId(), parent.getStack().mActivityContainer.mActivityDisplay,
+                    true /* onTop */);
             mParentActivity = parent;
             mCallback = callback;
             mContainerState = CONTAINER_STATE_NO_SURFACE;
@@ -4500,7 +4571,7 @@
                         new VirtualActivityDisplay(width, height, density);
                 mActivityDisplay = virtualActivityDisplay;
                 mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay);
-                addToDisplayLocked(virtualActivityDisplay, true /* onTop */);
+                addToDisplayLocked(virtualActivityDisplay);
             }
 
             if (mSurface != null) {
@@ -4568,6 +4639,9 @@
 
         ActivityRecord mVisibleBehindActivity;
 
+        /** Array of all UIDs that are present on the display. */
+        private IntArray mDisplayAccessUIDs = new IntArray();
+
         ActivityDisplay() {
         }
 
@@ -4586,10 +4660,9 @@
             mDisplayId = display.getDisplayId();
         }
 
-        void attachActivities(ActivityStack stack, boolean onTop) {
-            if (DEBUG_STACK) Slog.v(TAG_STACK,
-                    "attachActivities: attaching " + stack + " to displayId=" + mDisplayId
-                    + " onTop=" + onTop);
+        void attachStack(ActivityStack stack, boolean onTop) {
+            if (DEBUG_STACK) Slog.v(TAG_STACK, "attachStack: attaching " + stack
+                    + " to displayId=" + mDisplayId + " onTop=" + onTop);
             if (onTop) {
                 mStacks.add(stack);
             } else {
@@ -4597,8 +4670,8 @@
             }
         }
 
-        void detachActivitiesLocked(ActivityStack stack) {
-            if (DEBUG_STACK) Slog.v(TAG_STACK, "detachActivitiesLocked: detaching " + stack
+        void detachStack(ActivityStack stack) {
+            if (DEBUG_STACK) Slog.v(TAG_STACK, "detachStack: detaching " + stack
                     + " from displayId=" + mDisplayId);
             mStacks.remove(stack);
         }
@@ -4630,6 +4703,28 @@
         protected ConfigurationContainer getParent() {
             return ActivityStackSupervisor.this;
         }
+
+        boolean isPrivate() {
+            return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
+        }
+
+        boolean isUidPresent(int uid) {
+            for (ActivityStack stack : mStacks) {
+                if (stack.isUidPresent(uid)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /** Update and get all UIDs that are present on the display and have access to it. */
+        private IntArray getPresentUIDs() {
+            mDisplayAccessUIDs.clear();
+            for (ActivityStack stack : mStacks) {
+                stack.getPresentUIDs(mDisplayAccessUIDs);
+            }
+            return mDisplayAccessUIDs;
+        }
     }
 
     class VirtualActivityDisplay extends ActivityDisplay {
@@ -4654,8 +4749,8 @@
         }
 
         @Override
-        void detachActivitiesLocked(ActivityStack stack) {
-            super.detachActivitiesLocked(stack);
+        void detachStack(ActivityStack stack) {
+            super.detachStack(stack);
             if (mVirtualDisplay != null) {
                 mVirtualDisplay.release();
                 mVirtualDisplay = null;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 2634385..f7eb5d5 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -460,6 +460,7 @@
             final String splitName = rInfo.ephemeralResponse.splitName;
             final boolean needsPhaseTwo = rInfo.ephemeralResponse.needsPhase2;
             final String token = rInfo.ephemeralResponse.token;
+            final int versionCode = rInfo.ephemeralResponse.resolveInfo.getVersionCode();
             if (needsPhaseTwo) {
                 // request phase two resolution
                 mService.getPackageManagerInternalLocked().requestEphemeralResolutionPhaseTwo(
@@ -467,8 +468,8 @@
                         callingPackage, userId);
             }
             intent = EphemeralResolver.buildEphemeralInstallerIntent(intent, ephemeralIntent,
-                    callingPackage, resolvedType, userId, packageName, splitName, token,
-                    needsPhaseTwo);
+                    callingPackage, resolvedType, userId, packageName, splitName, versionCode,
+                    token, needsPhaseTwo);
             resolvedType = null;
             callingUid = realCallingUid;
             callingPid = realCallingPid;
@@ -476,10 +477,10 @@
             aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
         }
 
-        ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
-                intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord,
-                resultWho, requestCode, componentSpecified, voiceSession != null, mSupervisor,
-                container, options, sourceRecord);
+        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
+                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
+                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
+                mSupervisor, container, options, sourceRecord);
         if (outActivity != null) {
             outActivity[0] = r;
         }
@@ -582,7 +583,10 @@
             // The activity was already running in the pinned stack so it wasn't started, but either
             // brought to the front or the new intent was delivered to it since it was already in
             // front. Notify anyone interested in this piece of information.
-            mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt();
+            final ComponentName sourceComponent = sourceRecord == null ? null :
+                    sourceRecord.realActivity;
+            mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
+                    sourceComponent);
             return;
         }
     }
@@ -644,18 +648,18 @@
                     null);
             credential.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
             // Show confirm credentials activity.
-            startConfirmCredentialIntent(credential);
+            startConfirmCredentialIntent(credential, null);
         }
     }
 
-    void startConfirmCredentialIntent(Intent intent) {
+    void startConfirmCredentialIntent(Intent intent, Bundle optionsBundle) {
         intent.addFlags(FLAG_ACTIVITY_NEW_TASK |
                 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
                 FLAG_ACTIVITY_TASK_ON_HOME);
-        final ActivityOptions options = ActivityOptions.makeBasic();
+        ActivityOptions options = (optionsBundle != null ? new ActivityOptions(optionsBundle)
+                        : ActivityOptions.makeBasic());
         options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
-        mService.mContext.startActivityAsUser(intent, options.toBundle(),
-                UserHandle.CURRENT);
+        mService.mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
     }
 
     final int startActivityMayWait(IApplicationThread caller, int callingUid,
@@ -1128,7 +1132,8 @@
 
         mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
-
+        mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
+                mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
         if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {
             mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
         }
@@ -1830,7 +1835,7 @@
                 mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
                 mIntent, null, null, true, mStartActivity.mActivityType);
         mStartActivity.setTask(task, null);
-        mStartActivity.task.moveWindowContainerToTop(true /* includingParents */);
+        mStartActivity.task.getStack().positionChildWindowContainerAtTop(mStartActivity.task);
         if (DEBUG_TASKS) Slog.v(TAG_TASKS,
                 "Starting new activity " + mStartActivity + " in new guessed " + mStartActivity.task);
     }
@@ -1896,16 +1901,14 @@
 
         final ActivityStack currentStack = task != null ? task.getStack() : null;
         if (currentStack != null) {
-            if (currentStack.isOnHomeDisplay()) {
-                if (mSupervisor.mFocusedStack != currentStack) {
-                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                            "computeStackFocus: Setting " + "focused stack to r=" + r
-                                    + " task=" + task);
-                } else {
-                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                            "computeStackFocus: Focused stack already="
-                                    + mSupervisor.mFocusedStack);
-                }
+            if (mSupervisor.mFocusedStack != currentStack) {
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Setting " + "focused stack to r=" + r
+                                + " task=" + task);
+            } else {
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+                        "computeStackFocus: Focused stack already="
+                                + mSupervisor.mFocusedStack);
             }
             return currentStack;
         }
@@ -1922,13 +1925,7 @@
         // Same also applies to dynamic stacks, as they behave similar to fullscreen stack.
         // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
         // we can also put it in the focused stack.
-        final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
-        final boolean canUseFocusedStack = focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
-                || (focusedStackId == DOCKED_STACK_ID && r.canGoInDockedStack())
-                || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeableOrForced())
-                || isDynamicStack(focusedStackId);
-        if (canUseFocusedStack && (!newTask
-                || mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
+        if (canLaunchIntoFocusedStack(r, newTask)) {
             if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                     "computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
             return mSupervisor.mFocusedStack;
@@ -1955,6 +1952,36 @@
         return stack;
     }
 
+    /** Check if provided activity record can launch in currently focused stack. */
+    private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
+        // The fullscreen stack can contain any task regardless of if the task is resizeable
+        // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+        // Same also applies to dynamic stacks, as they behave similar to fullscreen stack.
+        // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
+        // we can also put it in the focused stack.
+        final ActivityStack focusedStack = mSupervisor.mFocusedStack;
+        final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
+        final boolean canUseFocusedStack;
+        switch (focusedStackId) {
+            case FULLSCREEN_WORKSPACE_STACK_ID:
+                canUseFocusedStack = true;
+                break;
+            case DOCKED_STACK_ID:
+                canUseFocusedStack = r.supportsSplitScreen();
+                break;
+            case FREEFORM_WORKSPACE_STACK_ID:
+                canUseFocusedStack = r.supportsFreeform();
+                break;
+            default:
+                canUseFocusedStack = isDynamicStack(focusedStackId)
+                        && mSupervisor.isCallerAllowedToLaunchOnDisplay(r.launchedFromPid,
+                        r.launchedFromUid, focusedStack.mDisplayId);
+        }
+
+        return canUseFocusedStack
+                && (!newTask || focusedStack.mActivityContainer.isEligibleForNewTasks());
+    }
+
     private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
             ActivityOptions aOptions) {
 
@@ -2030,29 +2057,24 @@
     }
 
     boolean isValidLaunchStackId(int stackId, ActivityRecord r) {
-        if (stackId == INVALID_STACK_ID || stackId == HOME_STACK_ID) {
-            return false;
+        switch (stackId) {
+            case INVALID_STACK_ID:
+            case HOME_STACK_ID:
+                return false;
+            case FULLSCREEN_WORKSPACE_STACK_ID:
+                return true;
+            case FREEFORM_WORKSPACE_STACK_ID:
+                return r.supportsFreeform();
+            case DOCKED_STACK_ID:
+                return r.supportsSplitScreen();
+            case PINNED_STACK_ID:
+                return r.supportsPictureInPicture();
+            case RECENTS_STACK_ID:
+                return r.isRecentsActivity();
+            default:
+                Slog.e(TAG, "isValidLaunchStackId: Unexpected stackId=" + stackId);
+                return false;
         }
-
-        if (stackId != FULLSCREEN_WORKSPACE_STACK_ID
-                && (!mService.mSupportsMultiWindow || !r.isResizeableOrForced())) {
-            return false;
-        }
-
-        if (stackId == DOCKED_STACK_ID && r.canGoInDockedStack()) {
-            return true;
-        }
-
-        if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
-            return false;
-        }
-
-        final boolean supportsPip = mService.mSupportsPictureInPicture
-                && (r.supportsPictureInPicture() || mService.mForceResizableActivities);
-        if (stackId == PINNED_STACK_ID && !supportsPip) {
-            return false;
-        }
-        return true;
     }
 
     Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) {
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 029b5dd..384f2f8 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -20,6 +20,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.os.ProcessCpuTracker;
+import com.android.server.RescueParty;
 import com.android.server.Watchdog;
 
 import android.app.ActivityManager;
@@ -258,7 +259,16 @@
         }
     }
 
-    void scheduleAppCrashLocked(int uid, int initialPid, String packageName,
+    /**
+     * Induce a crash in the given app.
+     *
+     * @param uid if nonnegative, the required matching uid of the target to crash
+     * @param initialPid fast-path match for the target to crash
+     * @param packageName fallback match if the stated pid is not found or doesn't match uid
+     * @param userId If nonnegative, required to identify a match by package name
+     * @param message
+     */
+    void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId,
             String message) {
         ProcessRecord proc = null;
 
@@ -269,14 +279,15 @@
         synchronized (mService.mPidsSelfLocked) {
             for (int i=0; i<mService.mPidsSelfLocked.size(); i++) {
                 ProcessRecord p = mService.mPidsSelfLocked.valueAt(i);
-                if (p.uid != uid) {
+                if (uid >= 0 && p.uid != uid) {
                     continue;
                 }
                 if (p.pid == initialPid) {
                     proc = p;
                     break;
                 }
-                if (p.pkgList.containsKey(packageName)) {
+                if (p.pkgList.containsKey(packageName)
+                        && (userId < 0 || p.userId == userId)) {
                     proc = p;
                 }
             }
@@ -285,7 +296,8 @@
         if (proc == null) {
             Slog.w(TAG, "crashApplication: nothing for uid=" + uid
                     + " initialPid=" + initialPid
-                    + " packageName=" + packageName);
+                    + " packageName=" + packageName
+                    + " userId=" + userId);
             return;
         }
 
@@ -323,6 +335,12 @@
             longMsg = shortMsg;
         }
 
+        // If a persistent app is stuck in a crash loop, the device isn't very
+        // usable, so we want to consider sending out a rescue party.
+        if (r != null && r.persistent) {
+            RescueParty.notePersistentAppCrash(mContext, r.uid);
+        }
+
         AppErrorResult result = new AppErrorResult();
         TaskRecord task;
         synchronized (mService) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8104a43..ee2467a 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -592,22 +592,6 @@
                     + " (uid " + r.callingUid + ")");
             skip = true;
         }
-        if (!skip) {
-            final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid,
-                    filter.packageName, -1, false);
-            if (false && allowed == ActivityManager.APP_START_MODE_DISABLED) {
-                // XXX should we really not allow this?  It means that while we are
-                // keeping an ephemeral app cached, its registered receivers will stop
-                // receiving broadcasts after it goes idle...  so if it comes back to
-                // the foreground, it won't know what the current state of those broadcasts is.
-                Slog.w(TAG, "Background execution not allowed: receiving "
-                        + r.intent
-                        + " to " + filter.receiverList.app
-                        + " (pid=" + filter.receiverList.pid
-                        + ", uid=" + filter.receiverList.uid + ")");
-                skip = true;
-            }
-        }
 
         if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
                 r.callingPid, r.resolvedType, filter.receiverList.uid)) {
@@ -1156,14 +1140,14 @@
                     info.activityInfo.applicationInfo.uid, false);
 
             if (!skip) {
-                final int allowed = mService.checkAllowBackgroundLocked(
-                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1,
-                        true);
+                final int allowed = mService.getAppStartModeLocked(
+                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
+                        info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false);
                 if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                     // We won't allow this receiver to be launched if the app has been
                     // completely disabled from launches, or it was not explicitly sent
                     // to it and the app is in a state that should not receive it
-                    // (depending on how checkAllowBackgroundLocked has determined that).
+                    // (depending on how getAppStartModeLocked has determined that).
                     if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
                         Slog.w(TAG, "Background execution disabled: receiving "
                                 + r.intent + " to "
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index cfe2eb0..b0a4746 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -29,6 +29,7 @@
 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE;
 import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static com.android.server.wm.AppTransition.TRANSIT_NONE;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
 
 import android.os.IBinder;
@@ -120,6 +121,7 @@
 
                 // Some stack visibility might change (e.g. docked stack)
                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
                 mWindowManager.executeAppTransition();
             } finally {
                 mWindowManager.continueSurfaceLayout();
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index e2870d8..9348023 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -20,7 +20,6 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructTimeval;
-import android.system.StructUcred;
 import android.system.UnixSocketAddress;
 import android.util.Slog;
 
@@ -105,9 +104,9 @@
 
         if (DEBUG) Slog.i(TAG, "Starting up");
 
-        // The file system entity for this socket is created with 0700 perms, owned
-        // by system:system.  debuggerd runs as root, so is capable of connecting to
-        // it, but 3rd party apps cannot.
+        // The file system entity for this socket is created with 0777 perms, owned
+        // by system:system. selinux restricts things so that only crash_dump can
+        // access it.
         {
             File socketFile = new File(DEBUGGERD_SOCKET_PATH);
             if (socketFile.exists()) {
@@ -121,6 +120,7 @@
                     DEBUGGERD_SOCKET_PATH);
             Os.bind(serverFd, sockAddr);
             Os.listen(serverFd, 1);
+            Os.chmod(DEBUGGERD_SOCKET_PATH, 0777);
 
             while (true) {
                 FileDescriptor peerFd = null;
@@ -129,19 +129,14 @@
                     peerFd = Os.accept(serverFd, null /* peerAddress */);
                     if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
                     if (peerFd != null) {
-                        // Only the superuser is allowed to talk to us over this socket
-                        StructUcred credentials =
-                                Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
-                        if (credentials.uid == 0) {
-                            // the reporting thread may take responsibility for
-                            // acking the debugger; make sure we play along.
-                            consumeNativeCrashData(peerFd);
-                        }
+                        // the reporting thread may take responsibility for
+                        // acking the debugger; make sure we play along.
+                        consumeNativeCrashData(peerFd);
                     }
                 } catch (Exception e) {
                     Slog.w(TAG, "Error handling connection", e);
                 } finally {
-                    // Always ack debuggerd's connection to us.  The actual
+                    // Always ack crash_dump's connection to us.  The actual
                     // byte written is irrelevant.
                     if (peerFd != null) {
                         try {
@@ -194,7 +189,7 @@
         return totalRead;
     }
 
-    // Read the crash report from the debuggerd connection
+    // Read a crash report from the connection
     void consumeNativeCrashData(FileDescriptor fd) {
         if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
         final byte[] buf = new byte[4096];
@@ -205,6 +200,10 @@
             Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
             Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
 
+            // The socket is guarded by an selinux neverallow rule that only
+            // permits crash_dump to connect to it. This allows us to trust the
+            // received values.
+
             // first, the pid and signal number
             int headerBytes = readExactly(fd, buf, 0, 8);
             if (headerBytes != 8) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1322ecf..40effff 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -138,31 +138,9 @@
     // without empty apps being able to push them out of memory.
     static final int MIN_CACHED_APPS = 2;
 
-    // The maximum number of cached processes we will keep around before killing them.
-    // NOTE: this constant is *only* a control to not let us go too crazy with
-    // keeping around processes on devices with large amounts of RAM.  For devices that
-    // are tighter on RAM, the out of memory killer is responsible for killing background
-    // processes as RAM is needed, and we should *never* be relying on this limit to
-    // kill them.  Also note that this limit only applies to cached background processes;
-    // we have no limit on the number of service, visible, foreground, or other such
-    // processes and the number of those processes does not count against the cached
-    // process limit.
-    static final int MAX_CACHED_APPS = 32;
-
     // We allow empty processes to stick around for at most 30 minutes.
     static final long MAX_EMPTY_TIME = 30*60*1000;
 
-    // The maximum number of empty app processes we will let sit around.
-    private static final int MAX_EMPTY_APPS = computeEmptyProcessLimit(MAX_CACHED_APPS);
-
-    // The number of empty apps at which we don't consider it necessary to do
-    // memory trimming.
-    static final int TRIM_EMPTY_APPS = MAX_EMPTY_APPS/2;
-
-    // The number of cached at which we don't consider it necessary to do
-    // memory trimming.
-    static final int TRIM_CACHED_APPS = (MAX_CACHED_APPS-MAX_EMPTY_APPS)/3;
-
     // Threshold of number of cached+empty where we consider memory critical.
     static final int TRIM_CRITICAL_THRESHOLD = 3;
 
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 71c7fd3..82b00da 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -534,7 +534,7 @@
                         // get to be foreground.
                         ams.setServiceForeground(name, ServiceRecord.this,
                                 0, null, 0);
-                        ams.crashApplication(appUid, appPid, localPackageName,
+                        ams.crashApplication(appUid, appPid, localPackageName, -1,
                                 "Bad notification for startForeground: " + e);
                     }
                 }
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index cb20eac..2990dff 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
 import android.app.ITaskStackListener;
 import android.app.ActivityManager.TaskDescription;
 import android.content.ComponentName;
@@ -43,6 +45,7 @@
     static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12;
     static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
     static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
+    static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
 
     // Delay in notifying task stack change listeners (in millis)
     static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -94,7 +97,7 @@
     };
 
     private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
-        l.onPinnedActivityRestartAttempt();
+        l.onPinnedActivityRestartAttempt((ComponentName) m.obj);
     };
 
     private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
@@ -113,6 +116,10 @@
         l.onTaskProfileLocked(m.arg1, m.arg2);
     };
 
+    private final TaskStackConsumer mNotifyTaskSnapshotChanged = (l, m) -> {
+        l.onTaskSnapshotChanged(m.arg1, (TaskSnapshot) m.obj);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -170,7 +177,9 @@
                     break;
                 case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyTaskProfileLocked, msg);
-
+                    break;
+                case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
                     break;
             }
         }
@@ -258,10 +267,11 @@
      * running in the pinned stack and the activity was not actually started, but the task is
      * either brought to the front or a new Intent is delivered to it.
      */
-    void notifyPinnedActivityRestartAttempt() {
+    void notifyPinnedActivityRestartAttempt(ComponentName sourceComponent) {
         mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
         final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+                mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
+                        sourceComponent);
         forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
         msg.sendToTarget();
     }
@@ -348,4 +358,14 @@
         forAllLocalListeners(mNotifyTaskProfileLocked, msg);
         msg.sendToTarget();
     }
+
+    /**
+     * Notify listeners that the snapshot of a task has changed.
+     */
+    void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG,
+                taskId, 0, snapshot);
+        forAllLocalListeners(mNotifyTaskSnapshotChanged, msg);
+        msg.sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 7a62f2c..9dde39e 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -651,6 +651,8 @@
                                         "omitting from persistentTaskIds task=" + task);
                             }
                         }
+                        mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
+                                mRecentTasks.usersWithRecentsLoadedLocked());
                     }
                     removeObsoleteFiles(persistentTaskIds);
                 }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4c4c444..f12d7b7 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -52,6 +52,8 @@
 import com.android.internal.util.XmlUtils;
 
 import com.android.server.wm.TaskWindowContainerController;
+import com.android.server.wm.TaskWindowContainerListener;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -83,6 +85,7 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
@@ -105,7 +108,7 @@
 import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 
-final class TaskRecord extends ConfigurationContainer {
+final class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -139,6 +142,7 @@
     private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
     private static final String ATTR_CALLING_UID = "calling_uid";
     private static final String ATTR_CALLING_PACKAGE = "calling_package";
+    private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
     private static final String ATTR_RESIZE_MODE = "resize_mode";
     private static final String ATTR_PRIVILEGED = "privileged";
     private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
@@ -186,6 +190,9 @@
 
     int mResizeMode;        // The resize mode of this task and its activities.
                             // Based on the {@link ActivityInfo#resizeMode} of the root activity.
+    boolean mSupportsPictureInPicture;  // Whether or not this task and its activities support PiP.
+            // Based on the {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag of the root
+            // activity.
     boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize
                                      // changes on a temporary basis.
     private int mLockTaskMode;  // Which tasklock mode to launch this task in. One of
@@ -354,8 +361,9 @@
             boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
             TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
             int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-            int resizeMode, boolean privileged, boolean _realActivitySuspended,
-            boolean userSetupComplete, int minWidth, int minHeight) {
+            int resizeMode, boolean supportsPictureInPicture, boolean privileged,
+            boolean _realActivitySuspended, boolean userSetupComplete, int minWidth,
+            int minHeight) {
         mService = service;
         mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
                 TaskPersister.IMAGE_EXTENSION;
@@ -394,6 +402,7 @@
         mCallingUid = callingUid;
         mCallingPackage = callingPackage;
         mResizeMode = resizeMode;
+        mSupportsPictureInPicture = supportsPictureInPicture;
         mPrivileged = privileged;
         mMinWidth = minWidth;
         mMinHeight = minHeight;
@@ -412,9 +421,10 @@
 
         final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
         final Configuration overrideConfig = getOverrideConfiguration();
-        mWindowContainerController = new TaskWindowContainerController(taskId, getStackId(), userId,
-                bounds, overrideConfig, mResizeMode, isHomeTask(), isOnTopLauncher(), onTop,
-                showForAllUsers);
+        mWindowContainerController = new TaskWindowContainerController(taskId, this,
+                getStack().getWindowContainerController(), userId, bounds, overrideConfig,
+                mResizeMode, mSupportsPictureInPicture, isHomeTask(), isOnTopLauncher(),
+                onTop, showForAllUsers, lastTaskDescription);
     }
 
     void removeWindowContainer() {
@@ -429,6 +439,11 @@
         mWindowContainerController = null;
     }
 
+    @Override
+    public void onSnapshotChanged(TaskSnapshot snapshot) {
+        mService.mTaskChangeNotificationController.notifyTaskSnapshotChanged(taskId, snapshot);
+    }
+
     void setResizeMode(int resizeMode) {
         if (mResizeMode == resizeMode) {
             return;
@@ -443,6 +458,12 @@
         mWindowContainerController.setTaskDockedResizing(resizing);
     }
 
+    // TODO: Consolidate this with the resize() method below.
+    @Override
+    public void requestResize(Rect bounds, int resizeMode) {
+        mService.resizeTask(taskId, bounds, resizeMode);
+    }
+
     boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) {
         if (!isResizeable()) {
             Slog.w(TAG, "resizeTask: task " + this + " not resizeable.");
@@ -513,25 +534,6 @@
                 false /* forced */);
     }
 
-    // TODO: Remove once we have a stack controller.
-    void positionWindowContainerAt(int position) {
-        mWindowContainerController.positionAt(position, mBounds, getOverrideConfiguration());
-    }
-
-    // TODO: Replace with moveChildToTop?
-    void moveWindowContainerToTop(boolean includingParents) {
-        if (mWindowContainerController != null) {
-            mWindowContainerController.moveToTop(includingParents);
-        }
-    }
-
-    // TODO: Replace with moveChildToBottom?
-    void moveWindowContainerToBottom() {
-        if (mWindowContainerController != null) {
-            mWindowContainerController.moveToBottom();
-        }
-    }
-
     void getWindowContainerBounds(Rect bounds) {
         mWindowContainerController.getBounds(bounds);
     }
@@ -549,7 +551,7 @@
 
             // Must reparent first in window manager to avoid a situation where AM can delete the
             // we are coming from in WM before we reparent because it became empty.
-            mWindowContainerController.reparent(stackId, position);
+            mWindowContainerController.reparent(newStack.getWindowContainerController(), position);
 
             final ActivityStack prevStack = mStack;
             prevStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING);
@@ -576,11 +578,14 @@
         mWindowContainerController.cancelThumbnailTransition();
     }
 
-    public TaskSnapshot getSnapshot() {
-        if (mWindowContainerController == null) {
-            return null;
-        }
-        return mWindowContainerController.getSnapshot();
+    /**
+     * DO NOT HOLD THE ACTIVITY MANAGER LOCK WHEN CALLING THIS METHOD!
+     */
+    TaskSnapshot getSnapshot() {
+
+        // TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
+        // synchronized between AM and WM.
+        return mService.mWindowManager.getTaskSnapshot(taskId, userId);
     }
 
     void touchActiveTime() {
@@ -681,6 +686,7 @@
             autoRemoveRecents = false;
         }
         mResizeMode = info.resizeMode;
+        mSupportsPictureInPicture = info.supportsPictureInPicture();
         mIsOnTopLauncher = (info.flags & FLAG_ON_TOP_LAUNCHER) != 0;
         mLockTaskMode = info.lockTaskLaunchMode;
         mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
@@ -734,8 +740,14 @@
         return mStack;
     }
 
-    /** Must be used for setting parent stack because it performs configuration updates. */
+    /**
+     * Must be used for setting parent stack because it performs configuration updates.
+     * Must be called after adding task as a child to the stack.
+     */
     void setStack(ActivityStack stack) {
+        if (stack != null && !stack.isInStackLocked(this)) {
+            throw new IllegalStateException("Task must be added as a Stack child first.");
+        }
         mStack = stack;
         onParentChanged();
     }
@@ -762,6 +774,12 @@
         return mStack;
     }
 
+    @Override
+    void onParentChanged() {
+        super.onParentChanged();
+        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+    }
+
     // Close up recents linked list.
     void closeRecentsChain() {
         if (mPrevAffiliate != null) {
@@ -781,6 +799,9 @@
             inRecents = false;
             mService.notifyTaskPersisterLocked(this, false);
         }
+
+        // TODO: Use window container controller once tasks are better synced between AM and WM
+        mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);
     }
 
     void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
@@ -1276,9 +1297,21 @@
         return mTaskToReturnTo == HOME_ACTIVITY_TYPE;
     }
 
+    private boolean isResizeable(boolean checkSupportsPip) {
+        return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
+                || (checkSupportsPip && mSupportsPictureInPicture)) && !mTemporarilyUnresizable;
+    }
+
     boolean isResizeable() {
-        return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode))
-                && !mTemporarilyUnresizable;
+        return isResizeable(true /* checkSupportsPip */);
+    }
+
+    boolean supportsSplitScreen() {
+        // A task can not be docked even if it is considered resizeable because it only supports
+        // picture-in-picture mode but has a non-resizeable resizeMode
+        return mService.mSupportsSplitScreenMultiWindow
+                && isResizeable(false /* checkSupportsPip */)
+                && !ActivityInfo.isPreserveOrientationMode(mResizeMode);
     }
 
     /**
@@ -1304,11 +1337,6 @@
         return isHomeTask() && mIsOnTopLauncher;
     }
 
-    boolean canGoInDockedStack() {
-        return isResizeable() && mService.mSupportsSplitScreenMultiWindow &&
-                !ActivityInfo.isPreserveOrientationMode(mResizeMode);
-    }
-
     /**
      * Find the activity in the history stack within the given task.  Returns
      * the index within the history at which it's found, or < 0 if not found.
@@ -1377,6 +1405,9 @@
             }
             lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
                     colorBackground);
+            if (mWindowContainerController != null) {
+                mWindowContainerController.setTaskDescription(lastTaskDescription);
+            }
             // Update the task affiliation color if we are the parent of the group
             if (taskId == mAffiliatedTaskId) {
                 mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
@@ -1454,6 +1485,8 @@
         out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
         out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
         out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
+        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
+                String.valueOf(mSupportsPictureInPicture));
         out.attribute(null, ATTR_PRIVILEGED, String.valueOf(mPrivileged));
         if (mLastNonFullscreenBounds != null) {
             out.attribute(
@@ -1524,6 +1557,7 @@
         int callingUid = -1;
         String callingPackage = "";
         int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        boolean supportsPictureInPicture = false;
         boolean privileged = false;
         Rect bounds = null;
         int minWidth = INVALID_MIN_SIZE;
@@ -1590,6 +1624,8 @@
                 callingPackage = attrValue;
             } else if (ATTR_RESIZE_MODE.equals(attrName)) {
                 resizeMode = Integer.parseInt(attrValue);
+            } else if (ATTR_SUPPORTS_PICTURE_IN_PICTURE.equals(attrName)) {
+                supportsPictureInPicture = Boolean.parseBoolean(attrValue);
             } else if (ATTR_PRIVILEGED.equals(attrName)) {
                 privileged = Boolean.parseBoolean(attrValue);
             } else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
@@ -1662,6 +1698,15 @@
             if (taskType == HOME_ACTIVITY_TYPE && resizeMode == RESIZE_MODE_RESIZEABLE) {
                 resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
             }
+        } else {
+            // This activity has previously marked itself explicitly as both resizeable and
+            // supporting picture-in-picture.  Since there is no longer a requirement for
+            // picture-in-picture activities to be resizeable, we can mark this simply as
+            // resizeable and supporting picture-in-picture separately.
+            if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
+                resizeMode = RESIZE_MODE_RESIZEABLE;
+                supportsPictureInPicture = true;
+            }
         }
 
         final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
@@ -1669,8 +1714,9 @@
                 autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
                 activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
                 taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
-                taskAffiliationColor, callingUid, callingPackage, resizeMode, privileged,
-                realActivitySuspended, userSetupComplete, minWidth, minHeight);
+                taskAffiliationColor, callingUid, callingPackage, resizeMode,
+                supportsPictureInPicture, privileged, realActivitySuspended, userSetupComplete,
+                minWidth, minHeight);
         task.updateOverrideConfiguration(bounds);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
@@ -1956,6 +2002,15 @@
         return rootAffinity != null && getStackId() != PINNED_STACK_ID;
     }
 
+    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+            final ActivityRecord r = mActivities.get(activityNdx);
+            if (r.visible) {
+                r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
+            }
+        }
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("userId="); pw.print(userId);
                 pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
@@ -2047,6 +2102,7 @@
         pw.print(prefix); pw.print("stackId="); pw.println(getStackId());
         pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
                 pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
+                pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
                 pw.print(" isResizeable=" + isResizeable());
                 pw.print(" firstActiveTime=" + lastActiveTime);
                 pw.print(" lastActiveTime=" + lastActiveTime);
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index d1a15bd..64e3417 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -26,10 +26,13 @@
  */
 public final class UidRecord {
     final int uid;
+    final boolean persistent;
     int curProcState;
     int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
     long lastBackgroundTime;
     boolean ephemeral;
+    boolean curWhitelist;
+    boolean setWhitelist;
     boolean idle;
     int numProcs;
 
@@ -49,8 +52,9 @@
 
     ChangeItem pendingChange;
 
-    public UidRecord(int _uid) {
+    public UidRecord(int _uid, boolean _persist) {
         uid = _uid;
+        persistent = _persist;
         reset();
     }
 
@@ -69,6 +73,9 @@
         if (ephemeral) {
             sb.append(" ephemeral");
         }
+        if (curWhitelist) {
+            sb.append(" whitelist");
+        }
         if (lastBackgroundTime > 0) {
             sb.append(" bg:");
             TimeUtils.formatDuration(SystemClock.elapsedRealtime()-lastBackgroundTime, sb);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 71ebad9..728476a 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -47,6 +47,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.Dialog;
 import android.app.IStopUserCallback;
@@ -55,6 +56,7 @@
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.BatteryStats;
@@ -255,10 +257,17 @@
             // storage is already unlocked.
             if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
                 mInjector.getUserManagerInternal().setUserState(userId, uss.state);
-                if (!mInjector.isRuntimeRestarted() && !mInjector.isFirstBoot()) {
+                // Do not report secondary users, runtime restarts or first boot/upgrade
+                if (userId == UserHandle.USER_SYSTEM
+                        && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
                     int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
                     MetricsLogger.histogram(mInjector.getContext(),
                             "framework_locked_boot_completed", uptimeSeconds);
+                    final int MAX_UPTIME_SECONDS = 120;
+                    if (uptimeSeconds > MAX_UPTIME_SECONDS) {
+                        Slog.wtf("SystemServerTiming",
+                                "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
+                    }
                 }
 
                 mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
@@ -414,7 +423,8 @@
                 if (userId != UserHandle.USER_SYSTEM) {
                     Slog.d(TAG, "Initializing user #" + userId);
                     Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
-                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+                            | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                     mInjector.broadcastIntentLocked(intent, null,
                             new IIntentReceiver.Stub() {
                                 @Override
@@ -430,7 +440,9 @@
             }
 
             Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
-            if (!mInjector.isRuntimeRestarted() && !mInjector.isFirstBoot()) {
+            // Do not report secondary users, runtime restarts or first boot/upgrade
+            if (userId == UserHandle.USER_SYSTEM
+                    && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
                 int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
                 MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed",
                         uptimeSeconds);
@@ -1703,8 +1715,13 @@
             return mService.mSystemServiceManager.isRuntimeRestarted();
         }
 
-        boolean isFirstBoot() {
-            return mService.mSystemServiceManager.isFirstBoot();
+        boolean isFirstBootOrUpgrade() {
+            IPackageManager pm = AppGlobals.getPackageManager();
+            try {
+                return pm.isFirstBoot() || pm.isUpgrade();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         void sendPreBootBroadcast(int userId, boolean quiet, final Runnable onFinish) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0f3f9ce..31ef94f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -144,7 +144,8 @@
  */
 public class AudioService extends IAudioService.Stub
         implements AccessibilityManager.TouchExplorationStateChangeListener,
-            AccessibilityManager.AccessibilityStateChangeListener{
+            AccessibilityManager.AccessibilityStateChangeListener,
+            AccessibilityManager.AccessibilityServicesStateChangeListener {
 
     private static final String TAG = "AudioService";
 
@@ -780,7 +781,7 @@
                 TAG,
                 SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
 
-        initA11yMonitoring(mContext);
+        initA11yMonitoring();
         onIndicateSystemReady();
     }
 
@@ -5925,13 +5926,25 @@
     //==========================================================================================
     // Accessibility
 
-    private void initA11yMonitoring(Context ctxt) {
-        AccessibilityManager accessibilityManager =
-                (AccessibilityManager) ctxt.getSystemService(Context.ACCESSIBILITY_SERVICE);
+    /**
+     * Compile-time constant to enable the use of an independent a11y volume:
+     * - set to true to listen to a11y services state changes and read
+     *   the whether any exposes the FLAG_ENABLE_ACCESSIBILITY_VOLUME flag
+     * - set to false to listen to when accessibility services are started (e.g. "TalkBack started")
+     */
+    private static final boolean USE_FLAG_ENABLE_ACCESSIBILITY_VOLUME = false;
+
+    private void initA11yMonitoring() {
+        final AccessibilityManager accessibilityManager =
+                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
         updateDefaultStreamOverrideDelay(accessibilityManager.isTouchExplorationEnabled());
         updateA11yVolumeAlias(accessibilityManager.isEnabled());
         accessibilityManager.addTouchExplorationStateChangeListener(this);
-        accessibilityManager.addAccessibilityStateChangeListener(this);
+        if (USE_FLAG_ENABLE_ACCESSIBILITY_VOLUME) {
+            accessibilityManager.addAccessibilityServicesStateChangeListener(this);
+        } else {
+            accessibilityManager.addAccessibilityStateChangeListener(this);
+        }
     }
 
     //---------------------------------------------------------------------------------
@@ -5969,21 +5982,31 @@
 
     private static boolean sIndependentA11yVolume = false;
 
+    // implementation of AccessibilityStateChangeListener
     @Override
     public void onAccessibilityStateChanged(boolean enabled) {
         updateA11yVolumeAlias(enabled);
     }
 
-    private void updateA11yVolumeAlias(boolean a11Enabled) {
-        if (DEBUG_VOL) Log.d(TAG, "Accessibility mode changed to " + a11Enabled);
-        // a11y has its own volume stream when a11y service is enabled
-        sIndependentA11yVolume = a11Enabled;
-        // update the volume mapping scheme
-        updateStreamVolumeAlias(true /*updateVolumes*/, TAG);
-        // update the volume controller behavior
-        mVolumeController.setA11yMode(sIndependentA11yVolume ?
-                VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
-                    VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
+    // implementation of AccessibilityServicesStateChangeListener
+    @Override
+    public void onAccessibilityServicesStateChanged() {
+        final AccessibilityManager accessibilityManager =
+                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        updateA11yVolumeAlias(accessibilityManager.isAccessibilityVolumeStreamActive());
+    }
+
+    private void updateA11yVolumeAlias(boolean a11VolEnabled) {
+        if (DEBUG_VOL) Log.d(TAG, "Accessibility volume enabled = " + a11VolEnabled);
+        if (sIndependentA11yVolume != a11VolEnabled) {
+            sIndependentA11yVolume = a11VolEnabled;
+            // update the volume mapping scheme
+            updateStreamVolumeAlias(true /*updateVolumes*/, TAG);
+            // update the volume controller behavior
+            mVolumeController.setA11yMode(sIndependentA11yVolume ?
+                    VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
+                        VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
+        }
     }
 
     //==========================================================================================
@@ -6479,6 +6502,35 @@
         return mRecordMonitor.getActiveRecordingConfigurations();
     }
 
+    public void disableRingtoneSync() {
+        final int callingUserId = UserHandle.getCallingUserId();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            UserManager userManager = UserManager.get(mContext);
+
+            // Disable the sync setting
+            Settings.Secure.putIntForUser(mContentResolver,
+                    Settings.Secure.SYNC_PARENT_SOUNDS, 0 /* false */, callingUserId);
+
+            UserInfo parentInfo = userManager.getProfileParent(callingUserId);
+            if (parentInfo != null && parentInfo.id != callingUserId) {
+                // This is a managed profile, so we clone the ringtones from the parent profile
+                cloneRingtoneSetting(callingUserId, parentInfo.id, Settings.System.RINGTONE);
+                cloneRingtoneSetting(callingUserId, parentInfo.id,
+                        Settings.System.NOTIFICATION_SOUND);
+                cloneRingtoneSetting(callingUserId, parentInfo.id, Settings.System.ALARM_ALERT);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void cloneRingtoneSetting(int userId, int parentId, String ringtoneSetting) {
+        String parentSetting = Settings.System.getStringForUser(mContentResolver, ringtoneSetting,
+                parentId);
+        Settings.System.putStringForUser(mContentResolver, ringtoneSetting, parentSetting, userId);
+    }
+
     //======================
     // Audio playback notification
     //======================
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 8d4f0a9..5fed397 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -40,6 +40,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
@@ -50,18 +51,106 @@
 import java.util.HashSet;
 import java.util.List;
 
+import java.lang.Thread;
+import java.lang.Runnable;
+import java.lang.InterruptedException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+// The following class is Android Emulator specific. It is used to read and
+// write contents of the host system's clipboard.
+class HostClipboardMonitor implements Runnable {
+    public interface HostClipboardCallback {
+        void onHostClipboardUpdated(String contents);
+    }
+
+    private RandomAccessFile mPipe = null;
+    private HostClipboardCallback mHostClipboardCallback;
+    private static final String PIPE_NAME = "pipe:clipboard";
+    private static final String PIPE_DEVICE = "/dev/qemu_pipe";
+
+    private void openPipe() {
+        try {
+            // String.getBytes doesn't include the null terminator,
+            // but the QEMU pipe device requires the pipe service name
+            // to be null-terminated.
+            byte[] b = new byte[PIPE_NAME.length() + 1];
+            b[PIPE_NAME.length()] = 0;
+            System.arraycopy(
+                PIPE_NAME.getBytes(),
+                0,
+                b,
+                0,
+                PIPE_NAME.length());
+            mPipe = new RandomAccessFile(PIPE_DEVICE, "rw");
+            mPipe.write(b);
+        } catch (IOException e) {
+            try {
+                if (mPipe != null) mPipe.close();
+            } catch (IOException ee) {}
+            mPipe = null;
+        }
+    }
+
+    public HostClipboardMonitor(HostClipboardCallback cb) {
+        mHostClipboardCallback = cb;
+    }
+
+    @Override
+    public void run() {
+        while(!Thread.interrupted()) {
+            try {
+                // There's no guarantee that QEMU pipes will be ready at the moment
+                // this method is invoked. We simply try to get the pipe open and
+                // retry on failure indefinitely.
+                while (mPipe == null) {
+                    openPipe();
+                    Thread.sleep(100);
+                }
+                int size = mPipe.readInt();
+                size = Integer.reverseBytes(size);
+                byte[] receivedData = new byte[size];
+                mPipe.readFully(receivedData);
+                mHostClipboardCallback.onHostClipboardUpdated(
+                    new String(receivedData));
+            } catch (IOException e) {
+                try {
+                    mPipe.close();
+                } catch (IOException ee) {}
+                mPipe = null;
+            } catch (InterruptedException e) {}
+        }
+    }
+
+    public void setHostClipboard(String content) {
+        try {
+            if (mPipe != null) {
+                mPipe.writeInt(Integer.reverseBytes(content.getBytes().length));
+                mPipe.write(content.getBytes());
+            }
+        } catch(IOException e) {
+            Slog.e("HostClipboardMonitor",
+                   "Failed to set host clipboard " + e.getMessage());
+        }
+    }
+}
+
 /**
  * Implementation of the clipboard for copy and paste.
  */
 public class ClipboardService extends SystemService {
 
     private static final String TAG = "ClipboardService";
+    private static final boolean IS_EMULATOR =
+        SystemProperties.getBoolean("ro.kernel.qemu", false);
 
     private final IActivityManager mAm;
     private final IUserManager mUm;
     private final PackageManager mPm;
     private final AppOpsManager mAppOps;
     private final IBinder mPermissionOwner;
+    private HostClipboardMonitor mHostClipboardMonitor = null;
+    private Thread mHostMonitorThread = null;
 
     private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
 
@@ -82,6 +171,23 @@
             Slog.w("clipboard", "AM dead", e);
         }
         mPermissionOwner = permOwner;
+        if (IS_EMULATOR) {
+            mHostClipboardMonitor = new HostClipboardMonitor(
+                new HostClipboardMonitor.HostClipboardCallback() {
+                    @Override
+                    public void onHostClipboardUpdated(String contents){
+                        ClipData clip =
+                            new ClipData("host clipboard",
+                                         new String[]{"text/plain"},
+                                         new ClipData.Item(contents));
+                        synchronized(mClipboards) {
+                            setPrimaryClipInternal(getClipboard(0), clip);
+                        }
+                    }
+                });
+            mHostMonitorThread = new Thread(mHostClipboardMonitor);
+            mHostMonitorThread.start();
+        }
     }
 
     @Override
@@ -142,6 +248,11 @@
                 if (clip != null && clip.getItemCount() <= 0) {
                     throw new IllegalArgumentException("No items");
                 }
+                if (clip.getItemAt(0).getText() != null &&
+                    mHostClipboardMonitor != null) {
+                    mHostClipboardMonitor.setHostClipboard(
+                        clip.getItemAt(0).getText().toString());
+                }
                 final int callingUid = Binder.getCallingUid();
                 if (mAppOps.noteOp(AppOpsManager.OP_WRITE_CLIPBOARD, callingUid,
                         callingPackage) != AppOpsManager.MODE_ALLOWED) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 5f9efe7..85d1d1e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -23,6 +23,7 @@
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.TrafficStats;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -381,7 +382,12 @@
         protected void setupSocket(
                 int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
                 throws ErrnoException, IOException {
-            mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
+            final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
+            try {
+                mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
+            } finally {
+                TrafficStats.setThreadStatsTag(oldTag);
+            }
             // Setting SNDTIMEO is purely for defensive purposes.
             Os.setsockoptTimeval(mFileDescriptor,
                     SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(writeTimeout));
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index c40780e..fbda901 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -779,6 +779,7 @@
         int httpResponseCode = 599;
         String redirectUrl = null;
         final Stopwatch probeTimer = new Stopwatch().start();
+        final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
         try {
             urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
             urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
@@ -839,6 +840,7 @@
             if (urlConnection != null) {
                 urlConnection.disconnect();
             }
+            TrafficStats.setThreadStatsTag(oldTag);
         }
         logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);
         return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString());
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 58c76ec..34826b6 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -25,6 +25,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.net.ProxyInfo;
+import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -103,11 +104,15 @@
             String file;
             synchronized (mProxyLock) {
                 if (Uri.EMPTY.equals(mPacUrl)) return;
+                final int oldTag = TrafficStats
+                        .getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC);
                 try {
                     file = get(mPacUrl);
                 } catch (IOException ioe) {
                     file = null;
                     Log.w(TAG, "Failed to load PAC file: " + ioe);
+                } finally {
+                    TrafficStats.setThreadStatsTag(oldTag);
                 }
             }
             if (file != null) {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 0c80166..b0e4509 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -16,6 +16,11 @@
 
 package com.android.server.connectivity;
 
+import static android.hardware.usb.UsbManager.USB_CONNECTED;
+import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -71,6 +76,7 @@
 import com.android.server.connectivity.tethering.IControlsTethering;
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.IPv6TetheringInterfaceServices;
+import com.android.server.connectivity.tethering.TetheringConfiguration;
 import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
 import com.android.server.net.BaseNetworkObserver;
@@ -108,23 +114,11 @@
     private static final SparseArray<String> sMagicDecoderRing =
             MessageUtils.findMessageNames(messageClasses);
 
-    // TODO - remove both of these - should be part of interface inspection/selection stuff
-    private String[] mTetherableUsbRegexs;
-    private String[] mTetherableWifiRegexs;
-    private String[] mTetherableBluetoothRegexs;
-    private Collection<Integer> mUpstreamIfaceTypes;
+    private volatile TetheringConfiguration mConfig;
 
     // used to synchronize public access to members
     private final Object mPublicSync;
 
-    private static final Integer MOBILE_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE);
-    private static final Integer HIPRI_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_HIPRI);
-    private static final Integer DUN_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_DUN);
-
-    // if we have to connect to mobile, what APN type should we use?  Calculated by examining the
-    // upstream type list and the DUN_REQUIRED secure-setting
-    private int mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_NONE;
-
     private final INetworkManagementService mNMService;
     private final INetworkStatsService mStatsService;
     private final INetworkPolicyManager mPolicyManager;
@@ -150,24 +144,6 @@
     private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
             .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
 
-    // USB is  192.168.42.1 and 255.255.255.0
-    // Wifi is 192.168.43.1 and 255.255.255.0
-    // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
-    // with 255.255.255.0
-    // P2P is 192.168.49.1 and 255.255.255.0
-
-    private String[] mDhcpRange;
-    private static final String[] DHCP_DEFAULT_RANGE = {
-        "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
-        "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
-        "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
-        "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
-    };
-
-    private String[] mDefaultDnsServers;
-    private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
-    private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
-
     private final StateMachine mTetherMasterSM;
     private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
     private String mCurrentUpstreamIface;
@@ -208,27 +184,16 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        mContext.registerReceiver(mStateReceiver, filter);
+        mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
 
         filter = new IntentFilter();
         filter.addAction(Intent.ACTION_MEDIA_SHARED);
         filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
         filter.addDataScheme("file");
-        mContext.registerReceiver(mStateReceiver, filter);
-
-        mDhcpRange = context.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_dhcp_range);
-        if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
-            mDhcpRange = DHCP_DEFAULT_RANGE;
-        }
+        mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
 
         // load device config info
         updateConfiguration();
-
-        // TODO - remove and rely on real notifications of the current iface
-        mDefaultDnsServers = new String[2];
-        mDefaultDnsServers[0] = DNS_DEFAULT_SERVER1;
-        mDefaultDnsServers[1] = DNS_DEFAULT_SERVER2;
     }
 
     // We can't do this once in the Tethering() constructor and cache the value, because the
@@ -237,30 +202,8 @@
         return (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
     }
 
-    void updateConfiguration() {
-        String[] tetherableUsbRegexs = mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_usb_regexs);
-        String[] tetherableWifiRegexs = mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_wifi_regexs);
-        String[] tetherableBluetoothRegexs = mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_bluetooth_regexs);
-
-        int ifaceTypes[] = mContext.getResources().getIntArray(
-                com.android.internal.R.array.config_tether_upstream_types);
-        Collection<Integer> upstreamIfaceTypes = new ArrayList<>();
-        for (int i : ifaceTypes) {
-            upstreamIfaceTypes.add(new Integer(i));
-        }
-
-        synchronized (mPublicSync) {
-            mTetherableUsbRegexs = tetherableUsbRegexs;
-            mTetherableWifiRegexs = tetherableWifiRegexs;
-            mTetherableBluetoothRegexs = tetherableBluetoothRegexs;
-            mUpstreamIfaceTypes = upstreamIfaceTypes;
-        }
-
-        // check if the upstream type list needs to be modified due to secure-settings
-        checkDunRequired();
+    private void updateConfiguration() {
+        mConfig = new TetheringConfiguration(mContext);
     }
 
     @Override
@@ -300,39 +243,14 @@
         interfaceStatusChanged(iface, up);
     }
 
-    private boolean isUsb(String iface) {
-        synchronized (mPublicSync) {
-            for (String regex : mTetherableUsbRegexs) {
-                if (iface.matches(regex)) return true;
-            }
-            return false;
-        }
-    }
-
-    private boolean isWifi(String iface) {
-        synchronized (mPublicSync) {
-            for (String regex : mTetherableWifiRegexs) {
-                if (iface.matches(regex)) return true;
-            }
-            return false;
-        }
-    }
-
-    private boolean isBluetooth(String iface) {
-        synchronized (mPublicSync) {
-            for (String regex : mTetherableBluetoothRegexs) {
-                if (iface.matches(regex)) return true;
-            }
-            return false;
-        }
-    }
-
     private int ifaceNameToType(String iface) {
-        if (isWifi(iface)) {
+        final TetheringConfiguration cfg = mConfig;
+
+        if (cfg.isWifi(iface)) {
             return ConnectivityManager.TETHERING_WIFI;
-        } else if (isUsb(iface)) {
+        } else if (cfg.isUsb(iface)) {
             return ConnectivityManager.TETHERING_USB;
-        } else if (isBluetooth(iface)) {
+        } else if (cfg.isBluetooth(iface)) {
             return ConnectivityManager.TETHERING_BLUETOOTH;
         }
         return ConnectivityManager.TETHERING_INVALID;
@@ -662,6 +580,8 @@
         boolean usbTethered = false;
         boolean bluetoothTethered = false;
 
+        final TetheringConfiguration cfg = mConfig;
+
         synchronized (mPublicSync) {
             for (int i = 0; i < mTetherStates.size(); i++) {
                 TetherState tetherState = mTetherStates.valueAt(i);
@@ -671,11 +591,11 @@
                 } else if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
                     availableList.add(iface);
                 } else if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
-                    if (isUsb(iface)) {
+                    if (cfg.isUsb(iface)) {
                         usbTethered = true;
-                    } else if (isWifi(iface)) {
+                    } else if (cfg.isWifi(iface)) {
                         wifiTethered = true;
-                    } else if (isBluetooth(iface)) {
+                    } else if (cfg.isBluetooth(iface)) {
                         bluetoothTethered = true;
                     }
                     activeList.add(iface);
@@ -779,69 +699,84 @@
     private class StateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context content, Intent intent) {
-            String action = intent.getAction();
-            if (action == null) { return; }
+            final String action = intent.getAction();
+            if (action == null) return;
+
             if (action.equals(UsbManager.ACTION_USB_STATE)) {
-                synchronized (Tethering.this.mPublicSync) {
-                    boolean usbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
-                    mRndisEnabled = intent.getBooleanExtra(UsbManager.USB_FUNCTION_RNDIS, false);
-                    // start tethering if we have a request pending
-                    if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
-                        tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_USB);
-                    }
-                    mUsbTetherRequested = false;
-                }
+                handleUsbAction(intent);
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-                NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
-                        ConnectivityManager.EXTRA_NETWORK_INFO);
-                if (networkInfo != null &&
-                        networkInfo.getDetailedState() != NetworkInfo.DetailedState.FAILED) {
-                    if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
-                    mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
-                }
+                handleConnectivityAction(intent);
             } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
-                synchronized (Tethering.this.mPublicSync) {
-                    int curState =  intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
-                            WifiManager.WIFI_AP_STATE_DISABLED);
-                    switch (curState) {
-                        case WifiManager.WIFI_AP_STATE_ENABLING:
-                            // We can see this state on the way to both enabled and failure states.
-                            break;
-                        case WifiManager.WIFI_AP_STATE_ENABLED:
-                            // When the AP comes up and we've been requested to tether it, do so.
-                            if (mWifiTetherRequested) {
-                                tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_WIFI);
-                            }
-                            break;
-                        case WifiManager.WIFI_AP_STATE_DISABLED:
-                        case WifiManager.WIFI_AP_STATE_DISABLING:
-                        case WifiManager.WIFI_AP_STATE_FAILED:
-                        default:
-                            if (DBG) {
-                                Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" +
-                                    curState);
-                            }
-                            // Tell appropriate interface state machines that they should tear
-                            // themselves down.
-                            for (int i = 0; i < mTetherStates.size(); i++) {
-                                TetherInterfaceStateMachine tism =
-                                        mTetherStates.valueAt(i).mStateMachine;
-                                if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
-                                    tism.sendMessage(
-                                            TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
-                                    break;  // There should be at most one of these.
-                                }
-                            }
-                            // Regardless of whether we requested this transition, the AP has gone
-                            // down.  Don't try to tether again unless we're requested to do so.
-                            mWifiTetherRequested = false;
-                            break;
-                    }
-                }
+                handleWifiApAction(intent);
             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                 updateConfiguration();
             }
         }
+
+        private void handleConnectivityAction(Intent intent) {
+            final NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
+                    ConnectivityManager.EXTRA_NETWORK_INFO);
+            if (networkInfo == null ||
+                    networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
+                return;
+            }
+
+            if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION: " + networkInfo.toString());
+            mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+        }
+
+        private void handleUsbAction(Intent intent) {
+            final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false);
+            final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
+            synchronized (Tethering.this.mPublicSync) {
+                mRndisEnabled = rndisEnabled;
+                // start tethering if we have a request pending
+                if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
+                    tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_USB);
+                }
+                mUsbTetherRequested = false;
+            }
+        }
+
+        private void handleWifiApAction(Intent intent) {
+            final int curState =  intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
+            synchronized (Tethering.this.mPublicSync) {
+                switch (curState) {
+                    case WifiManager.WIFI_AP_STATE_ENABLING:
+                        // We can see this state on the way to both enabled and failure states.
+                        break;
+                    case WifiManager.WIFI_AP_STATE_ENABLED:
+                        // When the AP comes up and we've been requested to tether it, do so.
+                        if (mWifiTetherRequested) {
+                            tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_WIFI);
+                        }
+                        break;
+                    case WifiManager.WIFI_AP_STATE_DISABLED:
+                    case WifiManager.WIFI_AP_STATE_DISABLING:
+                    case WifiManager.WIFI_AP_STATE_FAILED:
+                    default:
+                        if (DBG) {
+                            Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" +
+                                curState);
+                        }
+                        // Tell appropriate interface state machines that they should tear
+                        // themselves down.
+                        for (int i = 0; i < mTetherStates.size(); i++) {
+                            TetherInterfaceStateMachine tism =
+                                    mTetherStates.valueAt(i).mStateMachine;
+                            if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
+                                tism.sendMessage(
+                                        TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+                                break;  // There should be at most one of these.
+                            }
+                        }
+                        // Regardless of whether we requested this transition, the AP has gone
+                        // down.  Don't try to tether again unless we're requested to do so.
+                        mWifiTetherRequested = false;
+                    break;
+                }
+            }
+        }
     }
 
     private void tetherMatchingInterfaces(boolean enable, int interfaceType) {
@@ -875,26 +810,38 @@
         }
     }
 
-    // TODO - return copies so people can't tamper
+    public TetheringConfiguration getTetheringConfiguration() {
+        return mConfig;
+    }
+
+    public boolean hasTetherableConfiguration() {
+        final TetheringConfiguration cfg = mConfig;
+        final boolean hasDownstreamConfiguration =
+                (cfg.tetherableUsbRegexs.length != 0) ||
+                (cfg.tetherableWifiRegexs.length != 0) ||
+                (cfg.tetherableBluetoothRegexs.length != 0);
+        final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty();
+
+        return hasDownstreamConfiguration && hasUpstreamConfiguration;
+    }
+
+    // TODO - update callers to use getTetheringConfiguration(),
+    // which has only final members.
     public String[] getTetherableUsbRegexs() {
-        return mTetherableUsbRegexs;
+        return copy(mConfig.tetherableUsbRegexs);
     }
 
     public String[] getTetherableWifiRegexs() {
-        return mTetherableWifiRegexs;
+        return copy(mConfig.tetherableWifiRegexs);
     }
 
     public String[] getTetherableBluetoothRegexs() {
-        return mTetherableBluetoothRegexs;
+        return copy(mConfig.tetherableBluetoothRegexs);
     }
 
     public int setUsbTethering(boolean enable) {
         if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
         UsbManager usbManager = mContext.getSystemService(UsbManager.class);
-        if (usbManager == null) {
-            return enable ? ConnectivityManager.TETHER_ERROR_MASTER_ERROR
-                          : ConnectivityManager.TETHER_ERROR_NO_ERROR;
-        }
 
         synchronized (mPublicSync) {
             if (enable) {
@@ -925,61 +872,6 @@
         return ConnectivityManager.TETHER_ERROR_NO_ERROR;
     }
 
-    public int[] getUpstreamIfaceTypes() {
-        int values[];
-        synchronized (mPublicSync) {
-            updateConfiguration();  // TODO - remove?
-            values = new int[mUpstreamIfaceTypes.size()];
-            Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator();
-            for (int i=0; i < mUpstreamIfaceTypes.size(); i++) {
-                values[i] = iterator.next();
-            }
-        }
-        return values;
-    }
-
-    private void checkDunRequired() {
-        int secureSetting = 2;
-        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-        if (tm != null) {
-            secureSetting = tm.getTetherApnRequired();
-        }
-        synchronized (mPublicSync) {
-            // 2 = not set, 0 = DUN not required, 1 = DUN required
-            if (secureSetting != 2) {
-                int requiredApn = (secureSetting == 1 ?
-                        ConnectivityManager.TYPE_MOBILE_DUN :
-                        ConnectivityManager.TYPE_MOBILE_HIPRI);
-                if (requiredApn == ConnectivityManager.TYPE_MOBILE_DUN) {
-                    while (mUpstreamIfaceTypes.contains(MOBILE_TYPE)) {
-                        mUpstreamIfaceTypes.remove(MOBILE_TYPE);
-                    }
-                    while (mUpstreamIfaceTypes.contains(HIPRI_TYPE)) {
-                        mUpstreamIfaceTypes.remove(HIPRI_TYPE);
-                    }
-                    if (mUpstreamIfaceTypes.contains(DUN_TYPE) == false) {
-                        mUpstreamIfaceTypes.add(DUN_TYPE);
-                    }
-                } else {
-                    while (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
-                        mUpstreamIfaceTypes.remove(DUN_TYPE);
-                    }
-                    if (mUpstreamIfaceTypes.contains(MOBILE_TYPE) == false) {
-                        mUpstreamIfaceTypes.add(MOBILE_TYPE);
-                    }
-                    if (mUpstreamIfaceTypes.contains(HIPRI_TYPE) == false) {
-                        mUpstreamIfaceTypes.add(HIPRI_TYPE);
-                    }
-                }
-            }
-            if (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
-                mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_DUN;
-            } else {
-                mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_HIPRI;
-            }
-        }
-    }
-
     // TODO review API - maybe return ArrayList<String> here and below?
     public String[] getTetheredIfaces() {
         ArrayList<String> list = new ArrayList<String>();
@@ -1008,7 +900,7 @@
     }
 
     public String[] getTetheredDhcpRanges() {
-        return mDhcpRange;
+        return mConfig.dhcpRanges;
     }
 
     public String[] getErroredIfaces() {
@@ -1083,8 +975,6 @@
         private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
         private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
 
-        private int mPreviousMobileApn = ConnectivityManager.TYPE_NONE;
-
         private static final int UPSTREAM_SETTLE_TIME_MS     = 10000;
 
         TetherMasterSM(String name, Looper looper) {
@@ -1118,46 +1008,18 @@
                 return false;
             }
 
-            protected boolean turnOnUpstreamMobileConnection(int apnType) {
-                if (apnType == ConnectivityManager.TYPE_NONE) { return false; }
-
-                if (apnType != mPreviousMobileApn) {
-                    // Unregister any previous mobile upstream callback because
-                    // this request, if any, will be different.
-                    turnOffUpstreamMobileConnection();
-                }
-
-                if (mUpstreamNetworkMonitor.mobileNetworkRequested()) {
-                    // Looks like we already filed a request for this apnType.
-                    return true;
-                }
-
-                switch (apnType) {
-                    case ConnectivityManager.TYPE_MOBILE_DUN:
-                    case ConnectivityManager.TYPE_MOBILE:
-                    case ConnectivityManager.TYPE_MOBILE_HIPRI:
-                        mPreviousMobileApn = apnType;
-                        break;
-                    default:
-                        return false;
-                }
-
-                // TODO: This should be called by the code that observes
-                // configuration changes, once the above code in this function
-                // is simplified (i.e. eradicated).
-                mUpstreamNetworkMonitor.mobileUpstreamRequiresDun(
-                        apnType == ConnectivityManager.TYPE_MOBILE_DUN);
-
+            protected boolean requestUpstreamMobileConnection() {
+                mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
                 mUpstreamNetworkMonitor.registerMobileNetworkRequest();
                 return true;
             }
 
-            protected void turnOffUpstreamMobileConnection() {
+            protected void unrequestUpstreamMobileConnection() {
                 mUpstreamNetworkMonitor.releaseMobileNetworkRequest();
-                mPreviousMobileApn = ConnectivityManager.TYPE_NONE;
             }
 
             protected boolean turnOnMasterTetherSettings() {
+                final TetheringConfiguration cfg = mConfig;
                 try {
                     mNMService.setIpForwardingEnabled(true);
                 } catch (Exception e) {
@@ -1165,11 +1027,11 @@
                     return false;
                 }
                 try {
-                    mNMService.startTethering(mDhcpRange);
+                    mNMService.startTethering(cfg.dhcpRanges);
                 } catch (Exception e) {
                     try {
                         mNMService.stopTethering();
-                        mNMService.startTethering(mDhcpRange);
+                        mNMService.startTethering(cfg.dhcpRanges);
                     } catch (Exception ee) {
                         transitionTo(mStartTetheringErrorState);
                         return false;
@@ -1202,29 +1064,31 @@
 
                 updateConfiguration(); // TODO - remove?
 
-                synchronized (mPublicSync) {
-                    if (VDBG) {
-                        Log.d(TAG, "chooseUpstreamType has upstream iface types:");
-                        for (Integer netType : mUpstreamIfaceTypes) {
-                            Log.d(TAG, " " + netType);
-                        }
-                    }
-
-                    for (Integer netType : mUpstreamIfaceTypes) {
-                        NetworkInfo info = cm.getNetworkInfo(netType.intValue());
-                        // TODO: if the network is suspended we should consider
-                        // that to be the same as connected here.
-                        if ((info != null) && info.isConnected()) {
-                            upType = netType.intValue();
-                            break;
-                        }
+                final TetheringConfiguration cfg = mConfig;
+                if (VDBG) {
+                    Log.d(TAG, "chooseUpstreamType has upstream iface types:");
+                    for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
+                        Log.d(TAG, " " + netType);
                     }
                 }
 
+                for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
+                    NetworkInfo info = cm.getNetworkInfo(netType.intValue());
+                    // TODO: if the network is suspended we should consider
+                    // that to be the same as connected here.
+                    if ((info != null) && info.isConnected()) {
+                        upType = netType.intValue();
+                        break;
+                    }
+                }
+
+                final int preferredUpstreamMobileApn = cfg.isDunRequired
+                        ? ConnectivityManager.TYPE_MOBILE_DUN
+                        : ConnectivityManager.TYPE_MOBILE_HIPRI;
                 if (DBG) {
                     Log.d(TAG, "chooseUpstreamType(" + tryCell + "),"
                             + " preferredApn="
-                            + ConnectivityManager.getNetworkTypeName(mPreferredUpstreamMobileApn)
+                            + ConnectivityManager.getNetworkTypeName(preferredUpstreamMobileApn)
                             + ", got type="
                             + ConnectivityManager.getNetworkTypeName(upType));
                 }
@@ -1233,11 +1097,10 @@
                     case ConnectivityManager.TYPE_MOBILE_DUN:
                     case ConnectivityManager.TYPE_MOBILE_HIPRI:
                         // If we're on DUN, put our own grab on it.
-                        turnOnUpstreamMobileConnection(upType);
+                        requestUpstreamMobileConnection();
                         break;
                     case ConnectivityManager.TYPE_NONE:
-                        if (tryCell &&
-                                turnOnUpstreamMobileConnection(mPreferredUpstreamMobileApn)) {
+                        if (tryCell && requestUpstreamMobileConnection()) {
                             // We think mobile should be coming up; don't set a retry.
                         } else {
                             sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
@@ -1250,7 +1113,7 @@
                          * If we found NONE we don't want to do this as we want any previous
                          * requests to keep trying to bring up something we can use.
                          */
-                        turnOffUpstreamMobileConnection();
+                        unrequestUpstreamMobileConnection();
                         break;
                 }
 
@@ -1295,7 +1158,8 @@
             }
 
             protected void setDnsForwarders(final Network network, final LinkProperties lp) {
-                String[] dnsServers = mDefaultDnsServers;
+                // TODO: Set v4 and/or v6 DNS per available connectivity.
+                String[] dnsServers = mConfig.defaultIPv4DNS;
                 final Collection<InetAddress> dnses = lp.getDnsServers();
                 // TODO: Properly support the absence of DNS servers.
                 if (dnses != null && !dnses.isEmpty()) {
@@ -1330,96 +1194,127 @@
             }
         }
 
-        private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
-        private SimChangeBroadcastReceiver mBroadcastReceiver = null;
+        private class SimChangeListener {
+            private final Context mContext;
+            private final AtomicInteger mSimBcastGenerationNumber;
+            private BroadcastReceiver mBroadcastReceiver;
 
-        private void startListeningForSimChanges() {
-            if (DBG) Log.d(TAG, "startListeningForSimChanges");
-            if (mBroadcastReceiver == null) {
+            SimChangeListener(Context ctx) {
+                mContext = ctx;
+                mSimBcastGenerationNumber = new AtomicInteger(0);
+            }
+
+            public int generationNumber() {
+                return mSimBcastGenerationNumber.get();
+            }
+
+            public void startListening() {
+                if (DBG) Log.d(TAG, "startListening for SIM changes");
+
+                if (mBroadcastReceiver != null) return;
+
                 mBroadcastReceiver = new SimChangeBroadcastReceiver(
                         mSimBcastGenerationNumber.incrementAndGet());
                 final IntentFilter filter = new IntentFilter();
                 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
 
-                mContext.registerReceiver(mBroadcastReceiver, filter);
+                mContext.registerReceiver(mBroadcastReceiver, filter, null,
+                        mTetherMasterSM.getHandler());
             }
-        }
 
-        private void stopListeningForSimChanges() {
-            if (DBG) Log.d(TAG, "stopListeningForSimChanges");
-            if (mBroadcastReceiver != null) {
+            public void stopListening() {
+                if (DBG) Log.d(TAG, "stopListening for SIM changes");
+
+                if (mBroadcastReceiver == null) return;
+
                 mSimBcastGenerationNumber.incrementAndGet();
                 mContext.unregisterReceiver(mBroadcastReceiver);
                 mBroadcastReceiver = null;
             }
-        }
 
-        class SimChangeBroadcastReceiver extends BroadcastReceiver {
-            // used to verify this receiver is still current
-            final private int mGenerationNumber;
-
-            // we're interested in edge-triggered LOADED notifications, so
-            // ignore LOADED unless we saw an ABSENT state first
-            private boolean mSimAbsentSeen = false;
-
-            public SimChangeBroadcastReceiver(int generationNumber) {
-                super();
-                mGenerationNumber = generationNumber;
+            public boolean hasMobileHotspotProvisionApp() {
+                try {
+                    if (!mContext.getResources().getString(com.android.internal.R.string.
+                            config_mobile_hotspot_provision_app_no_ui).isEmpty()) {
+                        Log.d(TAG, "re-evaluate provisioning");
+                        return true;
+                    }
+                } catch (Resources.NotFoundException e) {}
+                Log.d(TAG, "no prov-check needed for new SIM");
+                return false;
             }
 
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (DBG) {
-                    Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
-                            ", current generationNumber=" + mSimBcastGenerationNumber.get());
-                }
-                if (mGenerationNumber != mSimBcastGenerationNumber.get()) return;
+            private boolean isSimCardAbsent(String state) {
+                return IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state);
+            }
 
-                final String state =
-                        intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+            private boolean isSimCardLoaded(String state) {
+                return IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state);
+            }
 
-                Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" +
-                        mSimAbsentSeen);
-                if (!mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state)) {
-                    mSimAbsentSeen = true;
+            private void startProvisionIntent(int tetherType) {
+                final Intent startProvIntent = new Intent();
+                startProvIntent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
+                startProvIntent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
+                startProvIntent.setComponent(TETHER_SERVICE);
+                mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+            }
+
+            private class SimChangeBroadcastReceiver extends BroadcastReceiver {
+                // used to verify this receiver is still current
+                final private int mGenerationNumber;
+
+                // we're interested in edge-triggered LOADED notifications, so
+                // ignore LOADED unless we saw an ABSENT state first
+                private boolean mSimAbsentSeen = false;
+
+                public SimChangeBroadcastReceiver(int generationNumber) {
+                    mGenerationNumber = generationNumber;
                 }
 
-                if (mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state)) {
-                    mSimAbsentSeen = false;
-                    try {
-                        if (mContext.getResources().getString(com.android.internal.R.string.
-                                config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
-                            ArrayList<Integer> tethered = new ArrayList<Integer>();
-                            synchronized (mPublicSync) {
-                                for (int i = 0; i < mTetherStates.size(); i++) {
-                                    TetherState tetherState = mTetherStates.valueAt(i);
-                                    if (tetherState.mLastState !=
-                                            IControlsTethering.STATE_TETHERED) {
-                                        continue;  // Skip interfaces that aren't tethered.
-                                    }
-                                    String iface = mTetherStates.keyAt(i);
-                                    int interfaceType = ifaceNameToType(iface);
-                                    if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
-                                        tethered.add(new Integer(interfaceType));
-                                    }
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    final int currentGenerationNumber = mSimBcastGenerationNumber.get();
+
+                    if (DBG) {
+                        Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
+                                ", current generationNumber=" + currentGenerationNumber);
+                    }
+                    if (mGenerationNumber != currentGenerationNumber) return;
+
+                    final String state = intent.getStringExtra(
+                            IccCardConstants.INTENT_KEY_ICC_STATE);
+                    Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" +
+                            mSimAbsentSeen);
+
+                    if (isSimCardAbsent(state)) {
+                        if (!mSimAbsentSeen) mSimAbsentSeen = true;
+                        return;
+                    }
+
+                    if (isSimCardLoaded(state) && mSimAbsentSeen) {
+                        mSimAbsentSeen = false;
+
+                        if (!hasMobileHotspotProvisionApp()) return;
+
+                        ArrayList<Integer> tethered = new ArrayList<Integer>();
+                        synchronized (mPublicSync) {
+                            for (int i = 0; i < mTetherStates.size(); i++) {
+                                TetherState tetherState = mTetherStates.valueAt(i);
+                                if (tetherState.mLastState != IControlsTethering.STATE_TETHERED) {
+                                    continue;  // Skip interfaces that aren't tethered.
+                                }
+                                String iface = mTetherStates.keyAt(i);
+                                int interfaceType = ifaceNameToType(iface);
+                                if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
+                                    tethered.add(new Integer(interfaceType));
                                 }
                             }
-                            for (int tetherType : tethered) {
-                                Intent startProvIntent = new Intent();
-                                startProvIntent.putExtra(
-                                        ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
-                                startProvIntent.putExtra(
-                                        ConnectivityManager.EXTRA_RUN_PROVISION, true);
-                                startProvIntent.setComponent(TETHER_SERVICE);
-                                mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
-                            }
-                            Log.d(TAG, "re-evaluate provisioning");
-                        } else {
-                            Log.d(TAG, "no prov-check needed for new SIM");
                         }
-                    } catch (Resources.NotFoundException e) {
-                        Log.d(TAG, "no prov-check needed for new SIM");
-                        // not defined, do nothing
+
+                        for (int tetherType : tethered) {
+                            startProvisionIntent(tetherType);
+                        }
                     }
                 }
             }
@@ -1455,12 +1350,13 @@
         }
 
         class TetherModeAliveState extends TetherMasterUtilState {
+            final SimChangeListener simChange = new SimChangeListener(mContext);
             boolean mTryCell = true;
             @Override
             public void enter() {
                 // TODO: examine if we should check the return value.
                 turnOnMasterTetherSettings(); // may transition us out
-                startListeningForSimChanges();
+                simChange.startListening();
                 mUpstreamNetworkMonitor.start();
 
                 mTryCell = true;  // better try something first pass or crazy tests cases will fail
@@ -1470,9 +1366,9 @@
 
             @Override
             public void exit() {
-                turnOffUpstreamMobileConnection();
+                unrequestUpstreamMobileConnection();
                 mUpstreamNetworkMonitor.stop();
-                stopListeningForSimChanges();
+                simChange.stopListening();
                 notifyTetheredOfNewUpstreamIface(null);
                 handleNewUpstreamNetworkState(null);
             }
@@ -1672,9 +1568,10 @@
 
         pw.println("Tethering:");
         pw.increaseIndent();
-        pw.print("mUpstreamIfaceTypes:");
+        final TetheringConfiguration cfg = mConfig;
+        pw.print("preferredUpstreamIfaceTypes:");
         synchronized (mPublicSync) {
-            for (Integer netType : mUpstreamIfaceTypes) {
+            for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
                 pw.print(" " + ConnectivityManager.getNetworkTypeName(netType));
             }
             pw.println();
@@ -1754,4 +1651,8 @@
         mTetherStates.put(iface, tetherState);
         tetherState.mStateMachine.start();
     }
+
+    private static String[] copy(String[] strarray) {
+        return Arrays.copyOf(strarray, strarray.length);
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
new file mode 100644
index 0000000..14d06cc
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+
+/**
+ * A utility class to encapsulate the various tethering configuration elements.
+ *
+ * This configuration data includes elements describing upstream properties
+ * (preferred and required types of upstream connectivity as well as default
+ * DNS servers to use if none are available) and downstream properties (such
+ * as regular expressions use to match suitable downstream interfaces and the
+ * DHCPv4 ranges to use).
+ *
+ * @hide
+ */
+public class TetheringConfiguration {
+    private static final String TAG = TetheringConfiguration.class.getSimpleName();
+
+    private static final int DUN_NOT_REQUIRED = 0;
+    private static final int DUN_REQUIRED = 1;
+    private static final int DUN_UNSPECIFIED = 2;
+
+    // USB is  192.168.42.1 and 255.255.255.0
+    // Wifi is 192.168.43.1 and 255.255.255.0
+    // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
+    // with 255.255.255.0
+    // P2P is 192.168.49.1 and 255.255.255.0
+    private static final String[] DHCP_DEFAULT_RANGE = {
+        "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
+        "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
+        "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
+        "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
+    };
+
+    private final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
+
+    public final String[] tetherableUsbRegexs;
+    public final String[] tetherableWifiRegexs;
+    public final String[] tetherableBluetoothRegexs;
+    public final boolean isDunRequired;
+    public final Collection<Integer> preferredUpstreamIfaceTypes;
+    public final String[] dhcpRanges;
+    public final String[] defaultIPv4DNS;
+
+    public TetheringConfiguration(Context ctx) {
+        tetherableUsbRegexs = ctx.getResources().getStringArray(
+                com.android.internal.R.array.config_tether_usb_regexs);
+        tetherableWifiRegexs = ctx.getResources().getStringArray(
+                com.android.internal.R.array.config_tether_wifi_regexs);
+        tetherableBluetoothRegexs = ctx.getResources().getStringArray(
+                com.android.internal.R.array.config_tether_bluetooth_regexs);
+
+        isDunRequired = checkDunRequired(ctx);
+        preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, isDunRequired);
+
+        dhcpRanges = getDhcpRanges(ctx);
+        defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+    }
+
+    public boolean isUsb(String iface) {
+        return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
+    }
+
+    public boolean isWifi(String iface) {
+        return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
+    }
+
+    public boolean isBluetooth(String iface) {
+        return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
+    }
+
+    private static boolean checkDunRequired(Context ctx) {
+        final TelephonyManager tm = ctx.getSystemService(TelephonyManager.class);
+        final int secureSetting =
+                (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED;
+        return (secureSetting == DUN_REQUIRED);
+    }
+
+    private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, boolean requiresDun) {
+        final int ifaceTypes[] = ctx.getResources().getIntArray(
+                com.android.internal.R.array.config_tether_upstream_types);
+        final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
+        for (int i : ifaceTypes) {
+            switch (i) {
+                case TYPE_MOBILE:
+                case TYPE_MOBILE_HIPRI:
+                    if (requiresDun) continue;
+                    break;
+                case TYPE_MOBILE_DUN:
+                    if (!requiresDun) continue;
+                    break;
+            }
+            upstreamIfaceTypes.add(i);
+        }
+
+        // Fix up upstream interface types for DUN or mobile. NOTE: independent
+        // of the value of |requiresDun|, cell data of one form or another is
+        // *always* an upstream, regardless of the upstream interface types
+        // specified by configuration resources.
+        if (requiresDun) {
+            if (!upstreamIfaceTypes.contains(TYPE_MOBILE_DUN)) {
+                upstreamIfaceTypes.add(TYPE_MOBILE_DUN);
+            }
+        } else {
+            if (!upstreamIfaceTypes.contains(TYPE_MOBILE)) {
+                upstreamIfaceTypes.add(TYPE_MOBILE);
+            }
+            if (!upstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)) {
+                upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
+            }
+        }
+
+        return upstreamIfaceTypes;
+    }
+
+    private static boolean matchesDownstreamRegexs(String iface, String[] regexs) {
+        for (String regex : regexs) {
+            if (iface.matches(regex)) return true;
+        }
+        return false;
+    }
+
+    private static String[] getDhcpRanges(Context ctx) {
+        final String[] fromResource = ctx.getResources().getStringArray(
+                com.android.internal.R.array.config_tether_dhcp_range);
+        if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
+            return fromResource;
+        }
+        return copy(DHCP_DEFAULT_RANGE);
+    }
+
+    private static String[] copy(String[] strarray) {
+        return Arrays.copyOf(strarray, strarray.length);
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 927dfd5..23481dc 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -16,6 +16,9 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
@@ -106,7 +109,7 @@
         mNetworkMap.clear();
     }
 
-    public void mobileUpstreamRequiresDun(boolean dunRequired) {
+    public void updateMobileRequiresDun(boolean dunRequired) {
         final boolean valueChanged = (mDunRequired != dunRequired);
         mDunRequired = dunRequired;
         if (valueChanged && mobileNetworkRequested()) {
@@ -120,7 +123,10 @@
     }
 
     public void registerMobileNetworkRequest() {
-        if (mMobileNetworkCallback != null) return;
+        if (mMobileNetworkCallback != null) {
+            Log.e(TAG, "registerMobileNetworkRequest() already registered");
+            return;
+        }
 
         final NetworkRequest.Builder builder = new NetworkRequest.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
@@ -136,13 +142,16 @@
         // Therefore, to avoid duplicate notifications, we only register a no-op.
         mMobileNetworkCallback = new NetworkCallback();
 
-        // TODO: Change the timeout from 0 (no onUnavailable callback) to use some
-        // moderate callback time (once timeout callbacks are implemented). This might
-        // be useful for updating some UI. Additionally, we should definitely log a
-        // message to aid in any subsequent debugging
-        if (DBG) Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
+        // TODO: Change the timeout from 0 (no onUnavailable callback) to some
+        // moderate callback timeout. This might be useful for updating some UI.
+        // Additionally, we log a message to aid in any subsequent debugging.
+        Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
 
-        cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback);
+        // The following use of the legacy type system cannot be removed until
+        // after upstream selection no longer finds networks by legacy type.
+        // See also b/34364553.
+        final int apnType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
+        cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, apnType);
     }
 
     public void releaseMobileNetworkRequest() {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 11a3f11..5b539ff 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1019,8 +1019,7 @@
         final int owningUid = syncAdapterInfo.uid;
         final String owningPackage = syncAdapterInfo.componentName.getPackageName();
         try {
-            if (ActivityManager.getService().getAppStartMode(owningUid,
-                    owningPackage) == ActivityManager.APP_START_MODE_DISABLED) {
+            if (ActivityManager.getService().isAppStartModeDisabled(owningUid, owningPackage)) {
                 Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
                         + syncAdapterInfo.componentName
                         + " -- package not allowed to start");
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9c762cc..cd07793 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -48,6 +48,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.text.TextUtils;
+import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -230,6 +231,9 @@
     // intended for use inside of the requestGlobalDisplayStateInternal function.
     private final ArrayList<Runnable> mTempDisplayStateWorkQueue = new ArrayList<Runnable>();
 
+    // Lists of UIDs that are present on the displays. Maps displayId -> array of UIDs.
+    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
     public DisplayManagerService(Context context) {
         super(context);
         mContext = context;
@@ -394,7 +398,8 @@
             LogicalDisplay display = mLogicalDisplays.get(displayId);
             if (display != null) {
                 DisplayInfo info = display.getDisplayInfoLocked();
-                if (info.hasAccess(callingUid)) {
+                if (info.hasAccess(callingUid)
+                        || isUidPresentOnDisplayInternal(callingUid, displayId)) {
                     return info;
                 }
             }
@@ -937,6 +942,25 @@
         }
     }
 
+    // Updates the lists of UIDs that are present on displays.
+    private void setDisplayAccessUIDsInternal(SparseArray<IntArray> newDisplayAccessUIDs) {
+        synchronized (mSyncRoot) {
+            mDisplayAccessUIDs.clear();
+            for (int i = newDisplayAccessUIDs.size() - 1; i >= 0; i--) {
+                mDisplayAccessUIDs.append(newDisplayAccessUIDs.keyAt(i),
+                        newDisplayAccessUIDs.valueAt(i));
+            }
+        }
+    }
+
+    // Checks if provided UID's content is present on the display and UID has access to it.
+    private boolean isUidPresentOnDisplayInternal(int uid, int displayId) {
+        synchronized (mSyncRoot) {
+            final IntArray displayUIDs = mDisplayAccessUIDs.get(displayId);
+            return displayUIDs != null && displayUIDs.indexOf(uid) != -1;
+        }
+    }
+
     private void clearViewportsLocked() {
         mDefaultViewport.valid = false;
         mExternalTouchViewport.valid = false;
@@ -1647,5 +1671,15 @@
         public void setDisplayOffsets(int displayId, int x, int y) {
             setDisplayOffsetsInternal(displayId, x, y);
         }
+
+        @Override
+        public void setDisplayAccessUIDs(SparseArray<IntArray> newDisplayAccessUIDs) {
+            setDisplayAccessUIDsInternal(newDisplayAccessUIDs);
+        }
+
+        @Override
+        public boolean isUidPresentOnDisplay(int uid, int displayId) {
+            return isUidPresentOnDisplayInternal(uid, displayId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 8673225..841a951 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -285,6 +285,7 @@
                 int activeColorMode) {
             List<Integer> pendingColorModes = new ArrayList<>();
 
+            if (colorModes == null) return false;
             // Build an updated list of all existing color modes.
             boolean colorModesAdded = false;
             for (int colorMode: colorModes) {
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index 0ec48b9..0357b1b 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -75,6 +75,11 @@
     };
 
     /**
+     * The transition time, in milliseconds, for Night Display to turn on/off.
+     */
+    private static final long TRANSITION_DURATION = 3000L;
+
+    /**
      * The identity matrix, used if one of the given matrices is {@code null}.
      */
     private static final float[] MATRIX_IDENTITY = new float[16];
@@ -285,8 +290,7 @@
 
             mColorMatrixAnimator = ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
                     from == null ? MATRIX_IDENTITY : from, to == null ? MATRIX_IDENTITY : to);
-            mColorMatrixAnimator.setDuration(getContext().getResources()
-                    .getInteger(android.R.integer.config_longAnimTime));
+            mColorMatrixAnimator.setDuration(TRANSITION_DURATION);
             mColorMatrixAnimator.setInterpolator(AnimationUtils.loadInterpolator(
                     getContext(), android.R.interpolator.fast_out_slow_in));
             mColorMatrixAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 997222c..e2e0d6b 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -41,6 +41,7 @@
 import android.os.IRemoteCallback;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
+import android.security.KeyStore;
 import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.SystemClock;
@@ -63,7 +64,6 @@
 
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
-
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintService;
@@ -276,8 +276,18 @@
         }
     }
 
-    protected void handleAuthenticated(long deviceId, int fingerId, int groupId) {
+    protected void handleAuthenticated(long deviceId, int fingerId, int groupId,
+            ArrayList<Byte> token) {
         ClientMonitor client = mCurrentClient;
+        if (fingerId != 0) {
+            // Ugh...
+            final byte[] byteToken = new byte[token.size()];
+            for (int i = 0; i < token.size(); i++) {
+                byteToken[i] = token.get(i);
+            }
+            // Send to Keystore
+            KeyStore.getInstance().addAuthToken(byteToken);
+        }
         if (client != null && client.onAuthenticated(fingerId, groupId)) {
             removeClient(client);
         }
@@ -482,7 +492,7 @@
         UserManager um = UserManager.get(mContext);
 
         // Allow current user or profiles of the current user...
-        for (int profileId : um.getEnabledProfileIds(userId)) {
+        for (int profileId : um.getEnabledProfileIds(mCurrentUserId)) {
             if (profileId == userId) {
                 return true;
             }
@@ -721,11 +731,12 @@
         }
 
         @Override
-        public void onAuthenticated(final long deviceId, final int fingerId, final int groupId) {
+        public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
+                ArrayList<Byte> token) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    handleAuthenticated(deviceId, fingerId, groupId);
+                    handleAuthenticated(deviceId, fingerId, groupId, token);
                 }
             });
         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 87f4030..3793b91 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -25,6 +25,7 @@
 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.R;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
@@ -83,6 +84,7 @@
 import android.util.Xml;
 import android.view.IInputFilter;
 import android.view.IInputFilterHost;
+import android.view.IWindow;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -179,6 +181,9 @@
     IInputFilter mInputFilter; // guarded by mInputFilterLock
     InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
 
+    private IWindow mFocusedWindow;
+    private boolean mFocusedWindowHasCapture;
+
     private static native long nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
     private static native void nativeStart(long ptr);
@@ -225,6 +230,7 @@
     private static native void nativeSetPointerIconType(long ptr, int iconId);
     private static native void nativeReloadPointerIcons(long ptr);
     private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
+    private static native void nativeSetPointerCapture(long ptr, boolean detached);
 
     // Input event injection constants defined in InputDispatcher.h.
     private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
@@ -1502,7 +1508,16 @@
         }
     }
 
-    public void setInputWindows(InputWindowHandle[] windowHandles) {
+    public void setInputWindows(InputWindowHandle[] windowHandles,
+            InputWindowHandle focusedWindowHandle) {
+        final IWindow newFocusedWindow =
+            focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
+        if (mFocusedWindow != newFocusedWindow) {
+            mFocusedWindow = newFocusedWindow;
+            if (mFocusedWindowHasCapture) {
+                setPointerCapture(false);
+            }
+        }
         nativeSetInputWindows(mPtr, windowHandles);
     }
 
@@ -1510,6 +1525,30 @@
         nativeSetFocusedApplication(mPtr, application);
     }
 
+    @Override
+    public void requestPointerCapture(IBinder windowToken, boolean enabled) {
+        if (mFocusedWindow == null || mFocusedWindow.asBinder() != windowToken) {
+            Slog.e(TAG, "requestPointerCapture called for a window that has no focus: "
+                    + windowToken);
+            return;
+        }
+        if (mFocusedWindowHasCapture == enabled) {
+            Slog.i(TAG, "requestPointerCapture: already " + (enabled ? "enabled" : "disabled"));
+            return;
+        }
+        setPointerCapture(enabled);
+        try {
+            mFocusedWindow.dispatchPointerCaptureChanged(enabled);
+        } catch (RemoteException ex) {
+            /* ignore */
+        }
+    }
+
+    private void setPointerCapture(boolean enabled) {
+        mFocusedWindowHasCapture = enabled;
+        nativeSetPointerCapture(mPtr, enabled);
+    }
+
     public void setInputDispatchMode(boolean enabled, boolean frozen) {
         nativeSetInputDispatchMode(mPtr, enabled, frozen);
     }
@@ -1700,6 +1739,7 @@
     // Binder call
     @Override
     public void setCustomPointerIcon(PointerIcon icon) {
+        Preconditions.checkNotNull(icon);
         nativeSetCustomPointerIcon(mPtr, icon);
     }
 
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/services/core/java/com/android/server/input/InputWindowHandle.java
index eb3581a..3d6f7ad 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/services/core/java/com/android/server/input/InputWindowHandle.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Region;
 import android.view.InputChannel;
+import android.view.IWindow;
 
 /**
  * Functions as a handle for a window that can receive input.
@@ -36,6 +37,9 @@
     // The window manager's window state.
     public final Object windowState;
 
+    // The client window.
+    public final IWindow clientWindow;
+
     // The input channel associated with the window.
     public InputChannel inputChannel;
 
@@ -93,9 +97,10 @@
     private native void nativeDispose();
 
     public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
-            Object windowState, int displayId) {
+            Object windowState, IWindow clientWindow, int displayId) {
         this.inputApplicationHandle = inputApplicationHandle;
         this.windowState = windowState;
+        this.clientWindow = clientWindow;
         this.displayId = displayId;
     }
 
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index f42c5be..a748013 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -566,8 +566,8 @@
             String tag) {
         JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
         try {
-            if (ActivityManager.getService().getAppStartMode(uId,
-                    job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
+            if (ActivityManager.getService().isAppStartModeDisabled(uId,
+                    job.getService().getPackageName())) {
                 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
                         + " -- package not allowed to start");
                 return JobScheduler.RESULT_FAILURE;
@@ -1201,9 +1201,8 @@
             public void process(JobStatus job) {
                 if (isReadyToBeExecutedLocked(job)) {
                     try {
-                        if (ActivityManager.getService().getAppStartMode(job.getUid(),
-                                job.getJob().getService().getPackageName())
-                                == ActivityManager.APP_START_MODE_DISABLED) {
+                        if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
+                                job.getJob().getService().getPackageName())) {
                             Slog.w(TAG, "Aborting job " + job.getUid() + ":"
                                     + job.getJob().toString() + " -- package not allowed to start");
                             mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
diff --git a/services/core/java/com/android/server/location/GpsXtraDownloader.java b/services/core/java/com/android/server/location/GpsXtraDownloader.java
index bf779859..62332c9 100644
--- a/services/core/java/com/android/server/location/GpsXtraDownloader.java
+++ b/services/core/java/com/android/server/location/GpsXtraDownloader.java
@@ -16,22 +16,19 @@
 
 package com.android.server.location;
 
+import android.net.TrafficStats;
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
-
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.IOException;
 import java.util.Properties;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
 
-import libcore.io.Streams;
-
 /**
  * A class for downloading GPS XTRA data.
  *
@@ -94,7 +91,12 @@
 
         // load balance our requests among the available servers
         while (result == null) {
-            result = doDownload(mXtraServers[mNextServerIndex]);
+            final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_GPS);
+            try {
+                result = doDownload(mXtraServers[mNextServerIndex]);
+            } finally {
+                TrafficStats.setThreadStatsTag(oldTag);
+            }
 
             // increment mNextServerIndex and wrap around if necessary
             mNextServerIndex++;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9b37f12..3bf95ef 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -17,6 +17,7 @@
 package com.android.server.media;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
@@ -36,10 +37,13 @@
 import android.media.IAudioService;
 import android.media.IRemoteVolumeController;
 import android.media.session.IActiveSessionsListener;
+import android.media.session.IOnMediaKeyListener;
+import android.media.session.IOnVolumeKeyLongPressListener;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
 import android.media.session.ISessionManager;
 import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -78,10 +82,11 @@
 public class MediaSessionService extends SystemService implements Monitor {
     private static final String TAG = "MediaSessionService";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    // Leave log for media key event always.
-    private static final boolean DEBUG_MEDIA_KEY_EVENT = DEBUG || true;
+    // Leave log for key event always.
+    private static final boolean DEBUG_KEY_EVENT = true;
 
     private static final int WAKELOCK_TIMEOUT = 5000;
+    private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
 
     /* package */final IBinder mICallback = new Binder();
 
@@ -523,6 +528,14 @@
         }
     }
 
+    private String getCallingPackageName(int uid) {
+        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
+        if (packages != null && packages.length > 0) {
+            return packages[0];
+        }
+        return "";
+    }
+
     /**
      * Information about a particular user. The contents of this object is
      * guarded by mLock.
@@ -534,6 +547,15 @@
         private PendingIntent mLastMediaButtonReceiver;
         private ComponentName mRestoredMediaButtonReceiver;
 
+        private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
+        private int mOnVolumeKeyLongPressListenerUid;
+        private KeyEvent mInitialDownVolumeKeyEvent;
+        private int mInitialDownVolumeStream;
+        private boolean mInitialDownMusicOnly;
+
+        private IOnMediaKeyListener mOnMediaKeyListener;
+        private int mOnMediaKeyListenerUid;
+
         public UserRecord(Context context, int userId) {
             mContext = context;
             mUserId = userId;
@@ -564,6 +586,12 @@
             String indent = prefix + "  ";
             pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
             pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver);
+            pw.println(indent + "Volume key long-press listener:" + mOnVolumeKeyLongPressListener);
+            pw.println(indent + "Volume key long-press listener package:" +
+                    getCallingPackageName(mOnVolumeKeyLongPressListenerUid));
+            pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
+            pw.println(indent + "Media key listener package: " +
+                    getCallingPackageName(mOnMediaKeyListenerUid));
             int size = mSessions.size();
             pw.println(indent + size + " Sessions:");
             for (int i = 0; i < size; i++) {
@@ -769,28 +797,10 @@
                 }
 
                 synchronized (mLock) {
-                    // If we don't have a media button receiver to fall back on
-                    // include non-playing sessions for dispatching
-                    boolean useNotPlayingSessions = true;
-                    for (int userId : mCurrentUserIdList) {
-                        UserRecord ur = mUserRecords.get(userId);
-                        if (ur.mLastMediaButtonReceiver != null
-                                || ur.mRestoredMediaButtonReceiver != null) {
-                            useNotPlayingSessions = false;
-                            break;
-                        }
-                    }
-
-                    if (DEBUG) {
-                        Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
-                                + useNotPlayingSessions);
-                    }
-                    MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
-                            mCurrentUserIdList, useNotPlayingSessions);
-                    if (isVoiceKey(keyEvent.getKeyCode())) {
-                        handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
+                    if (!isGlobalPriorityActive() && isVoiceKey(keyEvent.getKeyCode())) {
+                        handleVoiceKeyEventLocked(keyEvent, needWakeLock);
                     } else {
-                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
+                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, true);
                     }
                 }
             } finally {
@@ -799,13 +809,245 @@
         }
 
         @Override
+        public void setOnVolumeKeyLongPressListener(IOnVolumeKeyLongPressListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // Enforce SET_VOLUME_KEY_LONG_PRESS_LISTENER permission.
+                if (getContext().checkPermission(
+                        android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER, pid, uid)
+                            != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Must hold the SET_VOLUME_KEY_LONG_PRESS_LISTENER" +
+                            " permission.");
+                }
+
+                synchronized (mLock) {
+                    UserRecord user = mUserRecords.get(UserHandle.getUserId(uid));
+                    if (user.mOnVolumeKeyLongPressListener != null &&
+                            user.mOnVolumeKeyLongPressListenerUid != uid) {
+                        Log.w(TAG, "Volume key long-press listener cannot be reset by another app");
+                        return;
+                    }
+
+                    user.mOnVolumeKeyLongPressListener = listener;
+                    user.mOnVolumeKeyLongPressListenerUid = uid;
+
+                    Log.d(TAG, "Volume key long-press listener "
+                            + listener + " is set by " + getCallingPackageName(uid));
+
+                    if (user.mOnVolumeKeyLongPressListener != null) {
+                        try {
+                            user.mOnVolumeKeyLongPressListener.asBinder().linkToDeath(
+                                    new IBinder.DeathRecipient() {
+                                        @Override
+                                        public void binderDied() {
+                                            synchronized (mLock) {
+                                                user.mOnVolumeKeyLongPressListener = null;
+                                            }
+                                        }
+                                    }, 0);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed to set death recipient "
+                                    + user.mOnVolumeKeyLongPressListener);
+                            user.mOnVolumeKeyLongPressListener = null;
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setOnMediaKeyListener(IOnMediaKeyListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                // Enforce SET_MEDIA_KEY_LISTENER permission.
+                if (getContext().checkPermission(
+                        android.Manifest.permission.SET_MEDIA_KEY_LISTENER, pid, uid)
+                            != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Must hold the SET_MEDIA_KEY_LISTENER" +
+                            " permission.");
+                }
+
+                synchronized (mLock) {
+                    int userId = UserHandle.getUserId(uid);
+                    UserRecord user = mUserRecords.get(userId);
+                    if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) {
+                        Log.w(TAG, "Media key listener cannot be reset by another app");
+                        return;
+                    }
+
+                    user.mOnMediaKeyListener = listener;
+                    user.mOnMediaKeyListenerUid = uid;
+
+                    Log.d(TAG, "Media key listener " + user.mOnMediaKeyListener
+                            + " is set by " + getCallingPackageName(uid));
+
+                    if (user.mOnMediaKeyListener != null) {
+                        try {
+                            user.mOnMediaKeyListener.asBinder().linkToDeath(
+                                    new IBinder.DeathRecipient() {
+                                        @Override
+                                        public void binderDied() {
+                                            synchronized (mLock) {
+                                                user.mOnMediaKeyListener = null;
+                                            }
+                                        }
+                                    }, 0);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed to set death recipient " + user.mOnMediaKeyListener);
+                            user.mOnMediaKeyListener = null;
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * Handles the dispatching of the volume button events to one of the
+         * registered listeners. If there's a volume key long-press listener and
+         * there's no active global priority session, long-pressess will be sent to the
+         * long-press listener instead of adjusting volume.
+         *
+         * @param keyEvent a non-null KeyEvent whose key code is one of the
+         *            {@link KeyEvent#KEYCODE_VOLUME_UP},
+         *            {@link KeyEvent#KEYCODE_VOLUME_DOWN},
+         *            or {@link KeyEvent#KEYCODE_VOLUME_MUTE}.
+         * @param stream stream type to adjust volume.
+         * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume.
+         */
+        @Override
+        public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) {
+            if (keyEvent == null ||
+                    (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP
+                             && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN
+                             && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_MUTE)) {
+                Log.w(TAG, "Attempted to dispatch null or non-volume key event.");
+                return;
+            }
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            if (DEBUG) {
+                Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
+                        + keyEvent);
+            }
+
+            try {
+                synchronized (mLock) {
+                    // Only consider full user.
+                    UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
+
+                    if (mPriorityStack.isGlobalPriorityActive()
+                            || user.mOnVolumeKeyLongPressListener == null) {
+                        dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
+                    } else {
+                        // TODO: Consider the case when both volume up and down keys are pressed
+                        //       at the same time.
+                        if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+                            if (keyEvent.getRepeatCount() == 0) {
+                                user.mInitialDownVolumeKeyEvent = keyEvent;
+                                user.mInitialDownVolumeStream = stream;
+                                user.mInitialDownMusicOnly = musicOnly;
+                            }
+                            if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) {
+                                if (user.mInitialDownVolumeKeyEvent != null) {
+                                    dispatchVolumeKeyLongPressLocked(
+                                            user.mInitialDownVolumeKeyEvent);
+                                    // Mark that the key is already handled.
+                                    user.mInitialDownVolumeKeyEvent = null;
+                                }
+                                dispatchVolumeKeyLongPressLocked(keyEvent);
+                            }
+                        } else { // if up
+                            if (user.mInitialDownVolumeKeyEvent != null
+                                    && user.mInitialDownVolumeKeyEvent.getDownTime()
+                                            == keyEvent.getDownTime()) {
+                                // Short-press. Should change volume.
+                                dispatchVolumeKeyEventLocked(
+                                        user.mInitialDownVolumeKeyEvent,
+                                        user.mInitialDownVolumeStream,
+                                        user.mInitialDownMusicOnly);
+                                dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly);
+                            } else {
+                                dispatchVolumeKeyLongPressLocked(keyEvent);
+                            }
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) {
+            // Only consider full user.
+            UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
+            try {
+                user.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener");
+            }
+        }
+
+        private void dispatchVolumeKeyEventLocked(
+                KeyEvent keyEvent, int stream, boolean musicOnly) {
+            boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
+            boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP;
+            int direction = 0;
+            boolean isMute = false;
+            switch (keyEvent.getKeyCode()) {
+                case KeyEvent.KEYCODE_VOLUME_UP:
+                    direction = AudioManager.ADJUST_RAISE;
+                    break;
+                case KeyEvent.KEYCODE_VOLUME_DOWN:
+                    direction = AudioManager.ADJUST_LOWER;
+                    break;
+                case KeyEvent.KEYCODE_VOLUME_MUTE:
+                    isMute = true;
+                    break;
+            }
+            if (down || up) {
+                int flags = AudioManager.FLAG_FROM_KEY;
+                if (musicOnly) {
+                    // This flag is used when the screen is off to only affect active media.
+                    flags |= AudioManager.FLAG_ACTIVE_MEDIA_ONLY;
+                } else {
+                    // These flags are consistent with the home screen
+                    if (up) {
+                        flags |= AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
+                    } else {
+                        flags |= AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
+                    }
+                }
+                if (direction != 0) {
+                    // If this is action up we want to send a beep for non-music events
+                    if (up) {
+                        direction = 0;
+                    }
+                    dispatchAdjustVolumeLocked(stream, direction, flags);
+                } else if (isMute) {
+                    if (down && keyEvent.getRepeatCount() == 0) {
+                        dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags);
+                    }
+                }
+            }
+        }
+
+        @Override
         public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    MediaSessionRecord session = mPriorityStack
-                            .getDefaultVolumeSession(mCurrentUserIdList);
-                    dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
+                    dispatchAdjustVolumeLocked(suggestedStream, delta, flags);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -881,8 +1123,9 @@
             return resolvedUserId;
         }
 
-        private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
-                MediaSessionRecord session) {
+        private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) {
+            MediaSessionRecord session = mPriorityStack.getDefaultVolumeSession(mCurrentUserIdList);
+
             boolean preferSuggestedStream = false;
             if (isValidLocalStreamType(suggestedStream)
                     && AudioSystem.isStreamActive(suggestedStream, 0)) {
@@ -925,13 +1168,7 @@
             }
         }
 
-        private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
-                MediaSessionRecord session) {
-            if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
-                // If the phone app has priority just give it the event
-                dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
-                return;
-            }
+        private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) {
             int action = keyEvent.getAction();
             boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
             if (action == KeyEvent.ACTION_DOWN) {
@@ -948,24 +1185,60 @@
                     if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
                         // Resend the down then send this event through
                         KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
-                        dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
-                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
+                        dispatchMediaKeyEventLocked(downEvent, needWakeLock, true);
+                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, true);
                     }
                 }
             }
         }
 
         private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
-                MediaSessionRecord session) {
+                boolean checkMediaKeyListener) {
+            // If we don't have a media button receiver to fall back on
+            // include non-playing sessions for dispatching.
+            boolean useNotPlayingSessions = true;
+            for (int userId : mCurrentUserIdList) {
+                UserRecord ur = mUserRecords.get(userId);
+                if (ur.mLastMediaButtonReceiver != null
+                        || ur.mRestoredMediaButtonReceiver != null) {
+                    useNotPlayingSessions = false;
+                    break;
+                }
+            }
+            if (DEBUG) {
+                Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
+                        + useNotPlayingSessions);
+            }
+
+            MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
+                    mCurrentUserIdList, useNotPlayingSessions);
+
+            if ((session == null
+                    || !session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY))
+                    && checkMediaKeyListener) {
+                // Only consider full user.
+                UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0));
+                if (user.mOnMediaKeyListener != null) {
+                    if (DEBUG_KEY_EVENT) {
+                        Log.d(TAG, "Send " + keyEvent + " to media key listener");
+                    }
+                    try {
+                        user.mOnMediaKeyListener.onMediaKey(keyEvent,
+                                new MediaKeyListenerResultReceiver(keyEvent, needWakeLock));
+                        return;
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to send " + keyEvent + " to media key listener");
+                    }
+                }
+            }
             if (session != null) {
-                if (DEBUG_MEDIA_KEY_EVENT) {
+                if (DEBUG_KEY_EVENT) {
                     Log.d(TAG, "Sending " + keyEvent + " to " + session);
                 }
                 if (needWakeLock) {
                     mKeyEventReceiver.aquireWakeLockLocked();
                 }
-                // If we don't need a wakelock use -1 as the id so we
-                // won't release it later
+                // If we don't need a wakelock use -1 as the id so we won't release it later.
                 session.sendMediaButton(keyEvent,
                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
                         mKeyEventReceiver, Process.SYSTEM_UID,
@@ -986,7 +1259,7 @@
                     mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
                     try {
                         if (user.mLastMediaButtonReceiver != null) {
-                            if (DEBUG_MEDIA_KEY_EVENT) {
+                            if (DEBUG_KEY_EVENT) {
                                 Log.d(TAG, "Sending " + keyEvent
                                         + " to the last known pendingIntent "
                                         + user.mLastMediaButtonReceiver);
@@ -995,7 +1268,7 @@
                                     needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
                                     mediaButtonIntent, mKeyEventReceiver, mHandler);
                         } else {
-                            if (DEBUG_MEDIA_KEY_EVENT) {
+                            if (DEBUG_KEY_EVENT) {
                                 Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
                                         + user.mRestoredMediaButtonReceiver);
                             }
@@ -1081,6 +1354,46 @@
                     && streamType <= AudioManager.STREAM_NOTIFICATION;
         }
 
+        private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable {
+            private KeyEvent mKeyEvent;
+            private boolean mNeedWakeLock;
+            private boolean mHandled;
+
+            private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) {
+                super(mHandler);
+                mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT);
+                mKeyEvent = keyEvent;
+                mNeedWakeLock = needWakeLock;
+            }
+
+            @Override
+            public void run() {
+                Log.d(TAG, "The media key listener is timed-out for " + mKeyEvent);
+                dispatchMediaKeyEvent();
+            }
+
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode == MediaSessionManager.RESULT_MEDIA_KEY_HANDLED) {
+                    mHandled = true;
+                    mHandler.removeCallbacks(this);
+                    return;
+                }
+                dispatchMediaKeyEvent();
+            }
+
+            private void dispatchMediaKeyEvent() {
+                if (mHandled) {
+                    return;
+                }
+                mHandled = true;
+                mHandler.removeCallbacks(this);
+                synchronized (mLock) {
+                    dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock, false);
+                }
+            }
+        }
+
         private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
 
         class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index c48f430..ee00fdc 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -17,6 +17,8 @@
 package com.android.server.net;
 
 import android.net.NetworkIdentity;
+import android.service.NetworkIdentitySetProto;
+import android.util.proto.ProtoOutputStream;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -143,4 +145,14 @@
         final NetworkIdentity anotherIdent = another.iterator().next();
         return ident.compareTo(anotherIdent);
     }
+
+    public void writeToProto(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+
+        for (NetworkIdentity ident : this) {
+            ident.writeToProto(proto, NetworkIdentitySetProto.IDENTITIES);
+        }
+
+        proto.end(start);
+    }
 }
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index c45b416..0354300 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -34,9 +34,13 @@
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
 import android.os.Binder;
+import android.service.NetworkStatsCollectionKeyProto;
+import android.service.NetworkStatsCollectionProto;
+import android.service.NetworkStatsCollectionStatsProto;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.IntArray;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FileRotator;
@@ -532,12 +536,15 @@
                 / mBucketDuration);
     }
 
-    public void dump(IndentingPrintWriter pw) {
+    private ArrayList<Key> getSortedKeys() {
         final ArrayList<Key> keys = Lists.newArrayList();
         keys.addAll(mStats.keySet());
         Collections.sort(keys);
+        return keys;
+    }
 
-        for (Key key : keys) {
+    public void dump(IndentingPrintWriter pw) {
+        for (Key key : getSortedKeys()) {
             pw.print("ident="); pw.print(key.ident.toString());
             pw.print(" uid="); pw.print(key.uid);
             pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
@@ -550,6 +557,29 @@
         }
     }
 
+    public void writeToProto(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+
+        for (Key key : getSortedKeys()) {
+            final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
+
+            // Key
+            final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
+            key.ident.writeToProto(proto, NetworkStatsCollectionKeyProto.IDENTITY);
+            proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
+            proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
+            proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
+            proto.end(startKey);
+
+            // Value
+            final NetworkStatsHistory history = mStats.get(key);
+            history.writeToProto(proto, NetworkStatsCollectionStatsProto.HISTORY);
+            proto.end(startStats);
+        }
+
+        proto.end(start);
+    }
+
     public void dumpCheckin(PrintWriter pw, long start, long end) {
         dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
         dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index 090a076..80309e1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -29,9 +29,11 @@
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
 import android.os.DropBoxManager;
+import android.service.NetworkStatsRecorderProto;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.net.VpnInfo;
 import com.android.internal.util.FileRotator;
@@ -465,6 +467,15 @@
         }
     }
 
+    public void writeToProtoLocked(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+        if (mPending != null) {
+            proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES, mPending.getTotalBytes());
+        }
+        getOrLoadCompleteLocked().writeToProto(proto, NetworkStatsRecorderProto.COMPLETE_HISTORY);
+        proto.end(start);
+    }
+
     public void dumpCheckin(PrintWriter pw, long start, long end) {
         // Only load and dump stats from the requested window
         getOrLoadPartialLocked(start, end).dumpCheckin(pw, start, end);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 386e78b..104c296 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -104,6 +104,8 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.NetworkInterfaceProto;
+import android.service.NetworkStatsServiceDumpProto;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -115,6 +117,7 @@
 import android.util.Slog;
 import android.util.SparseIntArray;
 import android.util.TrustedTime;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.VpnInfo;
@@ -1255,6 +1258,12 @@
         final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, "  ");
 
         synchronized (mStatsLock) {
+            if (args.length > 0 && "--proto".equals(args[0])) {
+                // In this case ignore all other arguments.
+                dumpProto(fd);
+                return;
+            }
+
             if (poll) {
                 performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
                 pw.println("Forced poll");
@@ -1327,6 +1336,33 @@
         }
     }
 
+    private void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+        // TODO Right now it writes all history.  Should it limit to the "since-boot" log?
+
+        dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES, mActiveIfaces);
+        dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES, mActiveUidIfaces);
+        mDevRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS);
+        mXtRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.XT_STATS);
+        mUidRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.UID_STATS);
+        mUidTagRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.UID_TAG_STATS);
+
+        proto.flush();
+    }
+
+    private static void dumpInterfaces(ProtoOutputStream proto, long tag,
+            ArrayMap<String, NetworkIdentitySet> ifaces) {
+        for (int i = 0; i < ifaces.size(); i++) {
+            final long start = proto.start(tag);
+
+            proto.write(NetworkInterfaceProto.INTERFACE, ifaces.keyAt(i));
+            ifaces.valueAt(i).writeToProto(proto, NetworkInterfaceProto.IDENTITIES);
+
+            proto.end(start);
+        }
+    }
+
     /**
      * Return snapshot of current UID statistics, including any
      * {@link TrafficStats#UID_TETHERING} and {@link #mUidOperations} values.
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
new file mode 100644
index 0000000..4795fbf
--- /dev/null
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -0,0 +1,59 @@
+/**
+* Copyright (C) 2017 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.android.server.notification;
+
+import android.content.Context;
+import android.util.Slog;
+
+/**
+ * Determines whether a badge should be shown for this notification
+ */
+public class BadgeExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "BadgeExtractor";
+    private static final boolean DBG = false;
+
+    private RankingConfig mConfig;
+
+    public void initialize(Context ctx, NotificationUsageStats usageStats) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        if (mConfig == null) {
+            if (DBG) Slog.d(TAG, "missing config");
+            return null;
+        }
+        boolean appCanShowBadge =
+                mConfig.canShowBadge(record.sbn.getPackageName(), record.sbn.getUid());
+        if (!appCanShowBadge) {
+            record.setShowBadge(false);
+        } else {
+            record.setShowBadge(record.getChannel().canShowBadge() && appCanShowBadge);
+        }
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        mConfig = config;
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 1e0035d..6f49df4 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -30,7 +30,6 @@
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.Slog;
 
 import java.util.Comparator;
 import java.util.Objects;
@@ -57,7 +56,15 @@
 
     @Override
     public int compare(NotificationRecord left, NotificationRecord right) {
-        // First up: sufficiently important ongoing notifications of certain categories
+        // first all colorized notifications
+        boolean leftImportantColorized = isImportantColorized(left);
+        boolean rightImportantColorized = isImportantColorized(right);
+
+        if (leftImportantColorized != rightImportantColorized) {
+            return -1 * Boolean.compare(leftImportantColorized, rightImportantColorized);
+        }
+
+        // sufficiently important ongoing notifications of certain categories
         boolean leftImportantOngoing = isImportantOngoing(left);
         boolean rightImportantOngoing = isImportantOngoing(right);
 
@@ -106,6 +113,13 @@
         return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
     }
 
+    private boolean isImportantColorized(NotificationRecord record) {
+        if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
+            return false;
+        }
+        return record.getNotification().isColorized();
+    }
+
     private boolean isImportantOngoing(NotificationRecord record) {
         if (!isOngoing(record)) {
             return false;
@@ -148,7 +162,6 @@
         return (record.getNotification().flags & ongoingFlags) != 0;
     }
 
-
     private Class<? extends Notification.Style> getNotificationStyle(NotificationRecord record) {
         String templateClass =
                 record.getNotification().extras.getString(Notification.EXTRA_TEMPLATE);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0cac406..218b571 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -32,6 +32,7 @@
 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
 import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
 import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
+import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
@@ -50,9 +51,11 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
+import android.app.NotificationChannelGroup;
 import android.app.backup.BackupManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
@@ -165,6 +168,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -198,6 +202,8 @@
 
     static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
 
+    static final long SNOOZE_UNTIL_UNSPECIFIED = -1;
+
     static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
 
     static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
@@ -227,13 +233,21 @@
 
     private static final long DELAY_FOR_ASSISTANT_TIME = 100;
 
+    private static final String ACTION_NOTIFICATION_TIMEOUT =
+            NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
+    private static final int REQUEST_CODE_TIMEOUT = 1;
+    private static final String SCHEME_TIMEOUT = "timeout";
+    private static final String EXTRA_KEY = "key";
+
     private IActivityManager mAm;
     private IPackageManager mPackageManager;
+    private PackageManager mPackageManagerClient;
     AudioManager mAudioManager;
     AudioManagerInternal mAudioManagerInternal;
     @Nullable StatusBarManagerInternal mStatusBar;
     Vibrator mVibrator;
     private WindowManagerInternal mWindowManagerInternal;
+    private AlarmManager mAlarmManager;
 
     final IBinder mForegroundToken = new Binder();
     private Handler mHandler;
@@ -268,6 +282,7 @@
     private boolean mNotificationPulseEnabled;
 
     // used as a mutex for access to all active notifications & listeners
+    final Object mNotificationLock = new Object();
     final ArrayList<NotificationRecord> mNotificationList =
             new ArrayList<NotificationRecord>();
     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
@@ -372,7 +387,7 @@
 
     private void loadPolicyFile() {
         if (DBG) Slog.d(TAG, "loadPolicyFile");
-        synchronized(mPolicyFile) {
+        synchronized (mPolicyFile) {
 
             FileInputStream infile = null;
             try {
@@ -491,7 +506,7 @@
 
         @Override
         public void onSetDisabled(int status) {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 mDisableNotificationEffects =
                         (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
                 if (disableNotificationEffects(null) != null) {
@@ -519,7 +534,7 @@
 
         @Override
         public void onClearAll(int callingUid, int callingPid, int userId) {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
                         /*includeCurrentProfiles*/ true);
             }
@@ -527,7 +542,7 @@
 
         @Override
         public void onNotificationClick(int callingUid, int callingPid, String key) {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r == null) {
                     Log.w(TAG, "No notification with key: " + key);
@@ -548,7 +563,7 @@
         @Override
         public void onNotificationActionClick(int callingUid, int callingPid, String key,
                 int actionIndex) {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r == null) {
                     Log.w(TAG, "No notification with key: " + key);
@@ -584,7 +599,7 @@
 
         @Override
         public void clearEffects() {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 if (DBG) Slog.d(TAG, "clearEffects");
                 clearSoundLocked();
                 clearVibrateLocked();
@@ -601,7 +616,7 @@
                     REASON_DELEGATE_ERROR, null);
             long ident = Binder.clearCallingIdentity();
             try {
-                ActivityManager.getService().crashApplication(uid, initialPid, pkg,
+                ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
                         "Bad notification posted from package " + pkg
                         + ": " + message);
             } catch (RemoteException e) {
@@ -612,7 +627,7 @@
         @Override
         public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys,
                 NotificationVisibility[] noLongerVisibleKeys) {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 for (NotificationVisibility nv : newlyVisibleKeys) {
                     NotificationRecord r = mNotificationsByKey.get(nv.key);
                     if (r == null) continue;
@@ -635,7 +650,7 @@
         @Override
         public void onNotificationExpansionChanged(String key,
                 boolean userAction, boolean expanded) {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
                     r.stats.onExpansionChanged(userAction, expanded);
@@ -678,6 +693,29 @@
         updateLightsLocked();
     }
 
+    private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
+            if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
+                final NotificationRecord record;
+                synchronized (mNotificationLock) {
+                    record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
+                }
+                if (record != null) {
+                    cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
+                            record.sbn.getPackageName(), record.sbn.getTag(),
+                            record.sbn.getId(), 0,
+                            Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
+                            REASON_TIMEOUT, null);
+                }
+            }
+        }
+    };
+
     private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -702,16 +740,19 @@
                 int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                         UserHandle.USER_ALL);
                 String pkgList[] = null;
+                int uidList[] = null;
                 boolean removingPackage = queryRemove &&
                         !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                 if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                    uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                 } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     reason = REASON_PACKAGE_SUSPENDED;
                 } else if (queryRestart) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+                    uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
                 } else {
                     Uri uri = intent.getData();
                     if (uri == null) {
@@ -743,8 +784,8 @@
                         }
                     }
                     pkgList = new String[]{pkgName};
+                    uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
                 }
-
                 if (pkgList != null && (pkgList.length > 0)) {
                     for (String pkgName : pkgList) {
                         if (cancelNotifications) {
@@ -756,7 +797,8 @@
                 mListeners.onPackagesChanged(removingPackage, pkgList);
                 mNotificationAssistants.onPackagesChanged(removingPackage, pkgList);
                 mConditionProviders.onPackagesChanged(removingPackage, pkgList);
-                mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList);
+                mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
+                savePolicyFile();
             }
         }
     };
@@ -945,7 +987,8 @@
 
     // TODO: Tests should call onStart instead once the methods above are removed.
     @VisibleForTesting
-    void init(IPackageManager packageManager, LightsManager lightsManager) {
+    void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
+            LightsManager lightsManager, NotificationListeners notificationListeners) {
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -953,11 +996,13 @@
 
         mAm = ActivityManager.getService();
         mPackageManager = packageManager;
+        mPackageManagerClient = packageManagerClient;
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
         mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+        mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
 
-        mHandler = new WorkerHandler();
+        mHandler = new WorkerHandler(looper);
         mRankingThread.start();
         String[] extractorNames;
         try {
@@ -987,7 +1032,7 @@
                         new Intent(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL)
                                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT),
                         UserHandle.ALL, android.Manifest.permission.MANAGE_NOTIFICATIONS);
-                synchronized(mNotificationList) {
+                synchronized (mNotificationLock) {
                     updateInterruptionFilterLocked();
                 }
             }
@@ -1015,7 +1060,7 @@
         mGroupHelper = new GroupHelper(new GroupHelper.Callback() {
             @Override
             public void addAutoGroup(String key) {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     addAutogroupKeyLocked(key);
                 }
                 mRankingHandler.requestSort(false);
@@ -1023,7 +1068,7 @@
 
             @Override
             public void removeAutoGroup(String key) {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     removeAutogroupKeyLocked(key);
                 }
                 mRankingHandler.requestSort(false);
@@ -1036,7 +1081,7 @@
 
             @Override
             public void removeAutoGroupSummary(int userId, String pkg) {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     clearAutogroupSummaryLocked(userId, pkg);
                 }
             }
@@ -1047,8 +1092,8 @@
 
         syncBlockDb();
 
-        // This is a MangedServices object that keeps track of the listeners.
-        mListeners = new NotificationListeners();
+        // This is a ManagedServices object that keeps track of the listeners.
+        mListeners = notificationListeners;
 
         // This is a MangedServices object that keeps track of the assistant.
         mNotificationAssistants = new NotificationAssistants();
@@ -1122,6 +1167,10 @@
         getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
                 null);
 
+        IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
+        timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
+        getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
+
         mSettingsObserver = new SettingsObserver(mHandler);
 
         mArchive = new Archive(resources.getInteger(
@@ -1130,7 +1179,8 @@
 
     @Override
     public void onStart() {
-        init(AppGlobals.getPackageManager(), getLocalService(LightsManager.class));
+        init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
+                getLocalService(LightsManager.class), new NotificationListeners());
         publishBinderService(Context.NOTIFICATION_SERVICE, mService);
         publishLocalService(NotificationManagerInternal.class, mInternalService);
     }
@@ -1232,6 +1282,35 @@
         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
     }
 
+    private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
+            boolean fromAssistant) {
+        if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
+            // cancel
+            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
+                    UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
+                    null);
+        }
+        if (fromAssistant) {
+            mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
+        } else {
+            mRankingHelper.updateNotificationChannel(pkg, uid, channel);
+        }
+
+        synchronized (mNotificationList) {
+            final int N = mNotificationList.size();
+            for (int i = N - 1; i >= 0; --i) {
+                NotificationRecord r = mNotificationList.get(i);
+                if (channel.getId() != null && channel.getId().equals(r.getChannel().getId())) {
+                    r.updateNotificationChannel(mRankingHelper.getNotificationChannel(
+                            r.sbn.getPackageName(), r.getUser().getIdentifier(),
+                            channel.getId(), false));
+                }
+            }
+        }
+        mRankingHandler.requestSort(true);
+        savePolicyFile();
+    }
+
     private ArrayList<ComponentName> getSuppressors() {
         ArrayList<ComponentName> names = new ArrayList<ComponentName>();
         for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
@@ -1510,6 +1589,34 @@
         }
 
         @Override
+        public boolean canShowBadge(String pkg, int uid) {
+            checkCallerIsSystem();
+            return mRankingHelper.canShowBadge(pkg, uid);
+        }
+
+        @Override
+        public void setShowBadge(String pkg, int uid, boolean showBadge) {
+            checkCallerIsSystem();
+            mRankingHelper.setShowBadge(pkg, uid, showBadge);
+            savePolicyFile();
+        }
+
+        @Override
+        public void createNotificationChannelGroups(String pkg,
+                ParceledListSlice channelGroupList) throws RemoteException {
+            checkCallerIsSystemOrSameApp(pkg);
+            List<NotificationChannelGroup> groups = channelGroupList.getList();
+            final int groupSize = groups.size();
+            for (int i = 0; i < groupSize; i++) {
+                final NotificationChannelGroup group = groups.get(i);
+                Preconditions.checkNotNull(group, "group in list is null");
+                mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
+                        true /* fromTargetApp */);
+            }
+            savePolicyFile();
+        }
+
+        @Override
         public void createNotificationChannels(String pkg,
                 ParceledListSlice channelsList) throws RemoteException {
             checkCallerIsSystemOrSameApp(pkg);
@@ -1527,14 +1634,16 @@
         @Override
         public NotificationChannel getNotificationChannel(String pkg, String channelId) {
             checkCallerIsSystemOrSameApp(pkg);
-            return mRankingHelper.getNotificationChannel(pkg, Binder.getCallingUid(), channelId);
+            return mRankingHelper.getNotificationChannel(
+                    pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
         }
 
         @Override
         public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
-                String channelId) {
+                String channelId, boolean includeDeleted) {
             checkCallerIsSystem();
-            return mRankingHelper.getNotificationChannel(pkg, uid, channelId);
+            return mRankingHelper.getNotificationChannel
+                    (pkg, uid, channelId, includeDeleted);
         }
 
         @Override
@@ -1553,30 +1662,55 @@
         public void updateNotificationChannelForPackage(String pkg, int uid,
                 NotificationChannel channel) {
             enforceSystemOrSystemUI("Caller not system or systemui");
-            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
-                // cancel
-                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
-                        UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
-                        null);
-            }
-            mRankingHelper.updateNotificationChannel(pkg, uid, channel);
-            mRankingHandler.requestSort(true);
-            savePolicyFile();
+            Preconditions.checkNotNull(channel);
+            updateNotificationChannelInt(pkg, uid, channel, false);
         }
 
         @Override
         public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
-                int uid) {
+                int uid, boolean includeDeleted) {
             checkCallerIsSystem();
-            return mRankingHelper.getNotificationChannels(pkg, uid);
+            return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
+        }
+
+        @Override
+        public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
+                String pkg, int uid, boolean includeDeleted) {
+            checkCallerIsSystem();
+            return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
         }
 
         @Override
         public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
-            return mRankingHelper.getNotificationChannels(pkg, Binder.getCallingUid());
+            return mRankingHelper.getNotificationChannels(
+                    pkg, Binder.getCallingUid(), false /* includeDeleted */);
         }
 
+
+        @Override
+        public void clearData(String packageName, int uid) throws RemoteException {
+            checkCallerIsSystem();
+
+            // Cancel posted notifications
+            cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
+                    UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
+
+            // Listener & assistant
+            mListeners.onPackagesChanged(true, new String[] {packageName});
+            mNotificationAssistants.onPackagesChanged(true, new String[] {packageName});
+
+            // Zen
+            mConditionProviders.onPackagesChanged(true, new String[] {packageName});
+
+            // Reset notification preferences
+            mRankingHelper.onPackagesChanged(true, UserHandle.getCallingUserId(),
+                    new String[] {packageName}, new int[] {uid});
+
+            savePolicyFile();
+        }
+
+
         /**
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
@@ -1596,7 +1730,7 @@
             // noteOp will check to make sure the callingPkg matches the uid
             if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
                     == AppOpsManager.MODE_ALLOWED) {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     tmp = new StatusBarNotification[mNotificationList.size()];
                     final int N = mNotificationList.size();
                     for (int i=0; i<N; i++) {
@@ -1610,6 +1744,9 @@
         /**
          * Public API for getting a list of current notifications for the calling package/uid.
          *
+         * Note that since notification posting is done asynchronously, this will not return
+         * notifications that are in the process of being posted.
+         *
          * @returns A list of all the package's notifications, in natural order.
          */
         @Override
@@ -1622,7 +1759,7 @@
             final ArrayMap<String, StatusBarNotification> map
                     = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
 
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 final int N = mNotificationList.size();
                 for (int i = 0; i < N; i++) {
                     StatusBarNotification sbn = sanitizeSbn(pkg, userId,
@@ -1637,10 +1774,8 @@
                         map.put(sbn.getKey(), sbn);
                     }
                 }
-            }
-            synchronized (mEnqueuedNotifications) {
-                final int N = mEnqueuedNotifications.size();
-                for (int i = 0; i < N; i++) {
+                final int M = mEnqueuedNotifications.size();
+                for (int i = 0; i < M; i++) {
                     StatusBarNotification sbn = sanitizeSbn(pkg, userId,
                             mEnqueuedNotifications.get(i).sbn);
                     if (sbn != null) {
@@ -1665,7 +1800,6 @@
                 return new StatusBarNotification(
                         sbn.getPackageName(),
                         sbn.getOpPkg(),
-                        sbn.getNotificationChannel(),
                         sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
                         sbn.getNotification().clone(),
                         sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
@@ -1732,7 +1866,7 @@
             final int callingPid = Binder.getCallingPid();
             long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                     if (keys != null) {
                         final int N = keys.length;
@@ -1795,7 +1929,7 @@
         public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
             long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                     if (keys != null) {
                         final int N = keys.length;
@@ -1850,7 +1984,7 @@
             long identity = Binder.clearCallingIdentity();
             try {
                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                snoozeNotificationInt(key, snoozeCriterionId, info);
+                snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -1867,38 +2001,23 @@
             long identity = Binder.clearCallingIdentity();
             try {
                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                snoozeNotificationInt(key, snoozeUntil, info);
+                snoozeNotificationInt(key, snoozeUntil, null, info);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
 
         /**
-         * Allow an INotificationListener to snooze a single notification.
+         * Allows the notification assistant to un-snooze a single notification.
          *
-         * @param token The binder for the listener, to check that the caller is allowed
+         * @param token The binder for the assistant, to check that the caller is allowed
          */
         @Override
-        public void snoozeNotificationFromListener(INotificationListener token, String key) {
+        public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
             long identity = Binder.clearCallingIdentity();
             try {
-                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                snoozeNotificationInt(key, info);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        /**
-         * Allow an INotificationListener to un-snooze a single notification.
-         *
-         * @param token The binder for the listener, to check that the caller is allowed
-         */
-        @Override
-        public void unsnoozeNotificationFromListener(INotificationListener token, String key) {
-            long identity = Binder.clearCallingIdentity();
-            try {
-                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                final ManagedServiceInfo info =
+                        mNotificationAssistants.checkServiceTokenLocked(token);
                 unsnoozeNotificationInt(key, info);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -1919,7 +2038,7 @@
             final int callingPid = Binder.getCallingPid();
             long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                     if (info.supportsProfiles()) {
                         Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
@@ -1948,7 +2067,7 @@
         @Override
         public ParceledListSlice<StatusBarNotification> getActiveNotificationsFromListener(
                 INotificationListener token, String[] keys, int trim) {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                 final boolean getKeys = keys != null;
                 final int N = getKeys ? keys.length : mNotificationList.size();
@@ -1969,11 +2088,41 @@
             }
         }
 
+        /**
+         * Allow an INotificationListener to request the list of outstanding snoozed notifications
+         * seen by the current user. Useful when starting up, after which point the listener
+         * callbacks should be used.
+         *
+         * @param token The binder for the listener, to check that the caller is allowed
+         * @returns The return value will contain the notifications specified in keys, in that
+         *      order, or if keys is null, all the notifications, in natural order.
+         */
+        @Override
+        public ParceledListSlice<StatusBarNotification> getSnoozedNotificationsFromListener(
+                INotificationListener token, int trim) {
+            synchronized (mNotificationLock) {
+                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                List<NotificationRecord> snoozedRecords = mSnoozeHelper.getSnoozed();
+                final int N = snoozedRecords.size();
+                final ArrayList<StatusBarNotification> list = new ArrayList<>(N);
+                for (int i=0; i < N; i++) {
+                    final NotificationRecord r = snoozedRecords.get(i);
+                    if (r == null) continue;
+                    StatusBarNotification sbn = r.sbn;
+                    if (!isVisibleToListener(sbn, info)) continue;
+                    StatusBarNotification sbnToSend =
+                            (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
+                    list.add(sbnToSend);
+                }
+                return new ParceledListSlice<>(list);
+            }
+        }
+
         @Override
         public void requestHintsFromListener(INotificationListener token, int hints) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                     final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
                             | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
@@ -1994,7 +2143,7 @@
 
         @Override
         public int getHintsFromListener(INotificationListener token) {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 return mListenerHints;
             }
         }
@@ -2004,7 +2153,7 @@
                 int interruptionFilter) throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                     mZenModeHelper.requestFromListener(info.component, interruptionFilter);
                     updateInterruptionFilterLocked();
@@ -2025,7 +2174,7 @@
         @Override
         public void setOnNotificationPostedTrimFromListener(INotificationListener token, int trim)
                 throws RemoteException {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                 if (info == null) return;
                 mListeners.setOnNotificationPostedTrimLocked(info, trim);
@@ -2344,7 +2493,7 @@
             enforceSystemOrSystemUI("grant notification policy access");
             final long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     mPolicyAccess.put(pkg, granted);
                 }
             } finally {
@@ -2379,7 +2528,7 @@
                 Adjustment adjustment) throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mEnqueuedNotifications) {
+                synchronized (mNotificationLock) {
                     mNotificationAssistants.checkServiceTokenLocked(token);
                     int N = mEnqueuedNotifications.size();
                     for (int i = 0; i < N; i++) {
@@ -2401,7 +2550,7 @@
                 Adjustment adjustment) throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     mNotificationAssistants.checkServiceTokenLocked(token);
                     NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
                     applyAdjustment(n, adjustment);
@@ -2418,7 +2567,7 @@
 
             final long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
+                synchronized (mNotificationLock) {
                     mNotificationAssistants.checkServiceTokenLocked(token);
                     for (Adjustment adjustment : adjustments) {
                         NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
@@ -2459,15 +2608,9 @@
         public void updateNotificationChannelFromAssistant(INotificationListener token, String pkg,
                 NotificationChannel channel) throws RemoteException {
             ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
-            if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
-                // cancel
-                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
-                        info.userid, REASON_CHANNEL_BANNED, null);
-            }
+            Preconditions.checkNotNull(channel);
             int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
-            mRankingHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
-            mRankingHandler.requestSort(true);
-            savePolicyFile();
+            updateNotificationChannelInt(pkg, uid, channel, true);
         }
 
         @Override
@@ -2475,7 +2618,7 @@
                 INotificationListener token, String pkg) throws RemoteException {
             ManagedServiceInfo info = mNotificationAssistants.checkServiceTokenLocked(token);
             int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
-            return mRankingHelper.getNotificationChannels(pkg, uid);
+            return mRankingHelper.getNotificationChannels(pkg, uid, false /* includeDeleted */);
         }
     };
 
@@ -2492,8 +2635,9 @@
             final ArrayList<SnoozeCriterion> snoozeCriterionList =
                     adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
             if (!TextUtils.isEmpty(overrideChannelId)) {
-                n.setNotificationChannelOverride(mRankingHelper.getNotificationChannel(
-                        n.sbn.getPackageName(), n.sbn.getUid(), overrideChannelId));
+                n.updateNotificationChannel(mRankingHelper.getNotificationChannel(
+                        n.sbn.getPackageName(), n.sbn.getUid(), overrideChannelId,
+                        false /* includeDeleted */));
             }
             n.setPeopleOverride(people);
             n.setSnoozeCriteria(snoozeCriterionList);
@@ -2523,9 +2667,8 @@
         ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
         if (summaries != null && summaries.containsKey(pkg)) {
             // Clear summary.
-            final NotificationRecord removed = mNotificationsByKey.get(summaries.remove(pkg));
+            final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
             if (removed != null) {
-                mNotificationList.remove(removed);
                 cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
             }
         }
@@ -2534,7 +2677,7 @@
     // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
     private void createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
         NotificationRecord summaryRecord = null;
-        synchronized (mNotificationList) {
+        synchronized (mNotificationLock) {
             NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
             if (notificationRecord == null) {
                 // The notification could have been cancelled again already. A successive
@@ -2574,18 +2717,18 @@
                 final StatusBarNotification summarySbn =
                         new StatusBarNotification(adjustedSbn.getPackageName(),
                                 adjustedSbn.getOpPkg(),
-                                adjustedSbn.getNotificationChannel(),
                                 Integer.MAX_VALUE,
                                 GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(),
                                 adjustedSbn.getInitialPid(), summaryNotification,
                                 adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
                                 System.currentTimeMillis());
-                summaryRecord = new NotificationRecord(getContext(), summarySbn);
+                summaryRecord = new NotificationRecord(getContext(), summarySbn,
+                        notificationRecord.getChannel());
                 summaries.put(pkg, summarySbn.getKey());
             }
         }
         if (summaryRecord != null) {
-            synchronized (mEnqueuedNotifications) {
+            synchronized (mNotificationLock) {
                 mEnqueuedNotifications.add(summaryRecord);
             }
             mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
@@ -2640,7 +2783,7 @@
             }
         }
 
-        synchronized (mNotificationList) {
+        synchronized (mNotificationLock) {
             if (!zenOnly) {
                 N = mNotificationList.size();
                 if (N > 0) {
@@ -2678,19 +2821,17 @@
                 }
                 pw.println("  mArchive=" + mArchive.toString());
                 Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
-                int i=0;
+                int j=0;
                 while (iter.hasNext()) {
                     final StatusBarNotification sbn = iter.next();
                     if (filter != null && !filter.matches(sbn)) continue;
                     pw.println("    " + sbn);
-                    if (++i >= 5) {
+                    if (++j >= 5) {
                         if (iter.hasNext()) pw.println("    ...");
                         break;
                     }
                 }
-            }
 
-            synchronized (mEnqueuedNotifications) {
                 if (!zenOnly) {
                     N = mEnqueuedNotifications.size();
                     if (N > 0) {
@@ -2785,14 +2926,14 @@
         public void removeForegroundServiceFlagFromNotification(String pkg, int notificationId,
                 int userId) {
             checkCallerIsSystem();
-            synchronized (mNotificationList) {
-                int i = indexOfNotificationLocked(pkg, null, notificationId, userId);
-                if (i < 0) {
+            synchronized (mNotificationLock) {
+                NotificationRecord r = findNotificationByListLocked(mNotificationList, pkg, null,
+                        notificationId, userId);
+                if (r == null) {
                     Log.d(TAG, "stripForegroundServiceFlag: Could not find notification with "
                             + "pkg=" + pkg + " / id=" + notificationId + " / userId=" + userId);
                     return;
                 }
-                NotificationRecord r = mNotificationList.get(i);
                 StatusBarNotification sbn = r.sbn;
                 // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
                 // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove FLAG_FOREGROUND_SERVICE,
@@ -2829,7 +2970,7 @@
 
         // Fix the notification as best we can.
         try {
-            final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
+            final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
                     pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                     (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
             Notification.addFieldsFromContext(ai, userId, notification);
@@ -2846,16 +2987,16 @@
                     + " id=" + id + " notification=" + notification);
         }
         final NotificationChannel channel =  mRankingHelper.getNotificationChannelWithFallback(pkg,
-                callingUid, notification.getChannel());
+                callingUid, notification.getChannel(), false /* includeDeleted */);
         final StatusBarNotification n = new StatusBarNotification(
-                pkg, opPkg, channel, id, tag, callingUid, callingPid, notification,
+                pkg, opPkg, id, tag, callingUid, callingPid, notification,
                 user, null, System.currentTimeMillis());
 
         // Limit the number of notifications that any given package except the android
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
-            synchronized (mNotificationList) {
-                if(mNotificationsByKey.get(n.getKey()) != null) {
+            synchronized (mNotificationLock) {
+                if (mNotificationsByKey.get(n.getKey()) != null) {
                     // this is an update, rate limit updates only
                     final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
                     if (appEnqueueRate > mMaxPackageEnqueueRate) {
@@ -2912,10 +3053,7 @@
                 Notification.PRIORITY_MAX);
 
         // setup local book-keeping
-        final NotificationRecord r = new NotificationRecord(getContext(), n);
-        synchronized (mEnqueuedNotifications) {
-            mEnqueuedNotifications.add(r);
-        }
+        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
         mHandler.post(new EnqueueNotificationRunnable(userId, r));
 
         idOut[0] = id;
@@ -2932,7 +3070,10 @@
 
         @Override
         public void run() {
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
+                mEnqueuedNotifications.add(r);
+                scheduleTimeoutLocked(r);
+
                 if (mSnoozeHelper.isSnoozed(userId, r.sbn.getPackageName(), r.getKey())) {
                     // TODO: log to event log
                     if (DBG) {
@@ -3027,9 +3168,9 @@
 
         @Override
         public void run() {
-            try {
-                NotificationRecord r = null;
-                synchronized (mEnqueuedNotifications) {
+            synchronized (mNotificationLock) {
+                try {
+                    NotificationRecord r = null;
                     int N = mEnqueuedNotifications.size();
                     for (int i = 0; i < N; i++) {
                         final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
@@ -3038,12 +3179,10 @@
                             break;
                         }
                     }
-                }
-                if (r == null) {
-                    Slog.e(TAG, "Cannot find enqueued record for key: " + key);
-                    return;
-                }
-                synchronized (mNotificationList) {
+                    if (r == null) {
+                        Slog.i(TAG, "Cannot find enqueued record for key: " + key);
+                        return;
+                    }
                     NotificationRecord old = mNotificationsByKey.get(key);
                     final StatusBarNotification n = r.sbn;
                     final Notification notification = n.getNotification();
@@ -3102,9 +3241,7 @@
                     }
 
                     buzzBeepBlinkLocked(r);
-                }
-            } finally {
-                synchronized (mEnqueuedNotifications) {
+                } finally {
                     int N = mEnqueuedNotifications.size();
                     for (int i = 0; i < N; i++) {
                         final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
@@ -3161,8 +3298,23 @@
         // notification was a summary and the new one isn't, or when the old
         // notification was a summary and its group key changed.
         if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) {
-            cancelGroupChildrenLocked(old, callingUid, callingPid, null,
-                    REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
+            cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */);
+        }
+    }
+
+    @VisibleForTesting
+    void scheduleTimeoutLocked(NotificationRecord record) {
+        if (record.getNotification().getTimeout() > System.currentTimeMillis()) {
+            final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
+                    REQUEST_CODE_TIMEOUT,
+                    new Intent(ACTION_NOTIFICATION_TIMEOUT)
+                            .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
+                                    .appendPath(record.getKey()).build())
+                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                            .putExtra(EXTRA_KEY, record.getKey()),
+                    PendingIntent.FLAG_UPDATE_CURRENT);
+            mAlarmManager.setExactAndAllowWhileIdle(
+                    AlarmManager.RTC_WAKEUP, record.getNotification().getTimeout(), pi);
         }
     }
 
@@ -3289,7 +3441,9 @@
     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
-        if (!mAudioManager.isAudioFocusExclusive()) {
+        // or the device is in vibrate mode
+        if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal()
+                != AudioManager.RINGER_MODE_VIBRATE) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
@@ -3429,7 +3583,7 @@
         RankingReconsideration recon = (RankingReconsideration) message.obj;
         recon.run();
         boolean changed;
-        synchronized (mNotificationList) {
+        synchronized (mNotificationLock) {
             final NotificationRecord record = mNotificationsByKey.get(recon.getKey());
             if (record == null) {
                 return;
@@ -3457,16 +3611,20 @@
     private void handleRankingSort(Message msg) {
         if (!(msg.obj instanceof Boolean)) return;
         boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
-        synchronized (mNotificationList) {
+        synchronized (mNotificationLock) {
             final int N = mNotificationList.size();
+            // Any field that can change via one of the extractors or by the assistant
+            // needs to be added here.
             ArrayList<String> orderBefore = new ArrayList<String>(N);
             ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
             int[] visibilities = new int[N];
+            boolean[] showBadges = new boolean[N];
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
                 groupOverrideBefore.add(r.sbn.getGroupKey());
                 visibilities[i] = r.getPackageVisibilityOverride();
+                showBadges[i] = r.canShowBadge();
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
@@ -3475,7 +3633,8 @@
                 if (forceUpdate
                         || !orderBefore.get(i).equals(r.getKey())
                         || visibilities[i] != r.getPackageVisibilityOverride()
-                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
+                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
+                        || showBadges[i] != r.canShowBadge()) {
                     scheduleSendRankingUpdate();
                     return;
                 }
@@ -3514,7 +3673,7 @@
     }
 
     private void handleSendRankingUpdate() {
-        synchronized (mNotificationList) {
+        synchronized (mNotificationLock) {
             mListeners.notifyRankingUpdateLocked();
         }
     }
@@ -3533,19 +3692,23 @@
     }
 
     private void handleListenerHintsChanged(int hints) {
-        synchronized (mNotificationList) {
+        synchronized (mNotificationLock) {
             mListeners.notifyListenerHintsChangedLocked(hints);
         }
     }
 
     private void handleListenerInterruptionFilterChanged(int interruptionFilter) {
-        synchronized (mNotificationList) {
+        synchronized (mNotificationLock) {
             mListeners.notifyInterruptionFilterChanged(interruptionFilter);
         }
     }
 
     private final class WorkerHandler extends Handler
     {
+        public WorkerHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg)
         {
@@ -3631,6 +3794,17 @@
     }
 
     private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
+        final String canceledKey = r.getKey();
+
+        // Remove from either list
+        boolean wasPosted;
+        if (mNotificationList.remove(r)) {
+            mNotificationsByKey.remove(r.sbn.getKey());
+            wasPosted = true;
+        } else {
+            mEnqueuedNotifications.remove(r);
+            wasPosted = false;
+        }
 
         // Record caller.
         recordCallerLocked(r);
@@ -3648,50 +3822,51 @@
             }
         }
 
-        // status bar
-        if (r.getNotification().getSmallIcon() != null) {
-            r.isCanceled = true;
-            mListeners.notifyRemovedLocked(r.sbn, reason);
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mGroupHelper.onNotificationRemoved(r.sbn);
+        // Only cancel these if this notification actually got to be posted.
+        if (wasPosted) {
+            // status bar
+            if (r.getNotification().getSmallIcon() != null) {
+                r.isCanceled = true;
+                mListeners.notifyRemovedLocked(r.sbn, reason);
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mGroupHelper.onNotificationRemoved(r.sbn);
+                    }
+                });
+            }
+
+            // sound
+            if (canceledKey.equals(mSoundNotificationKey)) {
+                mSoundNotificationKey = null;
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+                    if (player != null) {
+                        player.stopAsync();
+                    }
+                } catch (RemoteException e) {
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
                 }
-            });
-        }
+            }
 
-        final String canceledKey = r.getKey();
-
-        // sound
-        if (canceledKey.equals(mSoundNotificationKey)) {
-            mSoundNotificationKey = null;
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
-                if (player != null) {
-                    player.stopAsync();
+            // vibrate
+            if (canceledKey.equals(mVibrateNotificationKey)) {
+                mVibrateNotificationKey = null;
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    mVibrator.cancel();
                 }
-            } catch (RemoteException e) {
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+                finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
             }
-        }
 
-        // vibrate
-        if (canceledKey.equals(mVibrateNotificationKey)) {
-            mVibrateNotificationKey = null;
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mVibrator.cancel();
-            }
-            finally {
-                Binder.restoreCallingIdentity(identity);
-            }
+            // light
+            mLights.remove(canceledKey);
         }
 
-        // light
-        mLights.remove(canceledKey);
-
         // Record usage stats
         // TODO: add unbundling stats?
         switch (reason) {
@@ -3707,10 +3882,9 @@
                 break;
         }
 
-        mNotificationsByKey.remove(r.sbn.getKey());
         String groupKey = r.getGroupKey();
         NotificationRecord groupSummary = mSummaryByGroupKey.get(groupKey);
-        if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
+        if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
             mSummaryByGroupKey.remove(groupKey);
         }
         final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
@@ -3745,10 +3919,11 @@
                 if (DBG) EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, id, tag,
                         userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
 
-                synchronized (mNotificationList) {
-                    int index = indexOfNotificationLocked(pkg, tag, id, userId);
-                    if (index >= 0) {
-                        NotificationRecord r = mNotificationList.get(index);
+                synchronized (mNotificationLock) {
+                    // Look for the notification, searching both the posted and enqueued lists.
+                    NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
+                    if (r != null) {
+                        // The notification was found, check if it should be removed.
 
                         // Ideally we'd do this in the caller of this method. However, that would
                         // require the caller to also find the notification.
@@ -3763,13 +3938,13 @@
                             return;
                         }
 
-                        mNotificationList.remove(index);
-
+                        // Cancel the notification.
                         cancelNotificationLocked(r, sendDelete, reason);
                         cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
-                                REASON_GROUP_SUMMARY_CANCELED, sendDelete);
+                                sendDelete);
                         updateLightsLocked();
                     } else {
+                        // No notification was found, assume that it is snoozed and cancel it.
                         final boolean wasSnoozed = mSnoozeHelper.cancel(userId, pkg, tag, id);
                         if (wasSnoozed) {
                             savePolicyFile();
@@ -3808,120 +3983,129 @@
      * Cancels all notifications from a given package that have all of the
      * {@code mustHaveFlags}.
      */
-    boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
+    void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
             int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
             ManagedServiceInfo listener) {
-        String listenerName = listener == null ? null : listener.component.toShortString();
-        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
-                pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
-                listenerName);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                String listenerName = listener == null ? null : listener.component.toShortString();
+                EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
+                        pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
+                        listenerName);
 
-        synchronized (mNotificationList) {
-            final int N = mNotificationList.size();
-            ArrayList<NotificationRecord> canceledNotifications = null;
-            for (int i = N-1; i >= 0; --i) {
-                NotificationRecord r = mNotificationList.get(i);
-                if (!notificationMatchesUserId(r, userId)) {
-                    continue;
-                }
-                // Don't remove notifications to all, if there's no package name specified
-                if (r.getUserId() == UserHandle.USER_ALL && pkg == null) {
-                    continue;
-                }
-                if ((r.getFlags() & mustHaveFlags) != mustHaveFlags) {
-                    continue;
-                }
-                if ((r.getFlags() & mustNotHaveFlags) != 0) {
-                    continue;
-                }
-                if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
-                    continue;
-                }
-                if (channelId != null && !channelId.equals(r.getChannel().getId())) {
-                    continue;
-                }
-                if (canceledNotifications == null) {
-                    canceledNotifications = new ArrayList<>();
-                }
-                canceledNotifications.add(r);
+                // Why does this parameter exist? Do we actually want to execute the above if doit
+                // is false?
                 if (!doit) {
-                    return true;
+                    return;
                 }
-                mNotificationList.remove(i);
-                cancelNotificationLocked(r, false, reason);
-            }
-            mSnoozeHelper.cancel(userId, pkg);
-            if (doit && canceledNotifications != null) {
-                final int M = canceledNotifications.size();
-                for (int i = 0; i < M; i++) {
-                    cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
-                            listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
+
+                synchronized (mNotificationLock) {
+                    FlagChecker flagChecker = (int flags) -> {
+                        if ((flags & mustHaveFlags) != mustHaveFlags) {
+                            return false;
+                        }
+                        if ((flags & mustNotHaveFlags) != 0) {
+                            return false;
+                        }
+                        return true;
+                    };
+
+                    cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
+                            pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
+                            false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
+                            listenerName);
+                    cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
+                            callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
+                            flagChecker, false /*includeCurrentProfiles*/, userId,
+                            false /*sendDelete*/, reason, listenerName);
+                    mSnoozeHelper.cancel(userId, pkg);
                 }
             }
-            if (canceledNotifications != null) {
-                updateLightsLocked();
+        });
+    }
+
+    private interface FlagChecker {
+        // Returns false if these flags do not pass the defined flag test.
+        public boolean apply(int flags);
+    }
+
+    private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
+            int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
+            String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
+            boolean sendDelete, int reason, String listenerName) {
+        ArrayList<NotificationRecord> canceledNotifications = null;
+        for (int i = notificationList.size() - 1; i >= 0; --i) {
+            NotificationRecord r = notificationList.get(i);
+            if (includeCurrentProfiles) {
+                if (!notificationMatchesCurrentProfiles(r, userId)) {
+                    continue;
+                }
+            } else if (!notificationMatchesUserId(r, userId)) {
+                continue;
             }
-            return canceledNotifications != null;
+            // Don't remove notifications to all, if there's no package name specified
+            if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
+                continue;
+            }
+            if (!flagChecker.apply(r.getFlags())) {
+                continue;
+            }
+            if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
+                continue;
+            }
+            if (channelId != null && !channelId.equals(r.getChannel().getId())) {
+                continue;
+            }
+
+            if (canceledNotifications == null) {
+                canceledNotifications = new ArrayList<>();
+            }
+            canceledNotifications.add(r);
+            cancelNotificationLocked(r, sendDelete, reason);
+        }
+        if (canceledNotifications != null) {
+            final int M = canceledNotifications.size();
+            for (int i = 0; i < M; i++) {
+                cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
+                        listenerName, false /* sendDelete */);
+            }
+            updateLightsLocked();
         }
     }
 
-    void snoozeNotificationInt(String key, String snoozeCriterionId, ManagedServiceInfo listener) {
+    void snoozeNotificationInt(String key, long until, String snoozeCriterionId,
+            ManagedServiceInfo listener) {
         String listenerName = listener == null ? null : listener.component.toShortString();
-        // TODO: write to event log
-        if (DBG) {
-            Slog.d(TAG, String.format("snooze event(%s, %s, %s)",
-                    key, snoozeCriterionId, listenerName));
-        }
-        synchronized (mNotificationList) {
-            final NotificationRecord r = mNotificationsByKey.get(key);
-            if (r != null) {
-                mNotificationList.remove(r);
-                cancelNotificationLocked(r, false, REASON_SNOOZED);
-                mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, snoozeCriterionId);
-                updateLightsLocked();
-                mSnoozeHelper.snooze(r);
-                savePolicyFile();
-            }
-        }
-    }
-
-    void snoozeNotificationInt(String key, long until, ManagedServiceInfo listener) {
-        String listenerName = listener == null ? null : listener.component.toShortString();
-        // TODO: write to event log
-        if (DBG) {
-            Slog.d(TAG, String.format("snooze event(%s, %d, %s)", key, until, listenerName));
-        }
-        if (until < System.currentTimeMillis()) {
+        if (until < System.currentTimeMillis() && snoozeCriterionId == null) {
             return;
         }
-        synchronized (mNotificationList) {
-            final NotificationRecord r = mNotificationsByKey.get(key);
-            if (r != null) {
-                mNotificationList.remove(r);
-                cancelNotificationLocked(r, false, REASON_SNOOZED);
-                updateLightsLocked();
-                mSnoozeHelper.snooze(r, until);
-                savePolicyFile();
-            }
-        }
-    }
-
-    void snoozeNotificationInt(String key, ManagedServiceInfo listener) {
-        String listenerName = listener == null ? null : listener.component.toShortString();
         // TODO: write to event log
         if (DBG) {
-            Slog.d(TAG, String.format("snooze event(%s, %s)", key, listenerName));
+            Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, until, snoozeCriterionId,
+                    listenerName));
         }
-        synchronized (mNotificationList) {
-            final NotificationRecord r = mNotificationsByKey.get(key);
-            if (r != null) {
-                mNotificationList.remove(r);
-                cancelNotificationLocked(r, false, REASON_SNOOZED);
-                updateLightsLocked();
-                mSnoozeHelper.snooze(r);
-                savePolicyFile();
+        // Needs to post so that it can cancel notifications not yet enqueued.
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mNotificationLock) {
+                    final NotificationRecord r = findNotificationByKeyLocked(key);
+                    if (r != null) {
+                        cancelNotificationLocked(r, false, REASON_SNOOZED);
+                        updateLightsLocked();
+                        if (snoozeCriterionId != null) {
+                            mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn,
+                                    snoozeCriterionId);
+                            mSnoozeHelper.snooze(r);
+                        } else {
+                            mSnoozeHelper.snooze(r, until);
+                        }
+                        savePolicyFile();
+                    }
+                }
             }
-        }
+        });
     }
 
     void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
@@ -3936,47 +4120,40 @@
 
     void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
             ManagedServiceInfo listener, boolean includeCurrentProfiles) {
-        String listenerName = listener == null ? null : listener.component.toShortString();
-        EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
-                null, userId, 0, 0, reason, listenerName);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mNotificationLock) {
+                    String listenerName =
+                            listener == null ? null : listener.component.toShortString();
+                    EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
+                            null, userId, 0, 0, reason, listenerName);
 
-        ArrayList<NotificationRecord> canceledNotifications = null;
-        final int N = mNotificationList.size();
-        for (int i=N-1; i>=0; i--) {
-            NotificationRecord r = mNotificationList.get(i);
-            if (includeCurrentProfiles) {
-                if (!notificationMatchesCurrentProfiles(r, userId)) {
-                    continue;
-                }
-            } else {
-                if (!notificationMatchesUserId(r, userId)) {
-                    continue;
+                    FlagChecker flagChecker = (int flags) -> {
+                        if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
+                                != 0) {
+                            return false;
+                        }
+                        return true;
+                    };
+
+                    cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
+                            null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
+                            includeCurrentProfiles, userId, true /*sendDelete*/, reason,
+                            listenerName);
+                    cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
+                            callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
+                            flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
+                            reason, listenerName);
+                    mSnoozeHelper.cancel(userId, includeCurrentProfiles);
                 }
             }
-
-            if ((r.getFlags() & (Notification.FLAG_ONGOING_EVENT
-                            | Notification.FLAG_NO_CLEAR)) == 0) {
-                mNotificationList.remove(i);
-                cancelNotificationLocked(r, true, reason);
-                // Make a note so we can cancel children later.
-                if (canceledNotifications == null) {
-                    canceledNotifications = new ArrayList<>();
-                }
-                canceledNotifications.add(r);
-            }
-        }
-        mSnoozeHelper.cancel(userId, includeCurrentProfiles);
-        int M = canceledNotifications != null ? canceledNotifications.size() : 0;
-        for (int i = 0; i < M; i++) {
-            cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
-                    listenerName, REASON_GROUP_SUMMARY_CANCELED, false /* sendDelete */);
-        }
-        updateLightsLocked();
+        });
     }
 
     // Warning: The caller is responsible for invoking updateLightsLocked().
     private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid,
-            String listenerName, int reason, boolean sendDelete) {
+            String listenerName, boolean sendDelete) {
         Notification n = r.getNotification();
         if (!n.isGroupSummary()) {
             return;
@@ -3990,15 +4167,26 @@
             return;
         }
 
-        final int N = mNotificationList.size();
-        for (int i = N - 1; i >= 0; i--) {
-            NotificationRecord childR = mNotificationList.get(i);
-            StatusBarNotification childSbn = childR.sbn;
+        cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
+                sendDelete);
+        cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
+                listenerName, sendDelete);
+    }
+
+    private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
+            NotificationRecord parentNotification, int callingUid, int callingPid,
+            String listenerName, boolean sendDelete) {
+        final String pkg = parentNotification.sbn.getPackageName();
+        final int userId = parentNotification.getUserId();
+        final int reason = REASON_GROUP_SUMMARY_CANCELED;
+        for (int i = notificationList.size() - 1; i >= 0; i--) {
+            final NotificationRecord childR = notificationList.get(i);
+            final StatusBarNotification childSbn = childR.sbn;
             if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
-                    childR.getGroupKey().equals(r.getGroupKey())) {
+                    childR.getGroupKey().equals(parentNotification.getGroupKey())
+                    && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
                 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
                         childSbn.getTag(), userId, 0, 0, reason, listenerName);
-                mNotificationList.remove(i);
                 cancelNotificationLocked(childR, sendDelete, reason);
             }
         }
@@ -4047,19 +4235,49 @@
         }
     }
 
-    // lock on mNotificationList
-    int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
+    // Searches both enqueued and posted notifications by key.
+    // TODO: need to combine a bunch of these getters with slightly different behavior.
+    // TODO: Should enqueuing just add to mNotificationsByKey instead?
+    private NotificationRecord findNotificationByKeyLocked(String key) {
+        final int N = mNotificationList.size();
+        for (int i = 0; i < N; i++) {
+            if (key.equals(mNotificationList.get(i).getKey())) {
+                return mNotificationList.get(i);
+            }
+        }
+        final int M = mEnqueuedNotifications.size();
+        for (int i = 0; i < M; i++) {
+            if (key.equals(mEnqueuedNotifications.get(i).getKey())) {
+                return mEnqueuedNotifications.get(i);
+            }
+        }
+        return null;
+    }
+
+    private NotificationRecord findNotificationLocked(String pkg, String tag, int id, int userId) {
+        NotificationRecord r;
+        if ((r = findNotificationByListLocked(mNotificationList, pkg, tag, id, userId)) != null) {
+            return r;
+        }
+        if ((r = findNotificationByListLocked(mEnqueuedNotifications, pkg, tag, id, userId))
+                != null) {
+            return r;
+        }
+        return null;
+    }
+
+    private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list,
+            String pkg, String tag, int id, int userId)
     {
-        ArrayList<NotificationRecord> list = mNotificationList;
         final int len = list.size();
-        for (int i=0; i<len; i++) {
+        for (int i = 0; i < len; i++) {
             NotificationRecord r = list.get(i);
             if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
                     TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
-                return i;
+                return r;
             }
         }
-        return -1;
+        return null;
     }
 
     // lock on mNotificationList
@@ -4074,7 +4292,7 @@
     }
 
     private void updateNotificationPulse() {
-        synchronized (mNotificationList) {
+        synchronized (mNotificationLock) {
             updateLightsLocked();
         }
     }
@@ -4154,9 +4372,10 @@
         Bundle visibilityOverrides = new Bundle();
         Bundle suppressedVisualEffects = new Bundle();
         Bundle explanation = new Bundle();
-        Bundle overrideChannels = new Bundle();
+        Bundle channels = new Bundle();
         Bundle overridePeople = new Bundle();
         Bundle snoozeCriteria = new Bundle();
+        Bundle showBadge = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -4178,9 +4397,10 @@
                 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
             }
             overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
-            overrideChannels.putParcelable(key, record.getChannel());
+            channels.putParcelable(key, record.getChannel());
             overridePeople.putStringArrayList(key, record.getPeopleOverride());
             snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
+            showBadge.putBoolean(key, record.canShowBadge());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -4191,7 +4411,7 @@
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
-                overrideChannels, overridePeople, snoozeCriteria);
+                channels, overridePeople, snoozeCriteria, showBadge);
     }
 
     private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
@@ -4378,7 +4598,7 @@
         public void onServiceAdded(ManagedServiceInfo info) {
             final INotificationListener listener = (INotificationListener) info.service;
             final NotificationRankingUpdate update;
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 update = makeRankingUpdateLocked(info);
             }
             try {
@@ -4573,12 +4793,12 @@
             }
         }
 
-        private boolean isListenerPackage(String packageName) {
+        public boolean isListenerPackage(String packageName) {
             if (packageName == null) {
                 return false;
             }
             // TODO: clean up locking object later
-            synchronized (mNotificationList) {
+            synchronized (mNotificationLock) {
                 for (final ManagedServiceInfo serviceInfo : mServices) {
                     if (packageName.equals(serviceInfo.component.getPackageName())) {
                         return true;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index e8c3d97..8998128 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,7 +25,6 @@
 import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -42,6 +41,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.EventLogTags;
@@ -114,12 +114,14 @@
     private Uri mSound;
     private long[] mVibration;
     private AudioAttributes mAttributes;
-    private NotificationChannel mOverrideChannel;
+    private NotificationChannel mChannel;
     private ArrayList<String> mPeopleOverride;
     private ArrayList<SnoozeCriterion> mSnoozeCriteria;
+    private boolean mShowBadge;
 
     @VisibleForTesting
-    public NotificationRecord(Context context, StatusBarNotification sbn)
+    public NotificationRecord(Context context, StatusBarNotification sbn,
+            NotificationChannel channel)
     {
         this.sbn = sbn;
         mOriginalFlags = sbn.getNotification().flags;
@@ -128,6 +130,7 @@
         mUpdateTimeMs = mCreationTimeMs;
         mContext = context;
         stats = new NotificationUsageStats.SingleNotificationStats();
+        mChannel = channel;
         mPreChannelsNotification = isPreChannelsNotification();
         mSound = calculateSound();
         mVibration = calculateVibration();
@@ -154,7 +157,7 @@
     private Uri calculateSound() {
         final Notification n = sbn.getNotification();
 
-        Uri sound = sbn.getNotificationChannel().getSound();
+        Uri sound = mChannel.getSound();
         if (mPreChannelsNotification && (getChannel().getUserLockedFields()
                 & NotificationChannel.USER_LOCKED_SOUND) == 0) {
 
@@ -316,6 +319,7 @@
         pw.println(prefix + "  vibrate=" + Arrays.toString(notification.vibrate));
         pw.println(prefix + String.format("  led=0x%08x onMs=%d offMs=%d",
                 notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
+        pw.println(prefix + "  timeout=" + TimeUtils.formatForLogging(notification.getTimeout()));
         if (notification.actions != null && notification.actions.length > 0) {
             pw.println(prefix + "  actions={");
             final int N = notification.actions.length;
@@ -386,7 +390,8 @@
         pw.println(prefix + "  mSound= " + mSound);
         pw.println(prefix + "  mVibration= " + mVibration);
         pw.println(prefix + "  mAttributes= " + mAttributes);
-        pw.println(prefix + "  overrideChannel=" + getChannel());
+        pw.println(prefix + "  mShowBadge=" + mShowBadge);
+        pw.println(prefix + "  channel=" + getChannel());
         if (getPeopleOverride() != null) {
             pw.println(prefix + "  overridePeople= " + TextUtils.join(",", getPeopleOverride()));
         }
@@ -640,16 +645,24 @@
     }
 
     public NotificationChannel getChannel() {
-        return mOverrideChannel == null ? sbn.getNotificationChannel() : mOverrideChannel;
+        return mChannel;
     }
 
-    protected void setNotificationChannelOverride(NotificationChannel channel) {
-        mOverrideChannel = channel;
-        if (mOverrideChannel != null) {
+    protected void updateNotificationChannel(NotificationChannel channel) {
+        if (channel != null) {
+            mChannel = channel;
             calculateImportance();
         }
     }
 
+    public void setShowBadge(boolean showBadge) {
+        mShowBadge = showBadge;
+    }
+
+    public boolean canShowBadge() {
+        return mShowBadge;
+    }
+
     public Uri getSound() {
         return mSound;
     }
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 5c1e99c..2e35e3d 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -16,19 +16,28 @@
 package com.android.server.notification;
 
 import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
 import android.content.pm.ParceledListSlice;
 
 public interface RankingConfig {
 
     void setImportance(String packageName, int uid, int importance);
     int getImportance(String packageName, int uid);
+    void setShowBadge(String packageName, int uid, boolean showBadge);
+    boolean canShowBadge(String packageName, int uid);
 
+    void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
+            boolean fromTargetApp);
+    ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
+            int uid, boolean includeDeleted);
     void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
             boolean fromTargetApp);
     void updateNotificationChannel(String pkg, int uid, NotificationChannel channel);
     void updateNotificationChannelFromAssistant(String pkg, int uid, NotificationChannel channel);
-    NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
-    NotificationChannel getNotificationChannelWithFallback(String pkg, int uid, String channelId);
+    NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
+    NotificationChannel getNotificationChannelWithFallback(String pkg, int uid, String channelId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, int uid, String channelId);
-    ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid);
+    void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
+    void permanentlyDeleteNotificationChannels(String pkg, int uid);
+    ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, boolean includeDeleted);
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 3fcce3c..01cca2d0 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -18,10 +18,12 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -29,7 +31,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.os.Build;
-import android.os.Process;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.text.TextUtils;
@@ -59,6 +60,7 @@
     private static final String TAG_RANKING = "ranking";
     private static final String TAG_PACKAGE = "package";
     private static final String TAG_CHANNEL = "channel";
+    private static final String TAG_GROUP = "channelGroup";
 
     private static final String ATT_VERSION = "version";
     private static final String ATT_NAME = "name";
@@ -67,10 +69,12 @@
     private static final String ATT_PRIORITY = "priority";
     private static final String ATT_VISIBILITY = "visibility";
     private static final String ATT_IMPORTANCE = "importance";
+    private static final String ATT_SHOW_BADGE = "show_badge";
 
     private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
     private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
     private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
+    private static final boolean DEFAULT_SHOW_BADGE = true;
 
     private final NotificationSignalExtractor[] mSignalExtractors;
     private final NotificationComparator mPreliminaryComparator;
@@ -169,9 +173,9 @@
                         Record r = getOrCreateRecord(name, uid,
                                 safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
                                 safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
-                                safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
+                                safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
+                                safeBool(parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
 
-                        // Channels
                         final int innerDepth = parser.getDepth();
                         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                                 && (type != XmlPullParser.END_TAG
@@ -181,6 +185,17 @@
                             }
 
                             String tagName = parser.getName();
+                            // Channel groups
+                            if (TAG_GROUP.equals(tagName)) {
+                                String id = parser.getAttributeValue(null, ATT_ID);
+                                CharSequence groupName = parser.getAttributeValue(null, ATT_NAME);
+                                if (!TextUtils.isEmpty(id)) {
+                                    final NotificationChannelGroup group =
+                                            new NotificationChannelGroup(id, groupName);
+                                    r.groups.put(id, group);
+                                }
+                            }
+                            // Channels
                             if (TAG_CHANNEL.equals(tagName)) {
                                 String id = parser.getAttributeValue(null, ATT_ID);
                                 CharSequence channelName = parser.getAttributeValue(null, ATT_NAME);
@@ -215,11 +230,11 @@
 
     private Record getOrCreateRecord(String pkg, int uid) {
         return getOrCreateRecord(pkg, uid,
-                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY);
+                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
     }
 
     private Record getOrCreateRecord(String pkg, int uid, int importance, int priority,
-            int visibility) {
+            int visibility, boolean showBadge) {
         final String key = recordKey(pkg, uid);
         Record r = (uid == Record.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg) : mRecords.get(key);
         if (r == null) {
@@ -229,6 +244,7 @@
             r.importance = importance;
             r.priority = priority;
             r.visibility = visibility;
+            r.showBadge = showBadge;
             createDefaultChannelIfMissing(r);
             if (r.uid == Record.UNKNOWN_UID) {
                 mRestoredWithoutUids.put(pkg, r);
@@ -298,7 +314,8 @@
             }
             final boolean hasNonDefaultSettings = r.importance != DEFAULT_IMPORTANCE
                     || r.priority != DEFAULT_PRIORITY || r.visibility != DEFAULT_VISIBILITY
-                    || r.channels.size() > 0;
+                    || r.showBadge != DEFAULT_SHOW_BADGE || r.channels.size() > 0
+                    || r.groups.size() > 0;
             if (hasNonDefaultSettings) {
                 out.startTag(null, TAG_PACKAGE);
                 out.attribute(null, ATT_NAME, r.pkg);
@@ -311,11 +328,16 @@
                 if (r.visibility != DEFAULT_VISIBILITY) {
                     out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
                 }
+                out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
 
                 if (!forBackup) {
                     out.attribute(null, ATT_UID, Integer.toString(r.uid));
                 }
 
+                for (NotificationChannelGroup group : r.groups.values()) {
+                    group.writeXml(out);
+                }
+
                 for (NotificationChannel channel : r.channels.values()) {
                     channel.writeXml(out);
                 }
@@ -396,6 +418,12 @@
         return Collections.binarySearch(notificationList, target, mFinalComparator);
     }
 
+    private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
+        final String value = parser.getAttributeValue(null, att);
+        if (TextUtils.isEmpty(value)) return defValue;
+        return Boolean.parseBoolean(value);
+    }
+
     private static int safeInt(XmlPullParser parser, String att, int defValue) {
         final String val = parser.getAttributeValue(null, att);
         return tryParseInt(val, defValue);
@@ -419,6 +447,32 @@
     }
 
     @Override
+    public boolean canShowBadge(String packageName, int uid) {
+        return getOrCreateRecord(packageName, uid).showBadge;
+    }
+
+    @Override
+    public void setShowBadge(String packageName, int uid, boolean showBadge) {
+        getOrCreateRecord(packageName, uid).showBadge = showBadge;
+        updateConfig();
+    }
+
+    @Override
+    public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
+            boolean fromTargetApp) {
+        Preconditions.checkNotNull(pkg);
+        Preconditions.checkNotNull(group);
+        Preconditions.checkNotNull(group.getId());
+        Preconditions.checkNotNull(group.getName());
+        Record r = getOrCreateRecord(pkg, uid);
+        if (r == null) {
+            throw new IllegalArgumentException("Invalid package");
+        }
+        r.groups.put(group.getId(), group);
+        updateConfig();
+    }
+
+    @Override
     public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
             boolean fromTargetApp) {
         Preconditions.checkNotNull(pkg);
@@ -432,9 +486,17 @@
         if (IMPORTANCE_NONE == r.importance) {
             throw new IllegalArgumentException("Package blocked");
         }
-        if (r.channels.containsKey(channel.getId()) || channel.getName().equals(
-                mContext.getString(R.string.default_notification_channel_label))) {
-            // Channel already exists, no-op.
+        if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
+            throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
+        }
+
+        NotificationChannel existing = r.channels.get(channel.getId());
+        // Keep existing settings
+        if (existing != null) {
+            if (existing.isDeleted()) {
+                existing.setDeleted(false);
+                updateConfig();
+            }
             return;
         }
         if (channel.getImportance() < NotificationManager.IMPORTANCE_NONE
@@ -450,6 +512,9 @@
         if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
             channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
         }
+        if (!r.showBadge) {
+            channel.setShowBadge(false);
+        }
         r.channels.put(channel.getId(), channel);
         updateConfig();
     }
@@ -471,7 +536,7 @@
             throw new IllegalArgumentException("Invalid package");
         }
         NotificationChannel channel = r.channels.get(updatedChannel.getId());
-        if (channel == null) {
+        if (channel == null || channel.isDeleted()) {
             throw new IllegalArgumentException("Channel does not exist");
         }
         if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
@@ -489,7 +554,7 @@
             throw new IllegalArgumentException("Invalid package");
         }
         NotificationChannel channel = r.channels.get(updatedChannel.getId());
-        if (channel == null) {
+        if (channel == null || channel.isDeleted()) {
             throw new IllegalArgumentException("Channel does not exist");
         }
 
@@ -519,6 +584,10 @@
         if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_SHOW_BADGE) == 0) {
             channel.setShowBadge(updatedChannel.canShowBadge());
         }
+        if (updatedChannel.isDeleted()) {
+            channel.setDeleted(true);
+        }
+        // Assistant cannot change the group
 
         r.channels.put(channel.getId(), channel);
         updateConfig();
@@ -526,13 +595,13 @@
 
     @Override
     public NotificationChannel getNotificationChannelWithFallback(String pkg, int uid,
-            String channelId) {
+            String channelId, boolean includeDeleted) {
         Record r = getOrCreateRecord(pkg, uid);
         if (channelId == null) {
             channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
         }
         NotificationChannel channel = r.channels.get(channelId);
-        if (channel != null) {
+        if (channel != null && (includeDeleted || !channel.isDeleted())) {
             return channel;
         } else {
             return r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID);
@@ -540,16 +609,21 @@
     }
 
     @Override
-    public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId) {
+    public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+            boolean includeDeleted) {
         Preconditions.checkNotNull(pkg);
         Record r = getOrCreateRecord(pkg, uid);
         if (r == null) {
-            throw new IllegalArgumentException("Invalid package");
+            return null;
         }
         if (channelId == null) {
             channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
         }
-        return r.channels.get(channelId);
+        final NotificationChannel nc = r.channels.get(channelId);
+        if (nc != null && (includeDeleted || !nc.isDeleted())) {
+            return nc;
+        }
+        return null;
     }
 
     @Override
@@ -558,24 +632,87 @@
         Preconditions.checkNotNull(channelId);
         Record r = getRecord(pkg, uid);
         if (r == null) {
-            throw new IllegalArgumentException("Invalid package");
+            return;
         }
-        if (r != null) {
-            r.channels.remove(channelId);
+        NotificationChannel channel = r.channels.get(channelId);
+        if (channel != null) {
+            channel.setDeleted(true);
         }
     }
 
     @Override
-    public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid) {
+    @VisibleForTesting
+    public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
+        Preconditions.checkNotNull(pkg);
+        Preconditions.checkNotNull(channelId);
+        Record r = getRecord(pkg, uid);
+        if (r == null) {
+            return;
+        }
+        r.channels.remove(channelId);
+    }
+
+    @Override
+    public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
+        Preconditions.checkNotNull(pkg);
+        Record r = getRecord(pkg, uid);
+        if (r == null) {
+            return;
+        }
+        int N = r.channels.size() - 1;
+        for (int i = N; i >= 0; i--) {
+            String key = r.channels.keyAt(i);
+            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+                r.channels.remove(key);
+            }
+        }
+    }
+
+    @Override
+    public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
+            int uid, boolean includeDeleted) {
+        Preconditions.checkNotNull(pkg);
+        List<NotificationChannelGroup> groups = new ArrayList<>();
+        Record r = getRecord(pkg, uid);
+        if (r == null) {
+            return ParceledListSlice.emptyList();
+        }
+        NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (includeDeleted || !nc.isDeleted()) {
+                if (nc.getGroup() != null) {
+                    // lazily populate channel list
+                    NotificationChannelGroup ncg = r.groups.get(nc.getGroup());
+                    ncg.addChannel(nc);
+                } else {
+                    nonGrouped.addChannel(nc);
+                }
+            }
+        }
+        groups.addAll(r.groups.values());
+        if (nonGrouped.getChannels().size() > 0) {
+            groups.add(nonGrouped);
+        }
+        return new ParceledListSlice<>(groups);
+    }
+
+    @Override
+    public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
+            boolean includeDeleted) {
         Preconditions.checkNotNull(pkg);
         List<NotificationChannel> channels = new ArrayList<>();
         Record r = getRecord(pkg, uid);
         if (r == null) {
-            throw new IllegalArgumentException("Invalid package");
+            return ParceledListSlice.emptyList();
         }
         int N = r.channels.size();
         for (int i = 0; i < N; i++) {
-            channels.add(r.channels.valueAt(i));
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (includeDeleted || !nc.isDeleted()) {
+                channels.add(nc);
+            }
         }
         return new ParceledListSlice<>(channels);
     }
@@ -627,7 +764,7 @@
             final Record r = records.valueAt(i);
             if (filter == null || filter.matches(r.pkg)) {
                 pw.print(prefix);
-                pw.print("  ");
+                pw.print("  AppSettings: ");
                 pw.print(r.pkg);
                 pw.print(" (");
                 pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
@@ -644,6 +781,8 @@
                     pw.print(" visibility=");
                     pw.print(Notification.visibilityToString(r.visibility));
                 }
+                pw.print(" showBadge=");
+                pw.print(Boolean.toString(r.showBadge));
                 pw.println();
                 for (NotificationChannel channel : r.channels.values()) {
                     pw.print(prefix);
@@ -651,6 +790,12 @@
                     pw.print("  ");
                     pw.println(channel);
                 }
+                for (NotificationChannelGroup group : r.groups.values()) {
+                    pw.print(prefix);
+                    pw.print("  ");
+                    pw.print("  ");
+                    pw.println(group);
+                }
             }
         }
     }
@@ -680,9 +825,15 @@
                     if (r.visibility != DEFAULT_VISIBILITY) {
                         record.put("visibility", Notification.visibilityToString(r.visibility));
                     }
+                    if (r.showBadge != DEFAULT_SHOW_BADGE) {
+                        record.put("showBadge", Boolean.valueOf(r.showBadge));
+                    }
                     for (NotificationChannel channel : r.channels.values()) {
                         record.put("channel", channel.toJson());
                     }
+                    for (NotificationChannelGroup group : r.groups.values()) {
+                        record.put("group", group.toJson());
+                    }
                 } catch (JSONException e) {
                    // pass
                 }
@@ -738,30 +889,46 @@
         return packageBans;
     }
 
-    public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList) {
-        if (removingPackage || pkgList == null || pkgList.length == 0) {
+    public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
+            int[] uidList) {
+        if (pkgList == null || pkgList.length == 0) {
             return; // nothing to do
         }
         boolean updated = false;
-        for (String pkg : pkgList) {
-            final Record r = mRestoredWithoutUids.get(pkg);
-            if (r != null) {
+        if (removingPackage) {
+            // Remove notification settings for uninstalled package
+            int size = Math.min(pkgList.length, uidList.length);
+            for (int i = 0; i < size; i++) {
+                final String pkg = pkgList[i];
+                final int uid = uidList[i];
+                mRecords.remove(recordKey(pkg, uid));
+                mRestoredWithoutUids.remove(pkg);
+                updated = true;
+            }
+        } else {
+            for (String pkg : pkgList) {
+                // Package install
+                final Record r = mRestoredWithoutUids.get(pkg);
+                if (r != null) {
+                    try {
+                        r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
+                        mRestoredWithoutUids.remove(pkg);
+                        mRecords.put(recordKey(r.pkg, r.uid), r);
+                        updated = true;
+                    } catch (NameNotFoundException e) {
+                        // noop
+                    }
+                }
+                // Package upgrade
                 try {
-                    r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
-                    mRestoredWithoutUids.remove(pkg);
-                    mRecords.put(recordKey(r.pkg, r.uid), r);
-                    updated = true;
+                    Record fullRecord = getRecord(pkg,
+                            mPm.getPackageUidAsUser(pkg, changeUserId));
+                    if (fullRecord != null) {
+                        clampDefaultChannel(fullRecord);
+                    }
                 } catch (NameNotFoundException e) {
-                    // noop
                 }
             }
-            try {
-                Record fullRecord = getRecord(pkg,
-                        mPm.getPackageUidAsUser(pkg, changeUserId));
-                if (fullRecord != null) {
-                    clampDefaultChannel(fullRecord);
-                }
-            } catch (NameNotFoundException e) {}
         }
 
         if (updated) {
@@ -777,7 +944,9 @@
         int importance = DEFAULT_IMPORTANCE;
         int priority = DEFAULT_PRIORITY;
         int visibility = DEFAULT_VISIBILITY;
+        boolean showBadge = DEFAULT_SHOW_BADGE;
 
         ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
+        ArrayMap<String, NotificationChannelGroup> groups = new ArrayMap<>();
    }
 }
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index e14700a..f2aff11 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -41,6 +41,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -98,6 +99,26 @@
         return Collections.EMPTY_LIST;
     }
 
+    protected List<NotificationRecord> getSnoozed() {
+        List<NotificationRecord> snoozedForUser = new ArrayList<>();
+        int[] userIds = mUserProfiles.getCurrentProfileIds();
+        final int N = userIds.length;
+        for (int i = 0; i < N; i++) {
+            final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
+                    mSnoozedNotifications.get(userIds[i]);
+            if (snoozedPkgs != null) {
+                final int M = snoozedPkgs.size();
+                for (int j = 0; j < M; j++) {
+                    final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
+                    if (records != null) {
+                        snoozedForUser.addAll(records.values());
+                    }
+                }
+            }
+        }
+        return snoozedForUser;
+    }
+
     /**
      * Snoozes a notification and schedules an alarm to repost at that time.
      */
diff --git a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
index 1e3e0ca..3d946e0 100644
--- a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
+++ b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
@@ -27,10 +28,13 @@
 import android.graphics.drawable.Drawable;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.Xml;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
@@ -51,6 +55,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -62,7 +67,7 @@
 class EphemeralApplicationRegistry {
     private static final boolean DEBUG = false;
 
-    private static final boolean ENABLED = false;
+    private static final boolean ENABLED = true;
 
     private static final String LOG_TAG = "EphemeralAppRegistry";
 
@@ -90,6 +95,16 @@
     @GuardedBy("mService.mPackages")
     private SparseArray<List<UninstalledEphemeralAppState>> mUninstalledEphemeralApps;
 
+    /**
+     * Automatic grants for access to instant app metadata.
+     * The key is the target application UID.
+     * The value is a set of instant app UIDs.
+     * UserID -> TargetAppId -> InstantAppId
+     */
+    private SparseArray<SparseArray<SparseBooleanArray>> mEphemeralGrants;
+    /** The set of all installed instant apps. UserID -> AppID */
+    private SparseArray<SparseBooleanArray> mInstalledEphemeralAppUids;
+
     public EphemeralApplicationRegistry(PackageManagerService service) {
         mService = service;
     }
@@ -188,7 +203,11 @@
             }
 
             // Propagate permissions before removing any state
-            propagateEphemeralAppPermissionsIfNeeded(pkg, userId);
+            // TODO: Fix this later
+            // propagateEphemeralAppPermissionsIfNeeded(pkg, userId);
+            if (pkg.applicationInfo.isEphemeralApp()) {
+                addEphemeralAppLPw(userId, ps.appId);
+            }
 
             // Remove the in-memory state
             if (mUninstalledEphemeralApps != null) {
@@ -248,9 +267,11 @@
             if (pkg.applicationInfo.isEphemeralApp()) {
                 // Add a record for an uninstalled ephemeral app
                 addUninstalledEphemeralAppLPw(pkg, userId);
+                removeEphemeralAppLPw(userId, ps.appId);
             } else {
                 // Deleting an app prunes all ephemeral state such as cookie
                 deleteDir(getEphemeralApplicationDir(pkg.packageName, userId));
+                removeAppLPw(userId, ps.appId);
             }
         }
     }
@@ -262,9 +283,114 @@
         if (mUninstalledEphemeralApps != null) {
             mUninstalledEphemeralApps.remove(userId);
         }
+        if (mInstalledEphemeralAppUids != null) {
+            mInstalledEphemeralAppUids.remove(userId);
+        }
+        if (mEphemeralGrants != null) {
+            mEphemeralGrants.remove(userId);
+        }
         deleteDir(getEphemeralApplicationsDir(userId));
     }
 
+    public boolean isEphemeralAccessGranted(int userId, int targetAppId, int ephemeralAppId) {
+        if (mEphemeralGrants == null) {
+            return false;
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+        if (targetAppList == null) {
+            return false;
+        }
+        final SparseBooleanArray ephemeralGrantList = targetAppList.get(targetAppId);
+        if (ephemeralGrantList == null) {
+            return false;
+        }
+        return ephemeralGrantList.get(ephemeralAppId);
+    }
+
+    public void grantEphemeralAccessLPw(int userId, Intent intent,
+            int targetAppId, int ephemeralAppId) {
+        if (mInstalledEphemeralAppUids == null) {
+            return;     // no ephemeral apps installed; no need to grant
+        }
+        SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+        if (ephemeralAppList == null || !ephemeralAppList.get(ephemeralAppId)) {
+            return;     // ephemeral app id isn't installed; no need to grant
+        }
+        if (ephemeralAppList.get(targetAppId)) {
+            return;     // target app id is an ephemeral app; no need to grant
+        }
+        if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
+            final Set<String> categories = intent.getCategories();
+            if (categories != null && categories.contains(Intent.CATEGORY_BROWSABLE)) {
+                return;  // launched via VIEW/BROWSABLE intent; no need to grant
+            }
+        }
+        if (mEphemeralGrants == null) {
+            mEphemeralGrants = new SparseArray<>();
+        }
+        SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+        if (targetAppList == null) {
+            targetAppList = new SparseArray<>();
+            mEphemeralGrants.put(userId, targetAppList);
+        }
+        SparseBooleanArray ephemeralGrantList = targetAppList.get(targetAppId);
+        if (ephemeralGrantList == null) {
+            ephemeralGrantList = new SparseBooleanArray();
+            targetAppList.put(targetAppId, ephemeralGrantList);
+        }
+        ephemeralGrantList.put(ephemeralAppId, true /*granted*/);
+    }
+
+    public void addEphemeralAppLPw(int userId, int ephemeralAppId) {
+        if (mInstalledEphemeralAppUids == null) {
+            mInstalledEphemeralAppUids = new SparseArray<>();
+        }
+        SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+        if (ephemeralAppList == null) {
+            ephemeralAppList = new SparseBooleanArray();
+            mInstalledEphemeralAppUids.put(userId, ephemeralAppList);
+        }
+        ephemeralAppList.put(ephemeralAppId, true /*installed*/);
+    }
+
+    private void removeEphemeralAppLPw(int userId, int ephemeralAppId) {
+        // remove from the installed list
+        if (mInstalledEphemeralAppUids == null) {
+            return; // no ephemeral apps on the system
+        }
+        final SparseBooleanArray ephemeralAppList = mInstalledEphemeralAppUids.get(userId);
+        if (ephemeralAppList == null) {
+            Slog.w(LOG_TAG, "Remove ephemeral not in install list");
+            return;
+        } else {
+            ephemeralAppList.delete(ephemeralAppId);
+        }
+        // remove any grants
+        if (mEphemeralGrants == null) {
+            return; // no grants on the system
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+        if (targetAppList == null) {
+            return; // no grants for this user
+        }
+        final int numApps = targetAppList.size();
+        for (int i = targetAppList.size() - 1; i >= 0; --i) {
+            targetAppList.valueAt(i).delete(ephemeralAppId);
+        }
+    }
+
+    private void removeAppLPw(int userId, int targetAppId) {
+        // remove from the installed list
+        if (mEphemeralGrants == null) {
+            return; // no grants on the system
+        }
+        final SparseArray<SparseBooleanArray> targetAppList = mEphemeralGrants.get(userId);
+        if (targetAppList == null) {
+            return; // no grants for this user
+        }
+        targetAppList.delete(targetAppId);
+    }
+
     private void addUninstalledEphemeralAppLPw(PackageParser.Package pkg, int userId) {
         EphemeralApplicationInfo uninstalledApp = createEphemeralAppInfoForPackage(pkg, userId);
         if (uninstalledApp == null) {
diff --git a/services/core/java/com/android/server/pm/EphemeralResolver.java b/services/core/java/com/android/server/pm/EphemeralResolver.java
index d735e72..96a0d18 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolver.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolver.java
@@ -78,6 +78,7 @@
                     int sequence) {
                 final String packageName;
                 final String splitName;
+                final int versionCode;
                 if (ephemeralResolveInfo != null) {
                     final ArrayList<EphemeralResolveInfo> ephemeralResolveInfoList =
                             new ArrayList<EphemeralResolveInfo>(1);
@@ -91,13 +92,16 @@
                             && ephemeralIntentInfo.resolveInfo != null) {
                         packageName = ephemeralIntentInfo.resolveInfo.getPackageName();
                         splitName = ephemeralIntentInfo.splitName;
+                        versionCode = ephemeralIntentInfo.resolveInfo.getVersionCode();
                     } else {
                         packageName = null;
                         splitName = null;
+                        versionCode = -1;
                     }
                 } else {
                     packageName = null;
                     splitName = null;
+                    versionCode = -1;
                 }
                 final Intent installerIntent = buildEphemeralInstallerIntent(
                         requestObj.launchIntent,
@@ -107,6 +111,7 @@
                         requestObj.userId,
                         packageName,
                         splitName,
+                        versionCode,
                         requestObj.responseObj.token,
                         false /*needsPhaseTwo*/);
                 installerIntent.setComponent(new ComponentName(
@@ -123,7 +128,7 @@
      */
     public static Intent buildEphemeralInstallerIntent(Intent launchIntent, Intent origIntent,
             String callingPackage, String resolvedType, int userId, String ephemeralPackageName,
-            String ephemeralSplitName, String token, boolean needsPhaseTwo) {
+            String ephemeralSplitName, int versionCode, String token, boolean needsPhaseTwo) {
         // Construct the intent that launches the ephemeral installer
         int flags = launchIntent.getFlags();
         final Intent intent = new Intent();
@@ -181,6 +186,7 @@
 
             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, ephemeralPackageName);
             intent.putExtra(Intent.EXTRA_SPLIT_NAME, ephemeralSplitName);
+            intent.putExtra(Intent.EXTRA_VERSION_CODE, versionCode);
         }
 
         return intent;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 98249dd1..fc66bb3 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -242,6 +242,16 @@
         }
     }
 
+    public void setAppQuota(String uuid, int userId, int appId, long cacheQuota)
+            throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.setAppQuota(uuid, userId, appId, cacheQuota);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
             int dexoptNeeded, @Nullable String outputPath, int dexFlags,
             String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries)
@@ -285,6 +295,15 @@
         }
     }
 
+    public void removeIdmap(String overlayApkPath) throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.removeIdmap(overlayApkPath);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     public void rmdex(String codePath, String instructionSet) throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
@@ -351,10 +370,10 @@
         }
     }
 
-    public void freeCache(String uuid, long freeStorageSize) throws InstallerException {
+    public void freeCache(String uuid, long freeStorageSize, int flags) throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            mInstalld.freeCache(uuid, freeStorageSize);
+            mInstalld.freeCache(uuid, freeStorageSize, flags);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
@@ -415,6 +434,15 @@
         }
     }
 
+    public void invalidateMounts() throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.invalidateMounts();
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     private static void assertValidInstructionSet(String instructionSet)
             throws InstallerException {
         for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 48e000d8..b6611eb 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -21,9 +21,11 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ILauncherApps;
@@ -217,11 +219,12 @@
         /**
          * Checks if the caller is in the same group as the userToCheck.
          */
-        private void ensureInUserProfiles(UserHandle userToCheck, String message) {
-            ensureInUserProfiles(userToCheck.getIdentifier(), message);
+        private void ensureInUserProfiles(
+                String callingPackage, UserHandle userToCheck, String message) {
+            ensureInUserProfiles(callingPackage, userToCheck.getIdentifier(), message);
         }
 
-        private void ensureInUserProfiles(int targetUserId, String message) {
+        private void ensureInUserProfiles(String callingPackage, int targetUserId, String message) {
             final int callingUserId = injectCallingUserId();
 
             if (targetUserId == callingUserId) return;
@@ -229,11 +232,20 @@
             long ident = injectClearCallingIdentity();
             try {
                 UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
+                if (callingUserInfo.isManagedProfile()) {
+                    // TODO: Make it SecurityException.  See b/34650921
+                    // throw new SecurityException(message + " for another profile " + targetUserId);
+
+                    // TODO: Report caller package name.
+                    Slog.wtfStack(TAG, message + " by " + callingPackage + " for another profile "
+                            + targetUserId + " from " + callingUserId);
+                }
+
                 UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
                 if (targetUserInfo == null
                         || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
                         || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
-                    throw new SecurityException(message);
+                    throw new SecurityException(message + " for unrelated profile " + targetUserId);
                 }
             } finally {
                 injectRestoreCallingIdentity(ident);
@@ -275,32 +287,21 @@
         }
 
         @Override
-        public ParceledListSlice<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
+        public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage,
+                String packageName, UserHandle user)
                 throws RemoteException {
-            ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
-            if (!isUserEnabled(user)) {
-                return null;
-            }
-
-            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
-            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-            mainIntent.setPackage(packageName);
-            long ident = Binder.clearCallingIdentity();
-            try {
-                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                        user.getIdentifier());
-                return new ParceledListSlice<>(apps);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            return queryActivitiesForUser(callingPackage,
+                    new Intent(Intent.ACTION_MAIN)
+                            .addCategory(Intent.CATEGORY_LAUNCHER)
+                            .setPackage(packageName),
+                    user);
         }
 
         @Override
-        public ActivityInfo resolveActivity(ComponentName component, UserHandle user)
+        public ActivityInfo resolveActivity(
+                String callingPackage, ComponentName component, UserHandle user)
                 throws RemoteException {
-            ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
+            ensureInUserProfiles(callingPackage, user, "Cannot resolve activity");
             if (!isUserEnabled(user)) {
                 return null;
             }
@@ -318,9 +319,57 @@
         }
 
         @Override
-        public boolean isPackageEnabled(String packageName, UserHandle user)
+        public ParceledListSlice getShortcutConfigActivities(
+                String callingPackage, String packageName, UserHandle user)
                 throws RemoteException {
-            ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
+            return queryActivitiesForUser(callingPackage,
+                    new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName), user);
+        }
+
+        private ParceledListSlice<ResolveInfo> queryActivitiesForUser(String callingPackage,
+                Intent intent, UserHandle user) {
+            ensureInUserProfiles(callingPackage, user, "Cannot retrieve activities");
+            if (!isUserEnabled(user)) {
+                return null;
+            }
+
+            long ident = injectClearCallingIdentity();
+            try {
+                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(intent,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                        user.getIdentifier());
+                return new ParceledListSlice<>(apps);
+            } finally {
+                injectRestoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public IntentSender getShortcutConfigActivityIntent(String callingPackage,
+                ComponentName component, UserHandle user) throws RemoteException {
+            ensureShortcutPermission(callingPackage, user);
+            Preconditions.checkNotNull(component);
+            Preconditions.checkArgument(isUserEnabled(user), "User not enabled");
+
+            // All right, create the sender.
+            Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return PendingIntent.getActivityAsUser(
+                        mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
+                                | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT,
+                        null, user)
+                        .getIntentSender();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public boolean isPackageEnabled(String callingPackage, String packageName, UserHandle user)
+                throws RemoteException {
+            ensureInUserProfiles(callingPackage, user, "Cannot check package");
             if (!isUserEnabled(user)) {
                 return false;
             }
@@ -339,9 +388,10 @@
         }
 
         @Override
-        public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user)
+        public ApplicationInfo getApplicationInfo(
+                String callingPackage, String packageName, int flags, UserHandle user)
                 throws RemoteException {
-            ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
+            ensureInUserProfiles(callingPackage, user, "Cannot check package");
             if (!isUserEnabled(user)) {
                 return null;
             }
@@ -363,7 +413,7 @@
 
         private void ensureShortcutPermission(@NonNull String callingPackage, int userId) {
             verifyCallingPackage(callingPackage);
-            ensureInUserProfiles(userId, "Cannot access shortcuts for unrelated profile " + userId);
+            ensureInUserProfiles(callingPackage, userId, "Cannot access shortcuts");
 
             if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
                     callingPackage)) {
@@ -439,7 +489,7 @@
         public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
                 Rect sourceBounds, Bundle startActivityOptions, int userId) {
             verifyCallingPackage(callingPackage);
-            ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
+            ensureInUserProfiles(callingPackage, userId, "Cannot start activity");
 
             if (!isUserEnabled(userId)) {
                 throw new IllegalStateException("Cannot start a shortcut for disabled profile "
@@ -490,9 +540,10 @@
         }
 
         @Override
-        public boolean isActivityEnabled(ComponentName component, UserHandle user)
+        public boolean isActivityEnabled(
+                String callingPackage, ComponentName component, UserHandle user)
                 throws RemoteException {
-            ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
+            ensureInUserProfiles(callingPackage , user, "Cannot check component");
             if (!isUserEnabled(user)) {
                 return false;
             }
@@ -511,9 +562,10 @@
         }
 
         @Override
-        public void startActivityAsUser(ComponentName component, Rect sourceBounds,
+        public void startActivityAsUser(String callingPackage,
+                ComponentName component, Rect sourceBounds,
                 Bundle opts, UserHandle user) throws RemoteException {
-            ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+            ensureInUserProfiles(callingPackage, user, "Cannot start activity");
             if (!isUserEnabled(user)) {
                 throw new IllegalStateException("Cannot start activity for disabled profile "  + user);
             }
@@ -564,9 +616,9 @@
         }
 
         @Override
-        public void showAppDetailsAsUser(ComponentName component, Rect sourceBounds,
-                Bundle opts, UserHandle user) throws RemoteException {
-            ensureInUserProfiles(user, "Cannot show app details for unrelated profile " + user);
+        public void showAppDetailsAsUser(String callingPackage, ComponentName component,
+                Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException {
+            ensureInUserProfiles(callingPackage, user, "Cannot show app details");
             if (!isUserEnabled(user)) {
                 throw new IllegalStateException("Cannot show app details for disabled profile "
                         + user);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d25abbf..da6a67e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -52,6 +52,7 @@
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.VersionedPackage;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.CompressFormat;
 import android.graphics.BitmapFactory;
@@ -145,6 +146,7 @@
     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
     private static final String ATTR_VOLUME_UUID = "volumeUuid";
     private static final String ATTR_NAME = "name";
+    private static final String ATTR_INSTALL_REASON = "installRason";
 
     /** Automatically destroy sessions older than this */
     private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
@@ -412,6 +414,7 @@
         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
         params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
+        params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
 
         final File appIconFile = buildAppIconFile(sessionId);
         if (appIconFile.exists()) {
@@ -484,6 +487,7 @@
         writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
         writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
         writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
+        writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
 
         // Persist app icon if changed since last written
         final File appIconFile = buildAppIconFile(session.sessionId);
@@ -866,8 +870,8 @@
     }
 
     @Override
-    public void uninstall(String packageName, String callerPackageName, int flags,
-                IntentSender statusReceiver, int userId) {
+    public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
+                IntentSender statusReceiver, int userId) throws RemoteException {
         final int callingUid = Binder.getCallingUid();
         mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
         if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
@@ -881,24 +885,24 @@
                 callerPackageName);
 
         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
-                statusReceiver, packageName, isDeviceOwner, userId);
+                statusReceiver, versionedPackage.getPackageName(), isDeviceOwner, userId);
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
                     == PackageManager.PERMISSION_GRANTED) {
             // Sweet, call straight through!
-            mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
+            mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
         } else if (isDeviceOwner) {
             // Allow the DeviceOwner to silently delete packages
             // Need to clear the calling identity to get DELETE_PACKAGES permission
             long ident = Binder.clearCallingIdentity();
             try {
-                mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
+                mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         } else {
             // Take a short detour to confirm with user
             final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
-            intent.setData(Uri.fromParts("package", packageName, null));
+            intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
             intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
             adapter.onUserActionRequired(intent);
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d1aed3e..067a136 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -894,7 +894,7 @@
 
         // This is kind of hacky; we're creating a half-parsed package that is
         // straddled between the inherited and staged APKs.
-        final PackageLite pkg = new PackageLite(null, baseApk, null,
+        final PackageLite pkg = new PackageLite(null, baseApk, null, null,
                 splitPaths.toArray(new String[splitPaths.size()]), null);
         final boolean isForwardLocked =
                 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 55a0ffc..23a6a90 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -16,7 +16,11 @@
 
 package com.android.server.pm;
 
+import static android.Manifest.permission.DELETE_PACKAGES;
+import static android.Manifest.permission.INSTALL_PACKAGES;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
+import static android.Manifest.permission.REQUEST_INSTALL_PACKAGES;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -68,6 +72,7 @@
 import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.content.pm.PackageManager.MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL;
 import static android.content.pm.PackageManager.MOVE_FAILED_DEVICE_ADMIN;
 import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
 import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR;
@@ -80,7 +85,6 @@
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDWR;
-
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
@@ -160,10 +164,12 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
+import android.content.pm.VersionedPackage;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.hardware.display.DisplayManager;
@@ -218,6 +224,7 @@
 import android.util.Log;
 import android.util.LogPrinter;
 import android.util.MathUtils;
+import android.util.PackageUtils;
 import android.util.Pair;
 import android.util.PrintStreamPrinter;
 import android.util.Slog;
@@ -415,6 +422,8 @@
     static final int REMOVE_CHATTY = 1<<16;
     static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1<<17;
 
+    private static final String STATIC_SHARED_LIB_DELIMITER = "_";
+
     private static final int[] EMPTY_INT_ARRAY = new int[0];
 
     /**
@@ -697,6 +706,8 @@
 
     boolean mFirstBoot;
 
+    PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
+
     // System configuration read by SystemConfig.
     final int[] mGlobalGids;
     final SparseArray<ArraySet<String>> mSystemPermissions;
@@ -710,16 +721,21 @@
     public static final class SharedLibraryEntry {
         public final String path;
         public final String apk;
+        public final SharedLibraryInfo info;
 
-        SharedLibraryEntry(String _path, String _apk) {
+        SharedLibraryEntry(String _path, String _apk, String name, int version, int type,
+                String declaringPackageName, int declaringPackageVersionCode) {
             path = _path;
             apk = _apk;
+            info = new SharedLibraryInfo(name, version, type, new VersionedPackage(
+                    declaringPackageName, declaringPackageVersionCode), null);
         }
     }
 
     // Currently known shared libraries.
-    final ArrayMap<String, SharedLibraryEntry> mSharedLibraries =
-            new ArrayMap<String, SharedLibraryEntry>();
+    final ArrayMap<String, SparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
+    final ArrayMap<String, SparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage =
+            new ArrayMap<>();
 
     // All available activities, for your resolving pleasure.
     final ActivityIntentResolver mActivities =
@@ -1713,9 +1729,11 @@
             }
 
             // Now that we successfully installed the package, grant runtime
-            // permissions if requested before broadcasting the install.
-            if (grantPermissions && res.pkg.applicationInfo.targetSdkVersion
-                    >= Build.VERSION_CODES.M) {
+            // permissions if requested before broadcasting the install. Also
+            // for legacy apps in permission review mode we clear the permission
+            // review flag which is used to emulate runtime permissions for
+            // legacy apps.
+            if (grantPermissions) {
                 grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
             }
 
@@ -1761,7 +1779,8 @@
             }
 
             // Send installed broadcasts if the install/update is not ephemeral
-            if (!isEphemeral(res.pkg)) {
+            // and the package is not a static shared lib.
+            if (!isEphemeral(res.pkg) && res.pkg.staticSharedLibName == null) {
                 mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);
 
                 // Send added for users that see the package for the first time
@@ -1950,9 +1969,10 @@
                 final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(fsUuid);
                 for (PackageSetting ps : packages) {
                     Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
-                    deletePackage(ps.name, new LegacyPackageDeleteObserver(null).getBinder(),
+                    deletePackageVersioned(new VersionedPackage(ps.name,
+                            PackageManager.VERSION_CODE_HIGHEST),
+                            new LegacyPackageDeleteObserver(null).getBinder(),
                             UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
-
                     // Try very hard to release any references to this package
                     // so we don't risk the system server being killed due to
                     // open FDs
@@ -1970,11 +1990,6 @@
         for (int userId : userIds) {
             grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
         }
-
-        // We could have touched GID membership, so flush out packages.list
-        synchronized (mPackages) {
-            mSettings.writePackageListLPr();
-        }
     }
 
     private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
@@ -1989,6 +2004,9 @@
         final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                 | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 
+        final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+                >= Build.VERSION_CODES.M;
+
         for (String permission : pkg.requestedPermissions) {
             final BasePermission bp;
             synchronized (mPackages) {
@@ -1998,9 +2016,18 @@
                     && (grantedPermissions == null
                            || ArrayUtils.contains(grantedPermissions, permission))) {
                 final int flags = permissionsState.getPermissionFlags(permission, userId);
-                // Installer cannot change immutable permissions.
-                if ((flags & immutableFlags) == 0) {
-                    grantRuntimePermission(pkg.packageName, permission, userId);
+                if (supportsRuntimePermissions) {
+                    // Installer cannot change immutable permissions.
+                    if ((flags & immutableFlags) == 0) {
+                        grantRuntimePermission(pkg.packageName, permission, userId);
+                    }
+                } else if (mPermissionReviewRequired) {
+                    // In permission review mode we clear the review flag when we
+                    // are asked to install the app with all permissions granted.
+                    if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                        updatePermissionFlags(permission, pkg.packageName,
+                                PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId);
+                    }
                 }
             }
         }
@@ -2227,6 +2254,7 @@
             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
 
             mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
+            mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
 
             File dataDir = Environment.getDataDirectory();
             mAppInstallDir = new File(dataDir, "app");
@@ -2253,9 +2281,12 @@
             }
 
             ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
-            for (int i=0; i<libConfig.size(); i++) {
-                mSharedLibraries.put(libConfig.keyAt(i),
-                        new SharedLibraryEntry(libConfig.valueAt(i), null));
+            final int builtInLibCount = libConfig.size();
+            for (int i = 0; i < builtInLibCount; i++) {
+                String name = libConfig.keyAt(i);
+                String path = libConfig.valueAt(i);
+                addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
+                        SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
             }
 
             mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
@@ -2320,32 +2351,38 @@
                 // to compile them only when we come across an app that uses them (there's
                 // already logic for that in scanPackageLI) but that adds some complexity.
                 for (String dexCodeInstructionSet : dexCodeInstructionSets) {
-                    for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
-                        final String lib = libEntry.path;
-                        if (lib == null) {
-                            continue;
-                        }
-
-                        try {
-                            // Shared libraries do not have profiles so we perform a full
-                            // AOT compilation (if needed).
-                            int dexoptNeeded = DexFile.getDexOptNeeded(
-                                    lib, dexCodeInstructionSet,
-                                    getCompilerFilterForReason(REASON_SHARED_APK),
-                                    false /* newProfile */);
-                            if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
-                                mInstaller.dexopt(lib, Process.SYSTEM_UID, "*",
-                                        dexCodeInstructionSet, dexoptNeeded, null,
-                                        DEXOPT_PUBLIC,
-                                        getCompilerFilterForReason(REASON_SHARED_APK),
-                                        StorageManager.UUID_PRIVATE_INTERNAL,
-                                        SKIP_SHARED_LIBRARY_CHECK);
+                    final int libCount = mSharedLibraries.size();
+                    for (int i = 0; i < libCount; i++) {
+                        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                        final int versionCount = versionedLib.size();
+                        for (int j = 0; j < versionCount; j++) {
+                            SharedLibraryEntry libEntry = versionedLib.valueAt(j);
+                            final String libPath = libEntry.path != null
+                                    ? libEntry.path : libEntry.apk;
+                            if (libPath == null) {
+                                continue;
                             }
-                        } catch (FileNotFoundException e) {
-                            Slog.w(TAG, "Library not found: " + lib);
-                        } catch (IOException | InstallerException e) {
-                            Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
-                                    + e.getMessage());
+                            try {
+                                // Shared libraries do not have profiles so we perform a full
+                                // AOT compilation (if needed).
+                                int dexoptNeeded = DexFile.getDexOptNeeded(
+                                        libPath, dexCodeInstructionSet,
+                                        getCompilerFilterForReason(REASON_SHARED_APK),
+                                        false /* newProfile */);
+                                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                                    mInstaller.dexopt(libPath, Process.SYSTEM_UID, "*",
+                                            dexCodeInstructionSet, dexoptNeeded, null,
+                                            DEXOPT_PUBLIC,
+                                            getCompilerFilterForReason(REASON_SHARED_APK),
+                                            StorageManager.UUID_PRIVATE_INTERNAL,
+                                            SKIP_SHARED_LIBRARY_CHECK);
+                                }
+                            } catch (FileNotFoundException e) {
+                                Slog.w(TAG, "Library not found: " + libPath);
+                            } catch (IOException | InstallerException e) {
+                                Slog.w(TAG, "Cannot dexopt " + libPath + "; is it an APK or JAR? "
+                                        + e.getMessage());
+                            }
                         }
                     }
                 }
@@ -2639,7 +2676,7 @@
 
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
-            updateAllSharedLibrariesLPw();
+            updateAllSharedLibrariesLPw(null);
 
             for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
                 // NOTE: We ignore potential failures here during a system scan (like
@@ -2776,9 +2813,11 @@
                 mIntentFilterVerifier = new IntentVerifierProxy(mContext,
                         mIntentFilterVerifierComponent);
                 mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
-                        PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES);
+                        PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES,
+                        SharedLibraryInfo.VERSION_UNDEFINED);
                 mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
-                        PackageManager.SYSTEM_SHARED_LIBRARY_SHARED);
+                        PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
+                        SharedLibraryInfo.VERSION_UNDEFINED);
             } else {
                 mRequiredVerifierPackage = null;
                 mRequiredInstallerPackage = null;
@@ -2809,8 +2848,6 @@
                 setUpEphemeralInstallerActivityLP(mEphemeralInstallerComponent);
             }
 
-            mEphemeralApplicationRegistry = new EphemeralApplicationRegistry(this);
-
             // Read and update the usage of dex files.
             // Do this at the end of PM init so that all the packages have their
             // data directory reconciled.
@@ -2937,11 +2974,11 @@
         throw new RuntimeException("There must be exactly one verifier; found " + matches);
     }
 
-    private @NonNull String getRequiredSharedLibraryLPr(String libraryName) {
+    private @NonNull String getRequiredSharedLibraryLPr(String name, int version) {
         synchronized (mPackages) {
-            SharedLibraryEntry libraryEntry = mSharedLibraries.get(libraryName);
+            SharedLibraryEntry libraryEntry = getSharedLibraryEntryLPr(name, version);
             if (libraryEntry == null) {
-                throw new IllegalStateException("Missing required shared library:" + libraryName);
+                throw new IllegalStateException("Missing required shared library:" + name);
             }
             return libraryEntry.apk;
         }
@@ -3260,6 +3297,31 @@
         if (p == null) {
             return null;
         }
+        // Filter out ephemeral app metadata:
+        //   * The system/shell/root can see metadata for any app
+        //   * An installed app can see metadata for 1) other installed apps
+        //     and 2) ephemeral apps that have explicitly interacted with it
+        //   * Ephemeral apps can only see their own metadata
+        final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+        if (callingAppId != Process.SYSTEM_UID
+                && callingAppId != Process.SHELL_UID
+                && callingAppId != Process.ROOT_UID) {
+            final String ephemeralPackageName = getEphemeralPackageName(Binder.getCallingUid());
+            if (ephemeralPackageName != null) {
+                // ephemeral apps can only get information on themselves
+                if (!ephemeralPackageName.equals(p.packageName)) {
+                    return null;
+                }
+            } else {
+                if (p.applicationInfo.isEphemeralApp()) {
+                    // only get access to the ephemeral app if we've been granted access
+                    if (!mEphemeralApplicationRegistry.isEphemeralAccessGranted(
+                            userId, callingAppId, ps.appId)) {
+                        return null;
+                    }
+                }
+            }
+        }
 
         final PermissionsState permissionsState = ps.getPermissionsState();
 
@@ -3276,8 +3338,17 @@
             flags |= MATCH_ANY_USER;
         }
 
-        return PackageParser.generatePackageInfo(p, gids, flags,
+        PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
                 ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
+
+        if (packageInfo == null) {
+            return null;
+        }
+
+        packageInfo.packageName = packageInfo.applicationInfo.packageName =
+                resolveExternalPackageNameLPr(p);
+
+        return packageInfo;
     }
 
     @Override
@@ -3332,6 +3403,20 @@
 
     @Override
     public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+        return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
+                flags, userId);
+    }
+
+    @Override
+    public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
+            int flags, int userId) {
+        return getPackageInfoInternal(versionedPackage.getPackageName(),
+                // TODO: We will change version code to long, so in the new API it is long
+                (int) versionedPackage.getVersionCode(), flags, userId);
+    }
+
+    private PackageInfo getPackageInfoInternal(String packageName, int versionCode,
+            int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForPackage(flags, userId, packageName);
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
@@ -3339,36 +3424,89 @@
 
         // reader
         synchronized (mPackages) {
-            // Normalize package name to hanlde renamed packages
-            packageName = normalizePackageNameLPr(packageName);
+            // Normalize package name to handle renamed packages and static libs
+            packageName = resolveInternalPackageNameLPr(packageName, versionCode);
 
             final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
-            PackageParser.Package p = null;
             if (matchFactoryOnly) {
                 final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
                 if (ps != null) {
+                    if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                        return null;
+                    }
                     return generatePackageInfo(ps, flags, userId);
                 }
             }
-            if (p == null) {
-                p = mPackages.get(packageName);
-                if (matchFactoryOnly && p != null && !isSystemApp(p)) {
-                    return null;
-                }
+
+            PackageParser.Package p = mPackages.get(packageName);
+            if (matchFactoryOnly && p != null && !isSystemApp(p)) {
+                return null;
             }
             if (DEBUG_PACKAGE_INFO)
                 Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
             if (p != null) {
+                if (filterSharedLibPackageLPr((PackageSetting) p.mExtras,
+                        Binder.getCallingUid(), userId)) {
+                    return null;
+                }
                 return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
             }
             if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
+                if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                    return null;
+                }
                 return generatePackageInfo(ps, flags, userId);
             }
         }
         return null;
     }
 
+
+    private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId) {
+        // System/shell/root get to see all static libs
+        final int appId = UserHandle.getAppId(uid);
+        if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
+                || appId == Process.ROOT_UID) {
+            return false;
+        }
+
+        // No package means no static lib as it is always on internal storage
+        if (ps == null || ps.pkg == null || !ps.pkg.applicationInfo.isStaticSharedLibrary()) {
+            return false;
+        }
+
+        final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(ps.pkg.staticSharedLibName,
+                ps.pkg.staticSharedLibVersion);
+        if (libEntry == null) {
+            return false;
+        }
+
+        final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+        final String[] uidPackageNames = getPackagesForUid(resolvedUid);
+        if (uidPackageNames == null) {
+            return true;
+        }
+
+        for (String uidPackageName : uidPackageNames) {
+            if (ps.name.equals(uidPackageName)) {
+                return false;
+            }
+            PackageSetting uidPs = mSettings.getPackageLPr(uidPackageName);
+            if (uidPs != null) {
+                final int index = ArrayUtils.indexOf(uidPs.usesStaticLibraries,
+                        libEntry.info.getName());
+                if (index < 0) {
+                    continue;
+                }
+                if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getVersion()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
     @Override
     public String[] currentToCanonicalPackageNames(String[] names) {
         String[] out = new String[names.length];
@@ -3521,10 +3659,13 @@
     }
 
     private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
-            int userId) {
+            int uid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps != null) {
+            if (filterSharedLibPackageLPr(ps, uid, userId)) {
+                return null;
+            }
             if (ps.pkg == null) {
                 final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
                 if (pInfo != null) {
@@ -3532,8 +3673,12 @@
                 }
                 return null;
             }
-            return PackageParser.generateApplicationInfo(ps.pkg, flags,
+            ApplicationInfo ai = PackageParser.generateApplicationInfo(ps.pkg, flags,
                     ps.readUserState(userId), userId);
+            if (ai != null) {
+                ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
+            }
+            return ai;
         }
         return null;
     }
@@ -3547,8 +3692,9 @@
 
         // writer
         synchronized (mPackages) {
-            // Normalize package name to hanlde renamed packages
-            packageName = normalizePackageNameLPr(packageName);
+            // Normalize package name to handle renamed packages and static libs
+            packageName = resolveInternalPackageNameLPr(packageName,
+                    PackageManager.VERSION_CODE_HIGHEST);
 
             PackageParser.Package p = mPackages.get(packageName);
             if (DEBUG_PACKAGE_INFO) Log.v(
@@ -3557,15 +3703,24 @@
             if (p != null) {
                 PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps == null) return null;
+                if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                    return null;
+                }
                 // Note: isEnabledLP() does not apply here - always return info
-                return PackageParser.generateApplicationInfo(
+                ApplicationInfo ai = PackageParser.generateApplicationInfo(
                         p, flags, ps.readUserState(userId), userId);
+                if (ai != null) {
+                    ai.packageName = resolveExternalPackageNameLPr(p);
+                }
+                return ai;
             }
             if ("android".equals(packageName)||"system".equals(packageName)) {
                 return mAndroidApplication;
             }
             if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
-                return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);
+                // Already generates the external package name
+                return generateApplicationInfoFromSettingsLPw(packageName,
+                        Binder.getCallingUid(), flags, userId);
             }
         }
         return null;
@@ -3588,7 +3743,7 @@
                 boolean success = true;
                 synchronized (mInstallLock) {
                     try {
-                        mInstaller.freeCache(volumeUuid, freeStorageSize);
+                        mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
                     } catch (InstallerException e) {
                         Slog.w(TAG, "Couldn't clear application caches: " + e);
                         success = false;
@@ -3617,7 +3772,7 @@
                 boolean success = true;
                 synchronized (mInstallLock) {
                     try {
-                        mInstaller.freeCache(volumeUuid, freeStorageSize);
+                        mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
                     } catch (InstallerException e) {
                         Slog.w(TAG, "Couldn't clear application caches: " + e);
                         success = false;
@@ -3640,7 +3795,7 @@
     void freeStorage(String volumeUuid, long freeStorageSize) throws IOException {
         synchronized (mInstallLock) {
             try {
-                mInstaller.freeCache(volumeUuid, freeStorageSize);
+                mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
             } catch (InstallerException e) {
                 throw new IOException("Failed to free enough space", e);
             }
@@ -3748,6 +3903,19 @@
     }
 
     /**
+     * Update given intent when being used to request {@link ResolveInfo}.
+     */
+    private Intent updateIntentForResolve(Intent intent) {
+        if (intent.getSelector() != null) {
+            intent = intent.getSelector();
+        }
+        if (DEBUG_PREFERRED) {
+            intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
+        }
+        return intent;
+    }
+
+    /**
      * Update given flags when being used to request {@link ResolveInfo}.
      */
     int updateFlagsForResolve(int flags, int userId, Object cookie) {
@@ -3838,6 +4006,113 @@
     }
 
     @Override
+    public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(int flags, int userId) {
+        if (!sUserManager.exists(userId)) return null;
+        Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
+
+        flags = updateFlagsForPackage(flags, userId, null);
+
+        final boolean canSeeStaticLibraries =
+                mContext.checkCallingOrSelfPermission(INSTALL_PACKAGES)
+                        == PERMISSION_GRANTED
+                || mContext.checkCallingOrSelfPermission(DELETE_PACKAGES)
+                        == PERMISSION_GRANTED
+                || mContext.checkCallingOrSelfPermission(REQUEST_INSTALL_PACKAGES)
+                        == PERMISSION_GRANTED
+                || mContext.checkCallingOrSelfPermission(REQUEST_DELETE_PACKAGES)
+                        == PERMISSION_GRANTED;
+
+        synchronized (mPackages) {
+            List<SharedLibraryInfo> result = null;
+
+            final int libCount = mSharedLibraries.size();
+            for (int i = 0; i < libCount; i++) {
+                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                if (versionedLib == null) {
+                    continue;
+                }
+
+                final int versionCount = versionedLib.size();
+                for (int j = 0; j < versionCount; j++) {
+                    SharedLibraryInfo libInfo = versionedLib.valueAt(j).info;
+                    if (!canSeeStaticLibraries && libInfo.isStatic()) {
+                        break;
+                    }
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        // TODO: We will change version code to long, so in the new API it is long
+                        PackageInfo packageInfo = getPackageInfoVersioned(
+                                libInfo.getDeclaringPackage(), flags, userId);
+                        if (packageInfo == null) {
+                            continue;
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+
+                    SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(),
+                            libInfo.getVersion(), libInfo.getType(), libInfo.getDeclaringPackage(),
+                            getPackagesUsingSharedLibraryLPr(libInfo, flags, userId));
+
+                    if (result == null) {
+                        result = new ArrayList<>();
+                    }
+                    result.add(resLibInfo);
+                }
+            }
+
+            return result != null ? new ParceledListSlice<>(result) : null;
+        }
+    }
+
+    private List<VersionedPackage> getPackagesUsingSharedLibraryLPr(
+            SharedLibraryInfo libInfo, int flags, int userId) {
+        List<VersionedPackage> versionedPackages = null;
+        final int packageCount = mSettings.mPackages.size();
+        for (int i = 0; i < packageCount; i++) {
+            PackageSetting ps = mSettings.mPackages.valueAt(i);
+
+            if (ps == null) {
+                continue;
+            }
+
+            if (!ps.getUserState().get(userId).isAvailable(flags)) {
+                continue;
+            }
+
+            final String libName = libInfo.getName();
+            if (libInfo.isStatic()) {
+                final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
+                if (libIdx < 0) {
+                    continue;
+                }
+                if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getVersion()) {
+                    continue;
+                }
+                if (versionedPackages == null) {
+                    versionedPackages = new ArrayList<>();
+                }
+                // If the dependent is a static shared lib, use the public package name
+                String dependentPackageName = ps.name;
+                if (ps.pkg != null && ps.pkg.applicationInfo.isStaticSharedLibrary()) {
+                    dependentPackageName = ps.pkg.manifestPackageName;
+                }
+                versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode));
+            } else if (ps.pkg != null) {
+                if (ArrayUtils.contains(ps.pkg.usesLibraries, libName)
+                        || ArrayUtils.contains(ps.pkg.usesOptionalLibraries, libName)) {
+                    if (versionedPackages == null) {
+                        versionedPackages = new ArrayList<>();
+                    }
+                    versionedPackages.add(new VersionedPackage(ps.name, ps.versionCode));
+                }
+            }
+        }
+
+        return versionedPackages;
+    }
+
+    @Override
     public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId, component);
@@ -3879,17 +4154,44 @@
 
     @Override
     public String[] getSystemSharedLibraryNames() {
-        Set<String> libSet;
         synchronized (mPackages) {
-            libSet = mSharedLibraries.keySet();
-            int size = libSet.size();
-            if (size > 0) {
-                String[] libs = new String[size];
-                libSet.toArray(libs);
-                return libs;
+            Set<String> libs = null;
+            final int libCount = mSharedLibraries.size();
+            for (int i = 0; i < libCount; i++) {
+                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                if (versionedLib == null) {
+                    continue;
+                }
+                final int versionCount = versionedLib.size();
+                for (int j = 0; j < versionCount; j++) {
+                    SharedLibraryEntry libEntry = versionedLib.valueAt(j);
+                    if (!libEntry.info.isStatic()) {
+                        if (libs == null) {
+                            libs = new ArraySet<>();
+                        }
+                        libs.add(libEntry.info.getName());
+                        break;
+                    }
+                    PackageSetting ps = mSettings.getPackageLPr(libEntry.apk);
+                    if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(),
+                            UserHandle.getUserId(Binder.getCallingUid()))) {
+                        if (libs == null) {
+                            libs = new ArraySet<>();
+                        }
+                        libs.add(libEntry.info.getName());
+                        break;
+                    }
+                }
             }
+
+            if (libs != null) {
+                String[] libsArray = new String[libs.size()];
+                libs.toArray(libsArray);
+                return libsArray;
+            }
+
+            return null;
         }
-        return null;
     }
 
     @Override
@@ -5001,7 +5303,9 @@
                 return res;
             } else if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
-                return new String[] { ps.name };
+                if (ps.getInstalled(userId)) {
+                    return new String[]{ps.name};
+                }
             }
         }
         return null;
@@ -5131,6 +5435,26 @@
     }
 
     @Override
+    public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) {
+        if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
+            throw new SecurityException(
+                    "findPersistentPreferredActivity can only be run by the system");
+        }
+        if (!sUserManager.exists(userId)) {
+            return null;
+        }
+        intent = updateIntentForResolve(intent);
+        final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
+        final int flags = updateFlagsForResolve(0, userId, intent);
+        final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
+                userId);
+        synchronized (mPackages) {
+            return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
+                    userId);
+        }
+    }
+
+    @Override
     public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
             IntentFilter filter, int match, ComponentName activity) {
         final int userId = UserHandle.getCallingUserId();
@@ -5396,13 +5720,9 @@
             boolean removeMatches, boolean debug, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForResolve(flags, userId, intent);
+        intent = updateIntentForResolve(intent);
         // writer
         synchronized (mPackages) {
-            if (intent.getSelector() != null) {
-                intent = intent.getSelector();
-            }
-            if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
-
             // Try to find a matching persistent preferred activity.
             ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query,
                     debug, userId);
@@ -5968,7 +6288,7 @@
                     int status = (int)(packedStatus >> 32);
                     int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
                     if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
-                        if (DEBUG_DOMAIN_VERIFICATION) {
+                        if (DEBUG_DOMAIN_VERIFICATION || debug) {
                             Slog.i(TAG, "  + always: " + info.activityInfo.packageName
                                     + " : linkgen=" + linkGeneration);
                         }
@@ -5977,18 +6297,18 @@
                         info.preferredOrder = linkGeneration;
                         alwaysList.add(info);
                     } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
-                        if (DEBUG_DOMAIN_VERIFICATION) {
+                        if (DEBUG_DOMAIN_VERIFICATION || debug) {
                             Slog.i(TAG, "  + never: " + info.activityInfo.packageName);
                         }
                         neverList.add(info);
                     } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
-                        if (DEBUG_DOMAIN_VERIFICATION) {
+                        if (DEBUG_DOMAIN_VERIFICATION || debug) {
                             Slog.i(TAG, "  + always-ask: " + info.activityInfo.packageName);
                         }
                         alwaysAskList.add(info);
                     } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
                             status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
-                        if (DEBUG_DOMAIN_VERIFICATION) {
+                        if (DEBUG_DOMAIN_VERIFICATION || debug) {
                             Slog.i(TAG, "  + ask: " + info.activityInfo.packageName);
                         }
                         undefinedList.add(info);
@@ -6553,30 +6873,32 @@
         synchronized (mPackages) {
             ArrayList<PackageInfo> list;
             if (listUninstalled) {
-                list = new ArrayList<PackageInfo>(mSettings.mPackages.size());
+                list = new ArrayList<>(mSettings.mPackages.size());
                 for (PackageSetting ps : mSettings.mPackages.values()) {
-                    final PackageInfo pi;
-                    if (ps.pkg != null) {
-                        pi = generatePackageInfo(ps, flags, userId);
-                    } else {
-                        pi = generatePackageInfo(ps, flags, userId);
+                    if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                        continue;
                     }
+                    final PackageInfo pi = generatePackageInfo(ps, flags, userId);
                     if (pi != null) {
                         list.add(pi);
                     }
                 }
             } else {
-                list = new ArrayList<PackageInfo>(mPackages.size());
+                list = new ArrayList<>(mPackages.size());
                 for (PackageParser.Package p : mPackages.values()) {
-                    final PackageInfo pi =
-                            generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
+                    if (filterSharedLibPackageLPr((PackageSetting) p.mExtras,
+                            Binder.getCallingUid(), userId)) {
+                        continue;
+                    }
+                    final PackageInfo pi = generatePackageInfo((PackageSetting)
+                            p.mExtras, flags, userId);
                     if (pi != null) {
                         list.add(pi);
                     }
                 }
             }
 
-            return new ParceledListSlice<PackageInfo>(list);
+            return new ParceledListSlice<>(list);
         }
     }
 
@@ -6596,12 +6918,8 @@
         if (numMatch == 0) {
             return;
         }
-        final PackageInfo pi;
-        if (ps.pkg != null) {
-            pi = generatePackageInfo(ps, flags, userId);
-        } else {
-            pi = generatePackageInfo(ps, flags, userId);
-        }
+        final PackageInfo pi = generatePackageInfo(ps, flags, userId);
+
         // The above might return null in cases of uninstalled apps or install-state
         // skew across users/profiles.
         if (pi != null) {
@@ -6666,7 +6984,7 @@
         synchronized (mPackages) {
             ArrayList<ApplicationInfo> list;
             if (listUninstalled) {
-                list = new ArrayList<ApplicationInfo>(mSettings.mPackages.size());
+                list = new ArrayList<>(mSettings.mPackages.size());
                 for (PackageSetting ps : mSettings.mPackages.values()) {
                     ApplicationInfo ai;
                     int effectiveFlags = flags;
@@ -6674,30 +6992,43 @@
                         effectiveFlags |= PackageManager.MATCH_ANY_USER;
                     }
                     if (ps.pkg != null) {
+                        if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                            continue;
+                        }
                         ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
                                 ps.readUserState(userId), userId);
+                        if (ai != null) {
+                            ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
+                        }
                     } else {
-                        ai = generateApplicationInfoFromSettingsLPw(ps.name, effectiveFlags,
-                                userId);
+                        // Shared lib filtering done in generateApplicationInfoFromSettingsLPw
+                        // and already converts to externally visible package name
+                        ai = generateApplicationInfoFromSettingsLPw(ps.name,
+                                Binder.getCallingUid(), effectiveFlags, userId);
                     }
                     if (ai != null) {
                         list.add(ai);
                     }
                 }
             } else {
-                list = new ArrayList<ApplicationInfo>(mPackages.size());
+                list = new ArrayList<>(mPackages.size());
                 for (PackageParser.Package p : mPackages.values()) {
                     if (p.mExtras != null) {
+                        PackageSetting ps = (PackageSetting) p.mExtras;
+                        if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                            continue;
+                        }
                         ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
-                                ((PackageSetting)p.mExtras).readUserState(userId), userId);
+                                ps.readUserState(userId), userId);
                         if (ai != null) {
+                            ai.packageName = resolveExternalPackageNameLPr(p);
                             list.add(ai);
                         }
                     }
                 }
             }
 
-            return new ParceledListSlice<ApplicationInfo>(list);
+            return new ParceledListSlice<>(list);
         }
     }
 
@@ -6787,6 +7118,7 @@
 
         mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
                 "getEphemeralApplicationIcon");
+
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "getEphemeralApplicationIcon");
@@ -7063,9 +7395,15 @@
             int errorCode = PackageManager.INSTALL_SUCCEEDED;
 
             if (throwable == null) {
+                // Static shared libraries have synthetic package names
+                if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
+                    renameStaticSharedLibraryPackage(parseResult.pkg);
+                }
                 try {
-                    scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags,
-                            currentTime, null);
+                    if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
+                        scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags,
+                                currentTime, null);
+                    }
                 } catch (PackageManagerException e) {
                     errorCode = e.error;
                     Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
@@ -7219,6 +7557,11 @@
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
+        // Static shared libraries have synthetic package names
+        if (pkg.applicationInfo.isStaticSharedLibrary()) {
+            renameStaticSharedLibraryPackage(pkg);
+        }
+
         return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
     }
 
@@ -7521,6 +7864,12 @@
         return scannedPkg;
     }
 
+    private void renameStaticSharedLibraryPackage(PackageParser.Package pkg) {
+        // Derive the new package synthetic package name
+        pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER
+                + pkg.staticSharedLibVersion);
+    }
+
     private static String fixProcessName(String defProcessName,
             String processName) {
         if (processName == null) {
@@ -7876,8 +8225,9 @@
                 targetCompilerFilter, getOrCreateCompilerPackageStats(p));
     }
 
-    Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
-        if (p.usesLibraries != null || p.usesOptionalLibraries != null) {
+    List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
+        if (p.usesLibraries != null || p.usesOptionalLibraries != null
+                || p.usesStaticLibraries != null) {
             ArrayList<PackageParser.Package> retValue = new ArrayList<>();
             Set<String> collectedNames = new HashSet<>();
             findSharedNonSystemLibrariesRecursive(p, retValue, collectedNames);
@@ -7891,37 +8241,74 @@
     }
 
     private void findSharedNonSystemLibrariesRecursive(PackageParser.Package p,
-            Collection<PackageParser.Package> collected, Set<String> collectedNames) {
+            ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
         if (!collectedNames.contains(p.packageName)) {
             collectedNames.add(p.packageName);
             collected.add(p);
 
             if (p.usesLibraries != null) {
-                findSharedNonSystemLibrariesRecursive(p.usesLibraries, collected, collectedNames);
+                findSharedNonSystemLibrariesRecursive(p.usesLibraries,
+                        null, collected, collectedNames);
             }
             if (p.usesOptionalLibraries != null) {
-                findSharedNonSystemLibrariesRecursive(p.usesOptionalLibraries, collected,
-                        collectedNames);
+                findSharedNonSystemLibrariesRecursive(p.usesOptionalLibraries,
+                        null, collected, collectedNames);
+            }
+            if (p.usesStaticLibraries != null) {
+                findSharedNonSystemLibrariesRecursive(p.usesStaticLibraries,
+                        p.usesStaticLibrariesVersions, collected, collectedNames);
             }
         }
     }
 
-    private void findSharedNonSystemLibrariesRecursive(Collection<String> libs,
-            Collection<PackageParser.Package> collected, Set<String> collectedNames) {
-        for (String libName : libs) {
-            PackageParser.Package libPkg = findSharedNonSystemLibrary(libName);
+    private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, int[] versions,
+            ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
+        final int libNameCount = libs.size();
+        for (int i = 0; i < libNameCount; i++) {
+            String libName = libs.get(i);
+            int version = (versions != null && versions.length == libNameCount)
+                    ? versions[i] : PackageManager.VERSION_CODE_HIGHEST;
+            PackageParser.Package libPkg = findSharedNonSystemLibrary(libName, version);
             if (libPkg != null) {
                 findSharedNonSystemLibrariesRecursive(libPkg, collected, collectedNames);
             }
         }
     }
 
-    private PackageParser.Package findSharedNonSystemLibrary(String libName) {
+    private PackageParser.Package findSharedNonSystemLibrary(String name, int version) {
         synchronized (mPackages) {
-            PackageManagerService.SharedLibraryEntry lib = mSharedLibraries.get(libName);
-            if (lib != null && lib.apk != null) {
-                return mPackages.get(lib.apk);
+            SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(name, version);
+            if (libEntry != null) {
+                return mPackages.get(libEntry.apk);
             }
+            return null;
+        }
+    }
+
+    private SharedLibraryEntry getSharedLibraryEntryLPr(String name, int version) {
+        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+        if (versionedLib == null) {
+            return null;
+        }
+        return versionedLib.get(version);
+    }
+
+    private SharedLibraryEntry getLatestSharedLibraVersionLPr(PackageParser.Package pkg) {
+        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+                pkg.staticSharedLibName);
+        if (versionedLib == null) {
+            return null;
+        }
+        int previousLibVersion = -1;
+        final int versionCount = versionedLib.size();
+        for (int i = 0; i < versionCount; i++) {
+            final int libVersion = versionedLib.keyAt(i);
+            if (libVersion < pkg.staticSharedLibVersion) {
+                previousLibVersion = Math.max(previousLibVersion, libVersion);
+            }
+        }
+        if (previousLibVersion >= 0) {
+            return versionedLib.get(previousLibVersion);
         }
         return null;
     }
@@ -8215,36 +8602,84 @@
 
     private void updateSharedLibrariesLPr(PackageParser.Package pkg,
             PackageParser.Package changingLib) throws PackageManagerException {
-        if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
-            final ArraySet<String> usesLibraryFiles = new ArraySet<>();
-            int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
-            for (int i=0; i<N; i++) {
-                final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesLibraries.get(i));
-                if (file == null) {
+        if (pkg == null) {
+            return;
+        }
+        ArraySet<String> usesLibraryFiles = null;
+        if (pkg.usesLibraries != null) {
+            usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
+                    null, null, pkg.packageName, changingLib, true, null);
+        }
+        if (pkg.usesStaticLibraries != null) {
+            usesLibraryFiles = addSharedLibrariesLPw(pkg.usesStaticLibraries,
+                    pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests,
+                    pkg.packageName, changingLib, true, usesLibraryFiles);
+        }
+        if (pkg.usesOptionalLibraries != null) {
+            usesLibraryFiles = addSharedLibrariesLPw(pkg.usesOptionalLibraries,
+                    null, null, pkg.packageName, changingLib, false, usesLibraryFiles);
+        }
+        if (!ArrayUtils.isEmpty(usesLibraryFiles)) {
+            pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]);
+        } else {
+            pkg.usesLibraryFiles = null;
+        }
+    }
+
+    private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
+            @Nullable int[] requiredVersions, @Nullable String[] requiredCertDigests,
+            @NonNull String packageName, @Nullable PackageParser.Package changingLib,
+            boolean required, @Nullable ArraySet<String> outUsedLibraries)
+            throws PackageManagerException {
+        final int libCount = requestedLibraries.size();
+        for (int i = 0; i < libCount; i++) {
+            final String libName = requestedLibraries.get(i);
+            final int libVersion = requiredVersions != null ? requiredVersions[i]
+                    : SharedLibraryInfo.VERSION_UNDEFINED;
+            final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(libName, libVersion);
+            if (libEntry == null) {
+                if (required) {
                     throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
-                            "Package " + pkg.packageName + " requires unavailable shared library "
-                            + pkg.usesLibraries.get(i) + "; failing!");
-                }
-                addSharedLibraryLPr(usesLibraryFiles, file, changingLib);
-            }
-            N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
-            for (int i=0; i<N; i++) {
-                final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
-                if (file == null) {
-                    Slog.w(TAG, "Package " + pkg.packageName
-                            + " desires unavailable shared library "
-                            + pkg.usesOptionalLibraries.get(i) + "; ignoring!");
+                            "Package " + packageName + " requires unavailable shared library "
+                                    + libName + "; failing!");
                 } else {
-                    addSharedLibraryLPr(usesLibraryFiles, file, changingLib);
+                    Slog.w(TAG, "Package " + packageName
+                            + " desires unavailable shared library "
+                            + libName + "; ignoring!");
                 }
-            }
-            N = usesLibraryFiles.size();
-            if (N > 0) {
-                pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[N]);
             } else {
-                pkg.usesLibraryFiles = null;
+                if (requiredVersions != null && requiredCertDigests != null) {
+                    if (libEntry.info.getVersion() != requiredVersions[i]) {
+                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                            "Package " + packageName + " requires unavailable static shared"
+                                    + " library " + libName + " version "
+                                    + libEntry.info.getVersion() + "; failing!");
+                    }
+
+                    PackageParser.Package libPkg = mPackages.get(libEntry.apk);
+                    if (libPkg == null) {
+                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                "Package " + packageName + " requires unavailable static shared"
+                                        + " library; failing!");
+                    }
+
+                    String expectedCertDigest = requiredCertDigests[i];
+                    String libCertDigest = PackageUtils.computeCertSha256Digest(
+                                libPkg.mSignatures[0]);
+                    if (!libCertDigest.equalsIgnoreCase(expectedCertDigest)) {
+                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+                                "Package " + packageName + " requires differently signed" +
+                                        " static shared library; failing!");
+                    }
+                }
+
+                if (outUsedLibraries == null) {
+                    outUsedLibraries = new ArraySet<>();
+                }
+                addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib);
             }
         }
+        return outUsedLibraries;
     }
 
     private static boolean hasString(List<String> list, List<String> which) {
@@ -8261,31 +8696,36 @@
         return false;
     }
 
-    private void updateAllSharedLibrariesLPw() {
-        for (PackageParser.Package pkg : mPackages.values()) {
-            try {
-                updateSharedLibrariesLPr(pkg, null);
-            } catch (PackageManagerException e) {
-                Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
-            }
-        }
-    }
-
     private ArrayList<PackageParser.Package> updateAllSharedLibrariesLPw(
             PackageParser.Package changingPkg) {
         ArrayList<PackageParser.Package> res = null;
         for (PackageParser.Package pkg : mPackages.values()) {
-            if (hasString(pkg.usesLibraries, changingPkg.libraryNames)
-                    || hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)) {
-                if (res == null) {
-                    res = new ArrayList<PackageParser.Package>();
+            if (changingPkg != null
+                    && !hasString(pkg.usesLibraries, changingPkg.libraryNames)
+                    && !hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)
+                    && !ArrayUtils.contains(pkg.usesStaticLibraries,
+                            changingPkg.staticSharedLibName)) {
+                return null;
+            }
+            if (res == null) {
+                res = new ArrayList<>();
+            }
+            res.add(pkg);
+            try {
+                updateSharedLibrariesLPr(pkg, changingPkg);
+            } catch (PackageManagerException e) {
+                // If a system app update or an app and a required lib missing we
+                // delete the package and for updated system apps keep the data as
+                // it is better for the user to reinstall than to be in an limbo
+                // state. Also libs disappearing under an app should never happen
+                // - just in case.
+                if (!pkg.isSystemApp() || pkg.isUpdatedSystemApp()) {
+                    final int flags = pkg.isUpdatedSystemApp()
+                            ? PackageManager.DELETE_KEEP_DATA : 0;
+                    deletePackageLIF(pkg.packageName, null, true, sUserManager.getUserIds(),
+                            flags , null, true, null);
                 }
-                res.add(pkg);
-                try {
-                    updateSharedLibrariesLPr(pkg, changingPkg);
-                } catch (PackageManagerException e) {
-                    Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
-                }
+                Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
             }
         }
         return res;
@@ -8543,9 +8983,17 @@
                     pkgSetting == null ? null : new PackageSetting(pkgSetting);
             final PackageSetting disabledPkgSetting =
                     mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+
+            String[] usesStaticLibraries = null;
+            if (pkg.usesStaticLibraries != null) {
+                usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
+                pkg.usesStaticLibraries.toArray(usesStaticLibraries);
+            }
+
             if (pkgSetting == null) {
                 final String parentPackageName = (pkg.parentPackage != null)
                         ? pkg.parentPackage.packageName : null;
+
                 // REMOVE SharedUserSetting from method; update in a separate call
                 pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,
                         disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,
@@ -8553,7 +9001,8 @@
                         pkg.applicationInfo.secondaryCpuAbi, pkg.mVersionCode,
                         pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user,
                         true /*allowInstall*/, parentPackageName, pkg.getChildPackageNames(),
-                        UserManagerService.getInstance());
+                        UserManagerService.getInstance(), usesStaticLibraries,
+                        pkg.usesStaticLibrariesVersions);
                 // SIDE EFFECTS; updates system state; move elsewhere
                 if (origPackage != null) {
                     mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);
@@ -8569,7 +9018,8 @@
                         pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.primaryCpuAbi,
                         pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags,
                         pkg.applicationInfo.privateFlags, pkg.getChildPackageNames(),
-                        UserManagerService.getInstance());
+                        UserManagerService.getInstance(), usesStaticLibraries,
+                        pkg.usesStaticLibrariesVersions);
             }
             // SIDE EFFECTS; persists system state to files on disk; move elsewhere
             mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
@@ -8606,12 +9056,15 @@
                 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
 
-            if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+            if ((scanFlags & SCAN_BOOTING) == 0
+                    && (policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                 // Check all shared libraries and map to their actual file path.
                 // We only do this here for apps not on a system dir, because those
                 // are the only ones that can fail an install due to this.  We
                 // will take care of the system apps by updating all of their
-                // library paths after the scan is done.
+                // library paths after the scan is done. Also during the initial
+                // scan don't update any libs as we do this wholesale after all
+                // apps are scanned to avoid dependency based scanning.
                 updateSharedLibrariesLPr(pkg, null);
             }
 
@@ -8621,8 +9074,22 @@
 
             pkg.applicationInfo.uid = pkgSetting.appId;
             pkg.mExtras = pkgSetting;
-            if (shouldCheckUpgradeKeySetLP(pkgSetting, scanFlags)) {
-                if (checkUpgradeKeySetLP(pkgSetting, pkg)) {
+
+
+            // Static shared libs have same package with different versions where
+            // we internally use a synthetic package name to allow multiple versions
+            // of the same package, therefore we need to compare signatures against
+            // the package setting for the latest library version.
+            PackageSetting signatureCheckPs = pkgSetting;
+            if (pkg.applicationInfo.isStaticSharedLibrary()) {
+                SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
+                if (libraryEntry != null) {
+                    signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
+                }
+            }
+
+            if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
+                if (checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
                     pkgSetting.signatures.mSignatures = pkg.mSignatures;
@@ -8641,7 +9108,7 @@
             } else {
                 try {
                     // SIDE EFFECTS; compareSignaturesCompat() changes KeysetManagerService
-                    verifySignaturesLP(pkgSetting, pkg);
+                    verifySignaturesLP(signatureCheckPs, pkg);
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
                     pkgSetting.signatures.mSignatures = pkg.mSignatures;
@@ -8657,8 +9124,8 @@
                     // What this means is that you can't change the signatures
                     // associated with an overall shared user, which doesn't seem all
                     // that unreasonable.
-                    if (pkgSetting.sharedUser != null) {
-                        if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
+                    if (signatureCheckPs.sharedUser != null) {
+                        if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
                                 pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
                             throw new PackageManagerException(
                                     INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
@@ -8842,6 +9309,10 @@
             // Modify state for the given package setting
             commitPackageSettings(pkg, pkgSetting, user, scanFlags,
                     (policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
+            if (isEphemeral(pkg)) {
+                final int userId = user == null ? 0 : user.getIdentifier();
+                mEphemeralApplicationRegistry.addEphemeralAppLPw(userId, pkgSetting.appId);
+            }
         }
         return pkg;
     }
@@ -8895,7 +9366,7 @@
     }
 
     /**
-     * Asserts the parsed package is valid according to teh given policy. If the
+     * Asserts the parsed package is valid according to the given policy. If the
      * package is invalid, for whatever reason, throws {@link PackgeManagerException}.
      * <p>
      * Implementation detail: This method must NOT have any side effects. It would
@@ -8934,13 +9405,134 @@
             }
 
             // A package name must be unique; don't allow duplicates
-            if (mPackages.containsKey(pkg.packageName)
-                    || mSharedLibraries.containsKey(pkg.packageName)) {
+            if (mPackages.containsKey(pkg.packageName)) {
                 throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
                         "Application package " + pkg.packageName
                         + " already installed.  Skipping duplicate.");
             }
 
+            if (pkg.applicationInfo.isStaticSharedLibrary()) {
+                // Static libs have a synthetic package name containing the version
+                // but we still want the base name to be unique.
+                if (mPackages.containsKey(pkg.manifestPackageName)) {
+                    throw new PackageManagerException(
+                            "Duplicate static shared lib provider package");
+                }
+
+                // Static shared libraries should have at least O target SDK
+                if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+                    throw new PackageManagerException(
+                            "Packages declaring static-shared libs must target O SDK or higher");
+                }
+
+                // Package declaring static a shared lib cannot be ephemeral
+                if (pkg.applicationInfo.isEphemeralApp()) {
+                    throw new PackageManagerException(
+                            "Packages declaring static-shared libs cannot be ephemeral");
+                }
+
+                // Package declaring static a shared lib cannot be renamed since the package
+                // name is synthetic and apps can't code around package manager internals.
+                if (!ArrayUtils.isEmpty(pkg.mOriginalPackages)) {
+                    throw new PackageManagerException(
+                            "Packages declaring static-shared libs cannot be renamed");
+                }
+
+                // Package declaring static a shared lib cannot declare child packages
+                if (!ArrayUtils.isEmpty(pkg.childPackages)) {
+                    throw new PackageManagerException(
+                            "Packages declaring static-shared libs cannot have child packages");
+                }
+
+                // Package declaring static a shared lib cannot declare dynamic libs
+                if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
+                    throw new PackageManagerException(
+                            "Packages declaring static-shared libs cannot declare dynamic libs");
+                }
+
+                // Package declaring static a shared lib cannot declare shared users
+                if (pkg.mSharedUserId != null) {
+                    throw new PackageManagerException(
+                            "Packages declaring static-shared libs cannot declare shared users");
+                }
+
+                // Static shared libs cannot declare activities
+                if (!pkg.activities.isEmpty()) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot declare activities");
+                }
+
+                // Static shared libs cannot declare services
+                if (!pkg.services.isEmpty()) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot declare services");
+                }
+
+                // Static shared libs cannot declare providers
+                if (!pkg.providers.isEmpty()) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot declare content providers");
+                }
+
+                // Static shared libs cannot declare receivers
+                if (!pkg.receivers.isEmpty()) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot declare broadcast receivers");
+                }
+
+                // Static shared libs cannot declare permission groups
+                if (!pkg.permissionGroups.isEmpty()) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot declare permission groups");
+                }
+
+                // Static shared libs cannot declare permissions
+                if (!pkg.permissions.isEmpty()) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot declare permissions");
+                }
+
+                // Static shared libs cannot declare protected broadcasts
+                if (pkg.protectedBroadcasts != null) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot declare protected broadcasts");
+                }
+
+                // Static shared libs cannot be overlay targets
+                if (pkg.mOverlayTarget != null) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot be overlay targets");
+                }
+
+                // The version codes must be ordered as lib versions
+                int minVersionCode = Integer.MIN_VALUE;
+                int maxVersionCode = Integer.MAX_VALUE;
+
+                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+                        pkg.staticSharedLibName);
+                if (versionedLib != null) {
+                    final int versionCount = versionedLib.size();
+                    for (int i = 0; i < versionCount; i++) {
+                        SharedLibraryInfo libInfo = versionedLib.valueAt(i).info;
+                        // TODO: We will change version code to long, so in the new API it is long
+                        final int libVersionCode = (int) libInfo.getDeclaringPackage()
+                                .getVersionCode();
+                        if (libInfo.getVersion() <  pkg.staticSharedLibVersion) {
+                            minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
+                        } else if (libInfo.getVersion() >  pkg.staticSharedLibVersion) {
+                            maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
+                        } else {
+                            minVersionCode = maxVersionCode = libVersionCode;
+                            break;
+                        }
+                    }
+                }
+                if (pkg.mVersionCode < minVersionCode || pkg.mVersionCode > maxVersionCode) {
+                    throw new PackageManagerException("Static shared"
+                            + " lib version codes must be ordered as lib versions");
+                }
+            }
+
             // Only privileged apps and updated privileged apps can add child packages.
             if (pkg.childPackages != null && !pkg.childPackages.isEmpty()) {
                 if ((policyFlags & PARSE_IS_PRIVILEGED) == 0) {
@@ -9021,6 +9613,45 @@
         }
     }
 
+    private boolean addSharedLibraryLPw(String path, String apk, String name, int version,
+            int type, String declaringPackageName, int declaringVersionCode) {
+        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+        if (versionedLib == null) {
+            versionedLib = new SparseArray<>();
+            mSharedLibraries.put(name, versionedLib);
+            if (type == SharedLibraryInfo.TYPE_STATIC) {
+                mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
+            }
+        } else if (versionedLib.indexOfKey(version) >= 0) {
+            return false;
+        }
+        SharedLibraryEntry libEntry = new SharedLibraryEntry(path, apk, name,
+                version, type, declaringPackageName, declaringVersionCode);
+        versionedLib.put(version, libEntry);
+        return true;
+    }
+
+    private boolean removeSharedLibraryLPw(String name, int version) {
+        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+        if (versionedLib == null) {
+            return false;
+        }
+        final int libIdx = versionedLib.indexOfKey(version);
+        if (libIdx < 0) {
+            return false;
+        }
+        SharedLibraryEntry libEntry = versionedLib.valueAt(libIdx);
+        versionedLib.remove(version);
+        if (versionedLib.size() <= 0) {
+            mSharedLibraries.remove(name);
+            if (libEntry.info.getType() == SharedLibraryInfo.TYPE_STATIC) {
+                mStaticLibsByDeclaringPackage.remove(libEntry.info.getDeclaringPackage()
+                        .getPackageName());
+            }
+        }
+        return true;
+    }
+
     /**
      * Adds a scanned package to the system. When this method is finished, the package will
      * be available for query, resolution, etc...
@@ -9073,10 +9704,30 @@
         ArrayList<PackageParser.Package> clientLibPkgs = null;
         // writer
         synchronized (mPackages) {
-            if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
-                // Only system apps can add new shared libraries.
+            boolean hasStaticSharedLibs = false;
+
+            // Any app can add new static shared libraries
+            if (pkg.staticSharedLibName != null) {
+                // Static shared libs don't allow renaming as they have synthetic package
+                // names to allow install of multiple versions, so use name from manifest.
+                if (addSharedLibraryLPw(null, pkg.packageName, pkg.staticSharedLibName,
+                        pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
+                        pkg.manifestPackageName, pkg.mVersionCode)) {
+                    hasStaticSharedLibs = true;
+                } else {
+                    Slog.w(TAG, "Package " + pkg.packageName + " library "
+                                + pkg.staticSharedLibName + " already exists; skipping");
+                }
+                // Static shared libs cannot be updated once installed since they
+                // use synthetic package name which includes the version code, so
+                // not need to update other packages's shared lib dependencies.
+            }
+
+            if (!hasStaticSharedLibs
+                    && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                // Only system apps can add new dynamic shared libraries.
                 if (pkg.libraryNames != null) {
-                    for (int i=0; i<pkg.libraryNames.size(); i++) {
+                    for (int i = 0; i < pkg.libraryNames.size(); i++) {
                         String name = pkg.libraryNames.get(i);
                         boolean allowed = false;
                         if (pkg.isUpdatedSystemApp()) {
@@ -9093,7 +9744,7 @@
                             final PackageSetting sysPs = mSettings
                                     .getDisabledSystemPkgLPr(pkg.packageName);
                             if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
-                                for (int j=0; j<sysPs.pkg.libraryNames.size(); j++) {
+                                for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
                                     if (name.equals(sysPs.pkg.libraryNames.get(j))) {
                                         allowed = true;
                                         break;
@@ -9104,9 +9755,10 @@
                             allowed = true;
                         }
                         if (allowed) {
-                            if (!mSharedLibraries.containsKey(name)) {
-                                mSharedLibraries.put(name, new SharedLibraryEntry(null, pkg.packageName));
-                            } else if (!name.equals(pkg.packageName)) {
+                            if (!addSharedLibraryLPw(null, pkg.packageName, name,
+                                    SharedLibraryInfo.VERSION_UNDEFINED,
+                                    SharedLibraryInfo.TYPE_DYNAMIC,
+                                    pkg.packageName, pkg.mVersionCode)) {
                                 Slog.w(TAG, "Package " + pkg.packageName + " library "
                                         + name + " already exists; skipping");
                             }
@@ -9115,6 +9767,7 @@
                                     + name + " that is not declared on system image; skipping");
                         }
                     }
+
                     if ((scanFlags & SCAN_BOOTING) == 0) {
                         // If we are not booting, we need to update any applications
                         // that are clients of our shared library.  If we are booting,
@@ -9462,8 +10115,10 @@
                 a.info.packageName = pkg.applicationInfo.packageName;
                 a.info.sourceDir = pkg.applicationInfo.sourceDir;
                 a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
+                a.info.splitNames = pkg.splitNames;
                 a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;
                 a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;
+                a.info.splitDependencies = pkg.applicationInfo.splitDependencies;
                 a.info.dataDir = pkg.applicationInfo.dataDir;
                 a.info.deviceProtectedDataDir = pkg.applicationInfo.deviceProtectedDataDir;
                 a.info.credentialProtectedDataDir = pkg.applicationInfo.credentialProtectedDataDir;
@@ -10328,11 +10983,9 @@
         if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
             // Only system apps can hold shared libraries.
             if (pkg.libraryNames != null) {
-                for (i=0; i<pkg.libraryNames.size(); i++) {
+                for (i = 0; i < pkg.libraryNames.size(); i++) {
                     String name = pkg.libraryNames.get(i);
-                    SharedLibraryEntry cur = mSharedLibraries.get(name);
-                    if (cur != null && cur.apk != null && cur.apk.equals(pkg.packageName)) {
-                        mSharedLibraries.remove(name);
+                    if (removeSharedLibraryLPw(name, 0)) {
                         if (DEBUG_REMOVE && chatty) {
                             if (r == null) {
                                 r = new StringBuilder(256);
@@ -10345,6 +10998,23 @@
                 }
             }
         }
+
+        r = null;
+
+        // Any package can hold static shared libraries.
+        if (pkg.staticSharedLibName != null) {
+            if (removeSharedLibraryLPw(pkg.staticSharedLibName, pkg.staticSharedLibVersion)) {
+                if (DEBUG_REMOVE && chatty) {
+                    if (r == null) {
+                        r = new StringBuilder(256);
+                    } else {
+                        r.append(' ');
+                    }
+                    r.append(pkg.staticSharedLibName);
+                }
+            }
+        }
+
         if (r != null) {
             if (DEBUG_REMOVE) Log.d(TAG, "  Libraries: " + r);
         }
@@ -12146,7 +12816,7 @@
         IActivityManager am = ActivityManager.getService();
         if (am != null) {
             try {
-                am.startService(null, intent, null, mContext.getOpPackageName(),
+                am.startService(null, intent, null, -1, null, mContext.getOpPackageName(),
                         UserHandle.USER_SYSTEM);
             } catch (RemoteException e) {
             }
@@ -12220,6 +12890,67 @@
         mHandler.sendMessage(msg);
     }
 
+
+    /**
+     * Ensure that the install reason matches what we know about the package installer (e.g. whether
+     * it is acting on behalf on an enterprise or the user).
+     *
+     * Note that the ordering of the conditionals in this method is important. The checks we perform
+     * are as follows, in this order:
+     *
+     * 1) If the install is being performed by a system app, we can trust the app to have set the
+     *    install reason correctly. Thus, we pass through the install reason unchanged, no matter
+     *    what it is.
+     * 2) If the install is being performed by a device or profile owner app, the install reason
+     *    should be enterprise policy. However, we cannot be sure that the device or profile owner
+     *    set the install reason correctly. If the app targets an older SDK version where install
+     *    reasons did not exist yet, or if the app author simply forgot, the install reason may be
+     *    unset or wrong. Thus, we force the install reason to be enterprise policy.
+     * 3) In all other cases, the install is being performed by a regular app that is neither part
+     *    of the system nor a device or profile owner. We have no reason to believe that this app is
+     *    acting on behalf of the enterprise admin. Thus, we check whether the install reason was
+     *    set to enterprise policy and if so, change it to unknown instead.
+     */
+    private int fixUpInstallReason(String installerPackageName, int installerUid,
+            int installReason) {
+        if (checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
+                == PERMISSION_GRANTED) {
+            // If the install is being performed by a system app, we trust that app to have set the
+            // install reason correctly.
+            return installReason;
+        }
+
+        final IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+            ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+        if (dpm != null) {
+            ComponentName owner = null;
+            try {
+                owner = dpm.getDeviceOwnerComponent(true /* callingUserOnly */);
+                if (owner == null) {
+                    owner = dpm.getProfileOwner(UserHandle.getUserId(installerUid));
+                }
+            } catch (RemoteException e) {
+            }
+            if (owner != null && owner.getPackageName().equals(installerPackageName)) {
+                // If the install is being performed by a device or profile owner, the install
+                // reason should be enterprise policy.
+                return PackageManager.INSTALL_REASON_POLICY;
+            }
+        }
+
+        if (installReason == PackageManager.INSTALL_REASON_POLICY) {
+            // If the install is being performed by a regular app (i.e. neither system app nor
+            // device or profile owner), we have no reason to believe that the app is acting on
+            // behalf of an enterprise. If the app set the install reason to enterprise policy,
+            // change it to unknown instead.
+            return PackageManager.INSTALL_REASON_UNKNOWN;
+        }
+
+        // If the install is being performed by a regular app and the install reason was set to any
+        // value but enterprise policy, leave the install reason unchanged.
+        return installReason;
+    }
+
     void installStage(String packageName, File stagedDir, String stagedCid,
             IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
             String installerPackageName, int installerUid, UserHandle user,
@@ -12241,10 +12972,12 @@
         }
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
+        final int installReason = fixUpInstallReason(installerPackageName, installerUid,
+                sessionParams.installReason);
         final InstallParams params = new InstallParams(origin, null, observer,
                 sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                 verificationInfo, user, sessionParams.abiOverride,
-                sessionParams.grantedRuntimePermissions, certificates, sessionParams.installReason);
+                sessionParams.grantedRuntimePermissions, certificates, installReason);
         params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
@@ -12343,6 +13076,16 @@
                     Slog.w(TAG, "Cannot hide package: android");
                     return false;
                 }
+                // Cannot hide static shared libs as they are considered
+                // a part of the using app (emulating static linking). Also
+                // static libs are installed always on internal storage.
+                PackageParser.Package pkg = mPackages.get(packageName);
+                if (pkg != null && pkg.staticSharedLibName != null) {
+                    Slog.w(TAG, "Cannot hide package: " + packageName
+                            + " providing static shared library: "
+                            + pkg.staticSharedLibName);
+                    return false;
+                }
                 // Only allow protected packages to hide themselves.
                 if (hidden && !UserHandle.isSameApp(uid, pkgSetting.appId)
                         && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
@@ -12603,6 +13346,17 @@
             return false;
         }
 
+        // Cannot suspend static shared libs as they are considered
+        // a part of the using app (emulating static linking). Also
+        // static libs are installed always on internal storage.
+        PackageParser.Package pkg = mPackages.get(packageName);
+        if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
+            Slog.w(TAG, "Cannot suspend package: " + packageName
+                    + " providing static shared library: "
+                    + pkg.staticSharedLibName);
+            return false;
+        }
+
         return true;
     }
 
@@ -13647,7 +14401,7 @@
                             origin.resolvedPath, isForwardLocked(), packageAbiOverride);
 
                     try {
-                        mInstaller.freeCache(null, sizeBytes + lowThreshold);
+                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
                         pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                                 installFlags, packageAbiOverride);
                     } catch (InstallerException e) {
@@ -15036,6 +15790,7 @@
         res.removedInfo = new PackageRemovedInfo();
         res.removedInfo.uid = oldPackage.applicationInfo.uid;
         res.removedInfo.removedPackage = oldPackage.packageName;
+        res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
         res.removedInfo.isUpdate = true;
         res.removedInfo.origUsers = installedUsers;
         final PackageSetting ps = mSettings.getPackageLPr(pkgName);
@@ -15698,6 +16453,19 @@
             return;
         }
 
+        if (pkg.applicationInfo.isStaticSharedLibrary()) {
+            // Static shared libraries have synthetic package names
+            renameStaticSharedLibraryPackage(pkg);
+
+            // No static shared libs on external storage
+            if (onExternal) {
+                Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
+                res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+                        "Packages declaring static-shared libs cannot be updated");
+                return;
+            }
+        }
+
         // If we are installing a clustered package add results for the children
         if (pkg.childPackages != null) {
             synchronized (mPackages) {
@@ -15822,11 +16590,23 @@
             if (ps != null) {
                 if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
 
+                // Static shared libs have same package with different versions where
+                // we internally use a synthetic package name to allow multiple versions
+                // of the same package, therefore we need to compare signatures against
+                // the package setting for the latest library version.
+                PackageSetting signatureCheckPs = ps;
+                if (pkg.applicationInfo.isStaticSharedLibrary()) {
+                    SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
+                    if (libraryEntry != null) {
+                        signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
+                    }
+                }
+
                 // Quick sanity check that we're signed correctly if updating;
                 // we'll check this again later when scanning, but we want to
                 // bail early here before tripping over redefined permissions.
-                if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
-                    if (!checkUpgradeKeySetLP(ps, pkg)) {
+                if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
+                    if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
                         res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                 + pkg.packageName + " upgrade keys do not match the "
                                 + "previously installed version");
@@ -15834,7 +16614,7 @@
                     }
                 } else {
                     try {
-                        verifySignaturesLP(ps, pkg);
+                        verifySignaturesLP(signatureCheckPs, pkg);
                     } catch (PackageManagerException e) {
                         res.setError(e.error, e.getMessage());
                         return;
@@ -15951,14 +16731,6 @@
                 return;
             }
 
-            // Shared libraries for the package need to be updated.
-            synchronized (mPackages) {
-                try {
-                    updateSharedLibrariesLPr(pkg, null);
-                } catch (PackageManagerException e) {
-                    Slog.e(TAG, "updateSharedLibrariesLPw failed: " + e.getMessage());
-                }
-            }
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
             // Do not run PackageDexOptimizer through the local performDexOpt
             // method because `pkg` may not be in `mPackages` yet.
@@ -15986,6 +16758,18 @@
         try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                 "installPackageLI")) {
             if (replace) {
+                if (pkg.applicationInfo.isStaticSharedLibrary()) {
+                    // Static libs have a synthetic package name containing the version
+                    // and cannot be updated as an update would get a new package name,
+                    // unless this is the exact same version code which is useful for
+                    // development.
+                    PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
+                    if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) {
+                        res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
+                                + "static-shared libs cannot be updated");
+                        return;
+                    }
+                }
                 replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                         installerPackageName, res, args.installReason);
             } else {
@@ -16231,22 +17015,37 @@
     }
 
     @Override
-    public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int userId,
-            int flags) {
-        deletePackage(packageName, new LegacyPackageDeleteObserver(observer).getBinder(), userId,
-                flags);
+    public void deletePackageAsUser(String packageName, int versionCode,
+            IPackageDeleteObserver observer, int userId, int flags) {
+        deletePackageVersioned(new VersionedPackage(packageName, versionCode),
+                new LegacyPackageDeleteObserver(observer).getBinder(), userId, flags);
     }
 
     @Override
-    public void deletePackage(final String packageName,
+    public void deletePackageVersioned(VersionedPackage versionedPackage,
             final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_PACKAGES, null);
-        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(versionedPackage);
         Preconditions.checkNotNull(observer);
+        Preconditions.checkArgumentInRange(versionedPackage.getVersionCode(),
+                PackageManager.VERSION_CODE_HIGHEST,
+                Integer.MAX_VALUE, "versionCode must be >= -1");
+
+        final String packageName = versionedPackage.getPackageName();
+        // TODO: We will change version code to long, so in the new API it is long
+        final int versionCode = (int) versionedPackage.getVersionCode();
+        final String internalPackageName;
+        synchronized (mPackages) {
+            // Normalize package name to handle renamed packages and static libs
+            internalPackageName = resolveInternalPackageNameLPr(versionedPackage.getPackageName(),
+                    // TODO: We will change version code to long, so in the new API it is long
+                    (int) versionedPackage.getVersionCode());
+        }
+
         final int uid = Binder.getCallingUid();
-        if (!isOrphaned(packageName)
-                && !isCallerAllowedToSilentlyUninstall(uid, packageName)) {
+        if (!isOrphaned(internalPackageName)
+                && !isCallerAllowedToSilentlyUninstall(uid, internalPackageName)) {
             try {
                 final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
                 intent.setData(Uri.fromParts(PACKAGE_SCHEME, packageName, null));
@@ -16273,7 +17072,7 @@
             return;
         }
 
-        if (!deleteAllUsers && getBlockUninstallForUser(packageName, userId)) {
+        if (!deleteAllUsers && getBlockUninstallForUser(internalPackageName, userId)) {
             try {
                 observer.onPackageDeleted(packageName,
                         PackageManager.DELETE_FAILED_OWNER_BLOCKED, null);
@@ -16283,8 +17082,10 @@
         }
 
         if (DEBUG_REMOVE) {
-            Slog.d(TAG, "deletePackageAsUser: pkg=" + packageName + " user=" + userId
-                    + " deleteAllUsers: " + deleteAllUsers );
+            Slog.d(TAG, "deletePackageAsUser: pkg=" + internalPackageName + " user=" + userId
+                    + " deleteAllUsers: " + deleteAllUsers + " version="
+                    + (versionCode == PackageManager.VERSION_CODE_HIGHEST
+                    ? "VERSION_CODE_HIGHEST" : versionCode));
         }
         // Queue up an async operation since the package deletion may take a little while.
         mHandler.post(new Runnable() {
@@ -16292,18 +17093,22 @@
                 mHandler.removeCallbacks(this);
                 int returnCode;
                 if (!deleteAllUsers) {
-                    returnCode = deletePackageX(packageName, userId, deleteFlags);
+                    returnCode = deletePackageX(internalPackageName, versionCode,
+                            userId, deleteFlags);
                 } else {
-                    int[] blockUninstallUserIds = getBlockUninstallForUsers(packageName, users);
+                    int[] blockUninstallUserIds = getBlockUninstallForUsers(
+                            internalPackageName, users);
                     // If nobody is blocking uninstall, proceed with delete for all users
                     if (ArrayUtils.isEmpty(blockUninstallUserIds)) {
-                        returnCode = deletePackageX(packageName, userId, deleteFlags);
+                        returnCode = deletePackageX(internalPackageName, versionCode,
+                                userId, deleteFlags);
                     } else {
                         // Otherwise uninstall individually for users with blockUninstalls=false
                         final int userFlags = deleteFlags & ~PackageManager.DELETE_ALL_USERS;
                         for (int userId : users) {
                             if (!ArrayUtils.contains(blockUninstallUserIds, userId)) {
-                                returnCode = deletePackageX(packageName, userId, userFlags);
+                                returnCode = deletePackageX(internalPackageName, versionCode,
+                                        userId, userFlags);
                                 if (returnCode != PackageManager.DELETE_SUCCEEDED) {
                                     Slog.w(TAG, "Package delete failed for user " + userId
                                             + ", returnCode " + returnCode);
@@ -16324,6 +17129,80 @@
         });
     }
 
+    private String resolveExternalPackageNameLPr(PackageParser.Package pkg) {
+        if (pkg.staticSharedLibName != null) {
+            return pkg.manifestPackageName;
+        }
+        return pkg.packageName;
+    }
+
+    private String resolveInternalPackageNameLPr(String packageName, int versionCode) {
+        // Handle renamed packages
+        String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
+        packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
+
+        // Is this a static library?
+        SparseArray<SharedLibraryEntry> versionedLib =
+                mStaticLibsByDeclaringPackage.get(packageName);
+        if (versionedLib == null || versionedLib.size() <= 0) {
+            return packageName;
+        }
+
+        // Figure out which lib versions the caller can see
+        SparseIntArray versionsCallerCanSee = null;
+        final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+        if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
+                && callingAppId != Process.ROOT_UID) {
+            versionsCallerCanSee = new SparseIntArray();
+            String libName = versionedLib.valueAt(0).info.getName();
+            String[] uidPackages = getPackagesForUid(Binder.getCallingUid());
+            if (uidPackages != null) {
+                for (String uidPackage : uidPackages) {
+                    PackageSetting ps = mSettings.getPackageLPr(uidPackage);
+                    final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
+                    if (libIdx >= 0) {
+                        final int libVersion = ps.usesStaticLibrariesVersions[libIdx];
+                        versionsCallerCanSee.append(libVersion, libVersion);
+                    }
+                }
+            }
+        }
+
+        // Caller can see nothing - done
+        if (versionsCallerCanSee != null && versionsCallerCanSee.size() <= 0) {
+            return packageName;
+        }
+
+        // Find the version the caller can see and the app version code
+        SharedLibraryEntry highestVersion = null;
+        final int versionCount = versionedLib.size();
+        for (int i = 0; i < versionCount; i++) {
+            SharedLibraryEntry libEntry = versionedLib.valueAt(i);
+            if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
+                    libEntry.info.getVersion()) < 0) {
+                continue;
+            }
+            // TODO: We will change version code to long, so in the new API it is long
+            final int libVersionCode = (int) libEntry.info.getDeclaringPackage().getVersionCode();
+            if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
+                if (libVersionCode == versionCode) {
+                    return libEntry.apk;
+                }
+            } else if (highestVersion == null) {
+                highestVersion = libEntry;
+            } else if (libVersionCode  > highestVersion.info
+                    .getDeclaringPackage().getVersionCode()) {
+                highestVersion = libEntry;
+            }
+        }
+
+        if (highestVersion != null) {
+            return highestVersion.apk;
+        }
+
+        return packageName;
+    }
+
     private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
         if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID
               || callingUid == Process.SYSTEM_UID) {
@@ -16422,7 +17301,7 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-    private int deletePackageX(String packageName, int userId, int deleteFlags) {
+    private int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) {
         final PackageRemovedInfo info = new PackageRemovedInfo();
         final boolean res;
 
@@ -16445,6 +17324,32 @@
                 Slog.w(TAG, "Not removing non-existent package " + packageName);
                 return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
             }
+
+            if (versionCode != PackageManager.VERSION_CODE_HIGHEST
+                    && uninstalledPs.versionCode != versionCode) {
+                Slog.w(TAG, "Not removing package " + packageName + " with versionCode "
+                        + uninstalledPs.versionCode + " != " + versionCode);
+                return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
+            }
+
+            // Static shared libs can be declared by any package, so let us not
+            // allow removing a package if it provides a lib others depend on.
+            PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg != null && pkg.staticSharedLibName != null) {
+                SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(pkg.staticSharedLibName,
+                        pkg.staticSharedLibVersion);
+                if (libEntry != null) {
+                    List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
+                            libEntry.info, 0, userId);
+                    if (!ArrayUtils.isEmpty(libClientPackages)) {
+                        Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
+                                + " hosting lib " + libEntry.info.getName() + " version "
+                                + libEntry.info.getVersion()  + " used by " + libClientPackages);
+                        return PackageManager.DELETE_FAILED_USED_SHARED_LIBRARY;
+                    }
+                }
+            }
+
             allUsers = sUserManager.getUserIds();
             info.origUsers = uninstalledPs.queryInstalledUsers(allUsers, true);
         }
@@ -16503,6 +17408,7 @@
         boolean isUpdate;
         boolean dataRemoved;
         boolean removedForAllUsers;
+        boolean isStaticSharedLib;
         // Clean up resources deleted packages.
         InstallArgs args = null;
         ArrayMap<String, PackageRemovedInfo> removedChildPackages;
@@ -16554,6 +17460,12 @@
         }
 
         private void sendPackageRemovedBroadcastInternal(boolean killApp) {
+            // Don't send static shared library removal broadcasts as these
+            // libs are visible only the the apps that depend on them an one
+            // cannot remove the library if it has a dependency.
+            if (isStaticSharedLib) {
+                return;
+            }
             Bundle extras = new Bundle(2);
             extras.putInt(Intent.EXTRA_UID, removedAppId >= 0  ? removedAppId : uid);
             extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
@@ -16567,7 +17479,8 @@
                         extras, 0, null, null, removedUsers);
                 if (dataRemoved && !isRemovedPackageSystemUpdate) {
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
-                            removedPackage, extras, 0, null, null, removedUsers);
+                            removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+                            null, null, removedUsers);
                 }
             }
             if (removedAppId >= 0) {
@@ -16596,6 +17509,8 @@
             deletedPs = mSettings.mPackages.get(packageName);
             if (outInfo != null) {
                 outInfo.removedPackage = packageName;
+                outInfo.isStaticSharedLib = deletedPkg != null
+                        && deletedPkg.staticSharedLibName != null;
                 outInfo.removedUsers = deletedPs != null
                         ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true)
                         : null;
@@ -16623,15 +17538,18 @@
             schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
         }
 
+        int removedAppId = -1;
+
         // writer
         synchronized (mPackages) {
             if (deletedPs != null) {
                 if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
                     clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
                     clearDefaultBrowserIfNeeded(packageName);
+                    mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
+                    removedAppId = mSettings.removePackageLPw(packageName);
                     if (outInfo != null) {
-                        mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
-                        outInfo.removedAppId = mSettings.removePackageLPw(packageName);
+                        outInfo.removedAppId = removedAppId;
                     }
                     updatePermissionsLPw(deletedPs.name, null, 0);
                     if (deletedPs.sharedUser != null) {
@@ -16681,10 +17599,10 @@
                 mSettings.writeLPr();
             }
         }
-        if (outInfo != null) {
+        if (removedAppId != -1) {
             // A user ID was deleted here. Go through all users and remove it
             // from KeyStore.
-            removeKeystoreDataIfNeeded(UserHandle.USER_ALL, outInfo.removedAppId);
+            removeKeystoreDataIfNeeded(UserHandle.USER_ALL, removedAppId);
         }
     }
 
@@ -16800,6 +17718,7 @@
                     + e.getMessage());
             return false;
         }
+
         try {
             // update shared libraries for the newly re-installed system package
             updateSharedLibrariesLPr(newPkg, null);
@@ -16918,6 +17837,15 @@
                 Log.i(TAG, "Package doesn't exist in set block uninstall " + packageName);
                 return false;
             }
+            // Cannot block uninstall of static shared libs as they are
+            // considered a part of the using app (emulating static linking).
+            // Also static libs are installed always on internal storage.
+            PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg != null && pkg.staticSharedLibName != null) {
+                Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+                        + " providing static shared library: " + pkg.staticSharedLibName);
+                return false;
+            }
             if (!ps.getInstalled(userId)) {
                 // Can't block uninstall for an app that is not installed or enabled.
                 Log.i(TAG, "Package not installed in set block uninstall " + packageName);
@@ -16979,7 +17907,6 @@
         if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
 
         PackageSetting ps;
-
         synchronized (mPackages) {
             ps = mSettings.mPackages.get(packageName);
             if (ps == null) {
@@ -17172,6 +18099,7 @@
 
         if (outInfo != null) {
             outInfo.removedPackage = ps.name;
+            outInfo.isStaticSharedLib = pkg != null && pkg.staticSharedLibName != null;
             outInfo.removedAppId = ps.appId;
             outInfo.removedUsers = userIds;
         }
@@ -19192,6 +20120,7 @@
                     pw.println("Error: check-permission missing package argument");
                     return;
                 }
+
                 String pkg = args[opti];
                 opti++;
                 int user = UserHandle.getUserId(Binder.getCallingUid());
@@ -19204,6 +20133,10 @@
                         return;
                     }
                 }
+
+                // Normalize package name to handle renamed packages and static libs
+                pkg = resolveInternalPackageNameLPr(pkg, PackageManager.VERSION_CODE_HIGHEST);
+
                 pw.println(checkPermission(perm, pkg, user));
                 return;
             } else if ("l".equals(cmd) || "libraries".equals(cmd)) {
@@ -19356,41 +20289,41 @@
                 boolean printedHeader = false;
                 final Iterator<String> it = mSharedLibraries.keySet().iterator();
                 while (it.hasNext()) {
-                    String name = it.next();
-                    SharedLibraryEntry ent = mSharedLibraries.get(name);
-                    if (!checkin) {
-                        if (!printedHeader) {
-                            if (dumpState.onTitlePrinted())
-                                pw.println();
-                            pw.println("Libraries:");
-                            printedHeader = true;
-                        }
-                        pw.print("  ");
-                    } else {
-                        pw.print("lib,");
+                    String libName = it.next();
+                    SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+                    if (versionedLib == null) {
+                        continue;
                     }
-                    pw.print(name);
-                    if (!checkin) {
-                        pw.print(" -> ");
-                    }
-                    if (ent.path != null) {
+                    final int versionCount = versionedLib.size();
+                    for (int i = 0; i < versionCount; i++) {
+                        SharedLibraryEntry libEntry = versionedLib.valueAt(i);
                         if (!checkin) {
-                            pw.print("(jar) ");
-                            pw.print(ent.path);
+                            if (!printedHeader) {
+                                if (dumpState.onTitlePrinted())
+                                    pw.println();
+                                pw.println("Libraries:");
+                                printedHeader = true;
+                            }
+                            pw.print("  ");
                         } else {
-                            pw.print(",jar,");
-                            pw.print(ent.path);
+                            pw.print("lib,");
                         }
-                    } else {
+                        pw.print(libEntry.info.getName());
+                        if (libEntry.info.isStatic()) {
+                            pw.print(" version=" + libEntry.info.getVersion());
+                        }
                         if (!checkin) {
-                            pw.print("(apk) ");
-                            pw.print(ent.apk);
-                        } else {
-                            pw.print(",apk,");
-                            pw.print(ent.apk);
+                            pw.print(" -> ");
                         }
+                        if (libEntry.path != null) {
+                            pw.print(" (jar) ");
+                            pw.print(libEntry.path);
+                        } else {
+                            pw.print(" (apk) ");
+                            pw.print(libEntry.apk);
+                        }
+                        pw.println();
                     }
-                    pw.println();
                 }
             }
 
@@ -19521,7 +20454,8 @@
                             count = 0;
                             for (PackageSetting ps : allPackageSettings) {
                                 final long status = ps.getDomainVerificationStatusForUser(userId);
-                                if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                                if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+                                        && !DEBUG_DOMAIN_VERIFICATION) {
                                     continue;
                                 }
                                 pw.println(prefix + "Package: " + ps.name);
@@ -20503,14 +21437,29 @@
         }
     }
 
+    private List<String> collectAbsoluteCodePaths() {
+        synchronized (mPackages) {
+            List<String> codePaths = new ArrayList<>();
+            final int packageCount = mSettings.mPackages.size();
+            for (int i = 0; i < packageCount; i++) {
+                final PackageSetting ps = mSettings.mPackages.valueAt(i);
+                codePaths.add(ps.codePath.getAbsolutePath());
+            }
+            return codePaths;
+        }
+    }
+
     /**
      * Examine all apps present on given mounted volume, and destroy apps that
      * aren't expected, either due to uninstallation or reinstallation on
      * another volume.
      */
     private void reconcileApps(String volumeUuid) {
-        final File[] files = FileUtils
-                .listFilesOrEmpty(Environment.getDataAppDirectory(volumeUuid));
+        List<String> absoluteCodePaths = collectAbsoluteCodePaths();
+        List<File> filesToDelete = null;
+
+        final File[] files = FileUtils.listFilesOrEmpty(
+                Environment.getDataAppDirectory(volumeUuid));
         for (File file : files) {
             final boolean isPackage = (isApkFile(file) || file.isDirectory())
                     && !PackageInstallerService.isStageName(file.getName());
@@ -20519,15 +21468,33 @@
                 continue;
             }
 
-            try {
-                final PackageLite pkg = PackageParser.parsePackageLite(file,
-                        PackageParser.PARSE_MUST_BE_APK);
-                assertPackageKnown(volumeUuid, pkg.packageName);
+            String absolutePath = file.getAbsolutePath();
 
-            } catch (PackageParserException | PackageManagerException e) {
-                logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
+            boolean pathValid = false;
+            final int absoluteCodePathCount = absoluteCodePaths.size();
+            for (int i = 0; i < absoluteCodePathCount; i++) {
+                String absoluteCodePath = absoluteCodePaths.get(i);
+                if (absolutePath.startsWith(absoluteCodePath)) {
+                    pathValid = true;
+                    break;
+                }
+            }
+
+            if (!pathValid) {
+                if (filesToDelete == null) {
+                    filesToDelete = new ArrayList<>();
+                }
+                filesToDelete.add(file);
+            }
+        }
+
+        if (filesToDelete != null) {
+            final int fileToDeleteCount = filesToDelete.size();
+            for (int i = 0; i < fileToDeleteCount; i++) {
+                File fileToDelete = filesToDelete.get(i);
+                logCriticalInfo(Log.WARN, "Destroying orphaned" + fileToDelete);
                 synchronized (mInstallLock) {
-                    removeCodePathLI(file);
+                    removeCodePathLI(fileToDelete);
                 }
             }
         }
@@ -20982,6 +21949,14 @@
                         "Cannot move system application");
             }
 
+            final boolean isInternalStorage = VolumeInfo.ID_PRIVATE_INTERNAL.equals(volumeUuid);
+            final boolean allow3rdPartyOnInternal = mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_allow3rdPartyAppOnInternal);
+            if (isInternalStorage && !allow3rdPartyOnInternal) {
+                throw new PackageManagerException(MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL,
+                        "3rd party apps are not allowed on internal storage");
+            }
+
             if (pkg.applicationInfo.isExternalAsec()) {
                 currentAsec = true;
                 currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
@@ -21297,7 +22272,8 @@
                 }
                 mHandler.post(new Runnable() {
                     public void run() {
-                        deletePackageX(packageName, userHandle, 0);
+                        deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+                                userHandle, 0);
                     } //end run
                 });
             }
@@ -21507,7 +22483,8 @@
             // after this method returns.
             mHandler.post(new Runnable() {
                 public void run() {
-                    deletePackageX(packageName, 0, PackageManager.DELETE_ALL_USERS);
+                    deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+                            0, PackageManager.DELETE_ALL_USERS);
                 }
             });
         }
@@ -21864,9 +22841,24 @@
                     responseObj, origIntent, resolvedType, launchIntent, callingPackage, userId);
         }
 
+        @Override
+        public void grantEphemeralAccess(int userId, Intent intent,
+                int targetAppId, int ephemeralAppId) {
+            synchronized (mPackages) {
+                mEphemeralApplicationRegistry.grantEphemeralAccessLPw(userId, intent,
+                        targetAppId, ephemeralAppId);
+            }
+        }
+
         public String getSetupWizardPackageName() {
             return mSetupWizardPackage;
         }
+
+        public void setExternalSourcesPolicy(ExternalSourcesPolicy policy) {
+            if (policy != null) {
+                mExternalSourcesPolicy = policy;
+            }
+        }
     }
 
     @Override
@@ -21956,4 +22948,38 @@
         }
         return PackageManager.INSTALL_REASON_UNKNOWN;
     }
+
+    @Override
+    public boolean canRequestPackageInstalls(String packageName, int userId) {
+        int callingUid = Binder.getCallingUid();
+        int uid = getPackageUid(packageName, 0, userId);
+        if (callingUid != uid && callingUid != Process.ROOT_UID
+                && callingUid != Process.SYSTEM_UID) {
+            throw new SecurityException(
+                    "Caller uid " + callingUid + " does not own package " + packageName);
+        }
+        ApplicationInfo info = getApplicationInfo(packageName, 0, userId);
+        if (info == null) {
+            return false;
+        }
+        if (info.targetSdkVersion < Build.VERSION_CODES.O) {
+            throw new UnsupportedOperationException(
+                    "Operation only supported on apps targeting Android O or higher");
+        }
+        String appOpPermission = Manifest.permission.REQUEST_INSTALL_PACKAGES;
+        String[] packagesDeclaringPermission = getAppOpPermissionPackages(appOpPermission);
+        if (!ArrayUtils.contains(packagesDeclaringPermission, packageName)) {
+            throw new SecurityException("Need to declare " + appOpPermission + " to call this api");
+        }
+        if (sUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)) {
+            return false;
+        }
+        if (mExternalSourcesPolicy != null) {
+            int isTrusted = mExternalSourcesPolicy.getPackageTrustedToInstallApps(packageName, uid);
+            if (isTrusted != PackageManagerInternal.ExternalSourcesPolicy.USER_DEFAULT) {
+                return isTrusted == PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
+            }
+        }
+        return checkUidPermission(appOpPermission, uid) == PERMISSION_GRANTED;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2751742..2f8d749 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -40,6 +40,7 @@
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.ResolveInfo;
+import android.content.pm.VersionedPackage;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.net.Uri;
@@ -161,7 +162,7 @@
             if (file.isFile()) {
                 try {
                     ApkLite baseApk = PackageParser.parseApkLite(file, 0);
-                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null);
+                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null);
                     params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
                             pkgLite, false, params.sessionParams.abiOverride));
                 } catch (PackageParserException | IOException e) {
@@ -586,6 +587,7 @@
         boolean listSystem = false, listThirdParty = false;
         boolean listInstaller = false;
         boolean showUid = false;
+        boolean showVersionCode = false;
         int uid = -1;
         int userId = UserHandle.USER_SYSTEM;
         try {
@@ -619,6 +621,9 @@
                     case "-3":
                         listThirdParty = true;
                         break;
+                    case "--show-versioncode":
+                        showVersionCode = true;
+                        break;
                     case "--user":
                         userId = UserHandle.parseUserArg(getNextArgRequired());
                         break;
@@ -664,6 +669,10 @@
                     pw.print("=");
                 }
                 pw.print(info.packageName);
+                if (showVersionCode) {
+                    pw.print(" versionCode:");
+                    pw.print(info.applicationInfo.versionCode);
+                }
                 if (listInstaller) {
                     pw.print("  installer=");
                     pw.print(mInterface.getInstallerPackageName(info.packageName));
@@ -770,6 +779,7 @@
         final PrintWriter pw = getOutPrintWriter();
         int flags = 0;
         int userId = UserHandle.USER_ALL;
+        int versionCode = PackageManager.VERSION_CODE_HIGHEST;
 
         String opt;
         while ((opt = getNextOption()) != null) {
@@ -780,6 +790,9 @@
                 case "--user":
                     userId = UserHandle.parseUserArg(getNextArgRequired());
                     break;
+                case "--versionCode":
+                    versionCode = Integer.parseInt(getNextArgRequired());
+                    break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
                     return 1;
@@ -819,7 +832,8 @@
         }
 
         final LocalIntentReceiver receiver = new LocalIntentReceiver();
-        mInterface.getPackageInstaller().uninstall(packageName, null /*callerPackageName*/, flags,
+        mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
+                versionCode), null /*callerPackageName*/, flags,
                 receiver.getIntentSender(), userId);
 
         final Intent result = receiver.getResult();
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 80a398a..5f348ab 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -46,10 +46,12 @@
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
-            List<String> childPackageNames, int sharedUserId) {
+            List<String> childPackageNames, int sharedUserId, String[] usesStaticLibraries,
+            int[] usesStaticLibrariesVersions) {
         super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
-                pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames);
+                pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames,
+                usesStaticLibraries, usesStaticLibrariesVersions);
         this.sharedUserId = sharedUserId;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index b332fa5..b63edfd 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -32,6 +32,7 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 
@@ -68,6 +69,9 @@
     File resourcePath;
     String resourcePathString;
 
+    String[] usesStaticLibraries;
+    int[] usesStaticLibrariesVersions;
+
     /**
      * The path under which native libraries have been unpacked. This path is
      * always derived at runtime, and is only stored here for cleanup when a
@@ -139,13 +143,16 @@
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             int pVersionCode, int pkgFlags, int pkgPrivateFlags,
-            String parentPackageName, List<String> childPackageNames) {
+            String parentPackageName, List<String> childPackageNames,
+            String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) {
         super(pkgFlags, pkgPrivateFlags);
         this.name = name;
         this.realName = realName;
         this.parentPackageName = parentPackageName;
         this.childPackageNames = (childPackageNames != null)
                 ? new ArrayList<>(childPackageNames) : null;
+        this.usesStaticLibraries = usesStaticLibraries;
+        this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
         init(codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString,
                 secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode);
     }
@@ -250,6 +257,12 @@
         versionCode = orig.versionCode;
         volumeUuid = orig.volumeUuid;
         categoryHint = orig.categoryHint;
+        usesStaticLibraries = orig.usesStaticLibraries != null
+                ? Arrays.copyOf(orig.usesStaticLibraries,
+                        orig.usesStaticLibraries.length) : null;
+        usesStaticLibrariesVersions = orig.usesStaticLibrariesVersions != null
+                ? Arrays.copyOf(orig.usesStaticLibrariesVersions,
+                       orig.usesStaticLibrariesVersions.length) : null;
     }
 
     private PackageUserState modifyUserState(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 8761a6d..281e445 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -183,6 +183,7 @@
     private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
     private static final String TAG_PERMISSIONS = "perms";
     private static final String TAG_CHILD_PACKAGE = "child-package";
+    private static final String TAG_USES_STATIC_LIB = "uses-static-lib";
 
     private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
             "persistent-preferred-activities";
@@ -201,6 +202,7 @@
     private static final String ATTR_CODE = "code";
     private static final String ATTR_GRANTED = "granted";
     private static final String ATTR_FLAGS = "flags";
+    private static final String ATTR_VERSION = "version";
 
     private static final String ATTR_CE_DATA_INODE = "ceDataInode";
     private static final String ATTR_INSTALLED = "inst";
@@ -561,7 +563,8 @@
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
                 p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
-                p.parentPackageName, p.childPackageNames);
+                p.parentPackageName, p.childPackageNames, p.usesStaticLibraries,
+                p.usesStaticLibrariesVersions);
         mDisabledSysPackages.remove(name);
         return ret;
     }
@@ -578,7 +581,8 @@
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, int vc, int
             pkgFlags, int pkgPrivateFlags, String parentPackageName,
-            List<String> childPackageNames) {
+            List<String> childPackageNames, String[] usesStaticLibraries,
+            int[] usesStaticLibraryNames) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
             if (p.appId == uid) {
@@ -591,7 +595,7 @@
         p = new PackageSetting(name, realName, codePath, resourcePath,
                 legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
                 cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
-                childPackageNames, 0 /*userId*/);
+                childPackageNames, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);
         p.appId = uid;
         if (addUserIdLPw(uid, p, name)) {
             mPackages.put(name, p);
@@ -678,7 +682,8 @@
             File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
             String secondaryCpuAbi, int versionCode, int pkgFlags, int pkgPrivateFlags,
             UserHandle installUser, boolean allowInstall, String parentPkgName,
-            List<String> childPkgNames, UserManagerService userManager) {
+            List<String> childPkgNames, UserManagerService userManager,
+            String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) {
         final PackageSetting pkgSetting;
         if (originalPkg != null) {
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
@@ -699,13 +704,16 @@
             // overwrite the signatures in the original package setting.
             pkgSetting.signatures = new PackageSignatures();
             pkgSetting.versionCode = versionCode;
+            pkgSetting.usesStaticLibraries = usesStaticLibraries;
+            pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
             // Update new package state.
             pkgSetting.setTimeStamp(codePath.lastModified());
         } else {
             pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath,
                     legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
                     null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
-                    parentPkgName, childPkgNames, 0 /*sharedUserId*/);
+                    parentPkgName, childPkgNames, 0 /*sharedUserId*/, usesStaticLibraries,
+                    usesStaticLibrariesVersions);
             pkgSetting.setTimeStamp(codePath.lastModified());
             pkgSetting.sharedUser = sharedUser;
             // If this is not a system app, it starts out stopped.
@@ -782,7 +790,8 @@
             @NonNull File codePath, @Nullable String legacyNativeLibraryPath,
             @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi,
             int pkgFlags, int pkgPrivateFlags, @Nullable List<String> childPkgNames,
-            @NonNull UserManagerService userManager) throws PackageManagerException {
+            @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries,
+            @Nullable int[] usesStaticLibrariesVersions) throws PackageManagerException {
         final String pkgName = pkgSetting.name;
         if (pkgSetting.sharedUser != sharedUser) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -844,6 +853,14 @@
         if (childPkgNames != null) {
             pkgSetting.childPackageNames = new ArrayList<>(childPkgNames);
         }
+        if (usesStaticLibraries != null) {
+            pkgSetting.usesStaticLibraries = Arrays.copyOf(usesStaticLibraries,
+                    usesStaticLibraries.length);
+        }
+        if (usesStaticLibrariesVersions != null) {
+            pkgSetting.usesStaticLibrariesVersions = Arrays.copyOf(usesStaticLibrariesVersions,
+                    usesStaticLibrariesVersions.length);
+        }
     }
 
     /**
@@ -949,6 +966,16 @@
         if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
             p.sharedUser.signatures.assignSignatures(pkg.mSignatures);
         }
+        // Update static shared library dependencies if needed
+        if (pkg.usesStaticLibraries != null && pkg.usesStaticLibrariesVersions != null
+                && pkg.usesStaticLibraries.size() == pkg.usesStaticLibrariesVersions.length) {
+            String[] usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
+            pkg.usesStaticLibraries.toArray(usesStaticLibraries);
+            p.usesStaticLibrariesVersions = pkg.usesStaticLibrariesVersions;
+        } else {
+            pkg.usesStaticLibraries = null;
+            p.usesStaticLibrariesVersions = null;
+        }
         addPackageSettingLPw(p, p.sharedUser);
     }
 
@@ -2163,6 +2190,53 @@
         }
     }
 
+    void readUsesStaticLibLPw(XmlPullParser parser, PackageSetting outPs)
+            throws IOException, XmlPullParserException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            String libName = parser.getAttributeValue(null, ATTR_NAME);
+            String libVersionStr = parser.getAttributeValue(null, ATTR_VERSION);
+
+            int libVersion = -1;
+            try {
+                libVersion = Integer.parseInt(libVersionStr);
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+
+            if (libName != null && libVersion >= 0) {
+                outPs.usesStaticLibraries = ArrayUtils.appendElement(String.class,
+                        outPs.usesStaticLibraries, libName);
+                outPs.usesStaticLibrariesVersions = ArrayUtils.appendInt(
+                        outPs.usesStaticLibrariesVersions, libVersion);
+            }
+
+            XmlUtils.skipCurrentTag(parser);
+        }
+    }
+
+    void writeUsesStaticLibLPw(XmlSerializer serializer, String[] usesStaticLibraries,
+            int[] usesStaticLibraryVersions) throws IOException {
+        if (ArrayUtils.isEmpty(usesStaticLibraries) || ArrayUtils.isEmpty(usesStaticLibraryVersions)
+                || usesStaticLibraries.length != usesStaticLibraryVersions.length) {
+            return;
+        }
+        final int libCount = usesStaticLibraries.length;
+        for (int i = 0; i < libCount; i++) {
+            final String libName = usesStaticLibraries[i];
+            final int libVersion = usesStaticLibraryVersions[i];
+            serializer.startTag(null, TAG_USES_STATIC_LIB);
+            serializer.attribute(null, ATTR_NAME, libName);
+            serializer.attribute(null, ATTR_VERSION, Integer.toString(libVersion));
+            serializer.endTag(null, TAG_USES_STATIC_LIB);
+        }
+    }
+
     // Note: assumed "stopped" field is already cleared in all packages.
     // Legacy reader, used to read in the old file format after an upgrade. Not used after that.
     void readStoppedLPw() {
@@ -2622,6 +2696,8 @@
 
         writeChildPackagesLPw(serializer, pkg.childPackageNames);
 
+        writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+
         // If this is a shared user, the permissions will be written there.
         if (pkg.sharedUser == null) {
             writePermissionsLPr(serializer, pkg.getPermissionsState()
@@ -2692,6 +2768,8 @@
 
         writeChildPackagesLPw(serializer, pkg.childPackageNames);
 
+        writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+
         pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
 
         writePermissionsLPr(serializer, pkg.getPermissionsState()
@@ -3465,7 +3543,7 @@
         PackageSetting ps = new PackageSetting(name, realName, codePathFile,
                 new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr,
                 secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags,
-                parentPackageName, null /*childPackageNames*/, 0 /*sharedUserId*/);
+                parentPackageName, null /*childPackageNames*/, 0 /*sharedUserId*/, null, null);
         String timeStampStr = parser.getAttributeValue(null, "ft");
         if (timeStampStr != null) {
             try {
@@ -3520,6 +3598,8 @@
                     ps.childPackageNames = new ArrayList<>();
                 }
                 ps.childPackageNames.add(childPackageName);
+            } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
+                readUsesStaticLibLPw(parser, ps);
             } else {
                 PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Unknown element under <updated-package>: " + parser.getName());
@@ -3705,7 +3785,8 @@
                 packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
                         new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
                         secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
-                        pkgPrivateFlags, parentPackageName, null /*childPackageNames*/);
+                        pkgPrivateFlags, parentPackageName, null /*childPackageNames*/,
+                        null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
                 if (PackageManagerService.DEBUG_SETTINGS)
                     Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
                             + userId + " pkg=" + packageSetting);
@@ -3724,7 +3805,8 @@
                             codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
                             primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                             versionCode, pkgFlags, pkgPrivateFlags, parentPackageName,
-                            null /*childPackageNames*/, sharedUserId);
+                            null /*childPackageNames*/, sharedUserId,
+                            null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
                     packageSetting.setTimeStamp(timeStamp);
                     packageSetting.firstInstallTime = firstInstallTime;
                     packageSetting.lastUpdateTime = lastUpdateTime;
@@ -4294,6 +4376,7 @@
         ApplicationInfo.PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_EXPLICITLY_SET, "RESIZEABLE_ACTIVITIES_EXPLICITLY_SET",
         ApplicationInfo.PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_VIA_SDK_VERSION, "RESIZEABLE_ACTIVITIES_VIA_SDK_VERSION",
         ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND",
+        ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY, "STATIC_SHARED_LIBRARY",
     };
 
     void dumpVersionLPr(IndentingPrintWriter pw) {
@@ -4486,23 +4569,39 @@
             }
             pw.println("]");
             if (ps.pkg.libraryNames != null && ps.pkg.libraryNames.size() > 0) {
-                pw.print(prefix); pw.println("  libraries:");
-                for (int i=0; i<ps.pkg.libraryNames.size(); i++) {
-                    pw.print(prefix); pw.print("    "); pw.println(ps.pkg.libraryNames.get(i));
+                pw.print(prefix); pw.println("  dynamic libraries:");
+                for (int i = 0; i<ps.pkg.libraryNames.size(); i++) {
+                    pw.print(prefix); pw.print("    ");
+                            pw.println(ps.pkg.libraryNames.get(i));
                 }
             }
+            if (ps.pkg.staticSharedLibName != null) {
+                pw.print(prefix); pw.println("  static library:");
+                pw.print(prefix); pw.print("    ");
+                pw.print("name:"); pw.print(ps.pkg.staticSharedLibName);
+                pw.print(" version:"); pw.println(ps.pkg.staticSharedLibVersion);
+            }
             if (ps.pkg.usesLibraries != null && ps.pkg.usesLibraries.size() > 0) {
                 pw.print(prefix); pw.println("  usesLibraries:");
                 for (int i=0; i<ps.pkg.usesLibraries.size(); i++) {
                     pw.print(prefix); pw.print("    "); pw.println(ps.pkg.usesLibraries.get(i));
                 }
             }
+            if (ps.pkg.usesStaticLibraries != null
+                    && ps.pkg.usesStaticLibraries.size() > 0) {
+                pw.print(prefix); pw.println("  usesStaticLibraries:");
+                for (int i=0; i<ps.pkg.usesStaticLibraries.size(); i++) {
+                    pw.print(prefix); pw.print("    ");
+                    pw.print(ps.pkg.usesStaticLibraries.get(i)); pw.print(" version:");
+                            pw.println(ps.pkg.usesStaticLibrariesVersions[i]);
+                }
+            }
             if (ps.pkg.usesOptionalLibraries != null
                     && ps.pkg.usesOptionalLibraries.size() > 0) {
                 pw.print(prefix); pw.println("  usesOptionalLibraries:");
                 for (int i=0; i<ps.pkg.usesOptionalLibraries.size(); i++) {
                     pw.print(prefix); pw.print("    ");
-                        pw.println(ps.pkg.usesOptionalLibraries.get(i));
+                    pw.println(ps.pkg.usesOptionalLibraries.get(i));
                 }
             }
             if (ps.pkg.usesLibraryFiles != null
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index c8ddf0a..6e96726 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.pm;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -24,6 +25,7 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.pm.ShortcutInfo;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.Log;
@@ -50,18 +52,31 @@
     private static class PinItemRequestInner extends IPinItemRequest.Stub {
         protected final ShortcutRequestPinProcessor mProcessor;
         private final IntentSender mResultIntent;
+        private final int mLauncherUid;
 
         @GuardedBy("this")
         private boolean mAccepted;
 
         private PinItemRequestInner(ShortcutRequestPinProcessor processor,
-                IntentSender resultIntent) {
+                IntentSender resultIntent, int launcherUid) {
             mProcessor = processor;
             mResultIntent = resultIntent;
+            mLauncherUid = launcherUid;
+        }
+
+        /**
+         * Returns true if the caller is same as the default launcher app when this request
+         * object was created.
+         */
+        private boolean isCallerValid() {
+            return mProcessor.isCallerUid(mLauncherUid);
         }
 
         @Override
         public boolean isValid() {
+            if (!isCallerValid()) {
+                return false;
+            }
             // TODO When an app calls requestPinShortcut(), all pending requests should be
             // invalidated.
             synchronized (this) {
@@ -76,6 +91,9 @@
         public boolean accept(Bundle options) {
             // Make sure the options are unparcellable by the FW. (e.g. not containing unknown
             // classes.)
+            if (!isCallerValid()) {
+                throw new SecurityException("Calling uid mismatch");
+            }
             Intent extras = null;
             if (options != null) {
                 try {
@@ -126,8 +144,8 @@
         private PinShortcutRequestInner(ShortcutRequestPinProcessor processor,
                 ShortcutInfo shortcutOriginal, ShortcutInfo shortcutForLauncher,
                 IntentSender resultIntent,
-                String launcherPackage, int launcherUserId, boolean preExisting) {
-            super(processor, resultIntent);
+                String launcherPackage, int launcherUserId, int launcherUid, boolean preExisting) {
+            super(processor, resultIntent, launcherUid);
             this.shortcutOriginal = shortcutOriginal;
             this.shortcutForLauncher = shortcutForLauncher;
             this.launcherPackage = launcherPackage;
@@ -157,6 +175,7 @@
     /**
      * Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)} and
      * {@link android.appwidget.AppWidgetManager#requestPinAppWidget}.
+     * In this flow the PinItemRequest is delivered directly to the default launcher app.
      * One of {@param inShortcut} and {@param inAppWidget} is always non-null and the other is
      * always null.
      */
@@ -186,21 +205,44 @@
         if (inShortcut != null) {
             request = requestPinShortcutLocked(inShortcut, resultIntent, confirmActivity);
         } else {
-            request = new PinItemRequest(inAppWidget, new PinItemRequestInner(this, resultIntent));
-        }
-
-        if (request == null) {
-            sendResultIntent(resultIntent, null);
-            return true;
+            int launcherUid = mService.injectGetPackageUid(
+                    confirmActivity.first.getPackageName(), launcherUserId);
+            request = new PinItemRequest(inAppWidget,
+                    new PinItemRequestInner(this, resultIntent, launcherUid));
         }
         return startRequestConfirmActivity(confirmActivity.first, launcherUserId, request);
     }
 
     /**
+     * Handle {@link android.content.pm.ShortcutManager#createShortcutResultIntent(ShortcutInfo)}.
+     * In this flow the PinItemRequest is delivered to the caller app. Its the app's responsibility
+     * to send it to the Launcher app (via {@link android.app.Activity#setResult(int, Intent)}).
+     */
+    public Intent createShortcutResultIntent(@NonNull ShortcutInfo inShortcut, int userId) {
+        // Find the default launcher activity
+        final int launcherUserId = mService.getParentOrSelfUserId(userId);
+        final ComponentName defaultLauncher = mService.getDefaultLauncher(launcherUserId);
+        if (defaultLauncher == null) {
+            Log.e(TAG, "Default launcher not found.");
+            return null;
+        }
+
+        // Make sure the launcher user is unlocked. (it's always the parent profile, so should
+        // really be unlocked here though.)
+        mService.throwIfUserLockedL(launcherUserId);
+
+        // Next, validate the incoming shortcut, etc.
+        final PinItemRequest request = requestPinShortcutLocked(inShortcut, null,
+                Pair.create(defaultLauncher, launcherUserId));
+        return new Intent().putExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST, request);
+    }
+
+    /**
      * Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)}.
      */
+    @NonNull
     private PinItemRequest requestPinShortcutLocked(ShortcutInfo inShortcut,
-            IntentSender resultIntent, Pair<ComponentName, Integer> confirmActivity) {
+            IntentSender resultIntentOriginal, Pair<ComponentName, Integer> confirmActivity) {
         final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
                 inShortcut.getPackage(), inShortcut.getUserId());
 
@@ -218,23 +260,30 @@
         final String launcherPackage = confirmActivity.first.getPackageName();
         final int launcherUserId = confirmActivity.second;
 
+        IntentSender resultIntentToSend = resultIntentOriginal;
+
         if (existsAlready) {
             validateExistingShortcut(existing);
 
-            // See if it's already pinned.
-            if (mService.getLauncherShortcutsLocked(
-                    launcherPackage, existing.getUserId(), launcherUserId).hasPinned(existing)) {
-                Log.i(TAG, "Launcher's already pinning shortcut " + existing.getId()
-                        + " for package " + existing.getPackage());
-                return null;
+            final boolean isAlreadyPinned = mService.getLauncherShortcutsLocked(
+                    launcherPackage, existing.getUserId(), launcherUserId).hasPinned(existing);
+            if (isAlreadyPinned) {
+                // When the shortcut is already pinned by this launcher, the request will always
+                // succeed, so just send the result at this point.
+                sendResultIntent(resultIntentOriginal, null);
+
+                // So, do not send the intent again.
+                resultIntentToSend = null;
             }
 
             // Pass a clone, not the original.
             // Note this will remove the intent and icons.
             shortcutForLauncher = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
 
-            // FLAG_PINNED is still set, if it's pinned by other launchers.
-            shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
+            if (!isAlreadyPinned) {
+                // FLAG_PINNED may still be set, if it's pinned by other launchers.
+                shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
+            }
         } else {
             // If the shortcut has no default activity, try to set the main activity.
             // But in the request-pin case, it's optional, so it's okay even if the caller
@@ -263,8 +312,10 @@
 
         // Create a request object.
         final PinShortcutRequestInner inner =
-                new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher, resultIntent,
-                        launcherPackage, launcherUserId, existsAlready);
+                new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher,
+                        resultIntentToSend, launcherPackage, launcherUserId,
+                        mService.injectGetPackageUid(launcherPackage, launcherUserId),
+                        existsAlready);
 
         return new PinItemRequest(shortcutForLauncher, inner);
     }
@@ -327,6 +378,10 @@
         mService.injectSendIntentSender(intent, extras);
     }
 
+    public boolean isCallerUid(int uid) {
+        return uid == mService.injectBinderCallingUid();
+    }
+
     /**
      * The last step of the "request pin shortcut" flow.  Called when the launcher accepted a
      * request.
@@ -347,6 +402,16 @@
                 return false;
             }
 
+            final ShortcutLauncher launcher = mService.getLauncherShortcutsLocked(
+                    launcherPackage, appUserId, launcherUserId);
+            launcher.attemptToRestoreIfNeededAndSave();
+            if (launcher.hasPinned(original)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Shortcut " + original + " already pinned.");
+                }
+                return true;
+            }
+
             final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
                     appPackageName, appUserId);
             final ShortcutInfo current = ps.findShortcutById(shortcutId);
@@ -384,9 +449,6 @@
                 Slog.d(TAG, "Pinning " + shortcutId);
             }
 
-            final ShortcutLauncher launcher = mService.getLauncherShortcutsLocked(
-                    launcherPackage, appUserId, launcherUserId);
-            launcher.attemptToRestoreIfNeededAndSave();
             launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId);
 
             if (current == null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a890526..56d679e 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1529,10 +1529,11 @@
         if (UserHandle.getUserId(callingUid) != userId) {
             throw new SecurityException("Invalid user-ID");
         }
-        if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
-            return; // Caller is valid.
+        if (injectGetPackageUid(packageName, userId) != callingUid) {
+            throw new SecurityException("Calling package name mismatch");
         }
-        throw new SecurityException("Calling package name mismatch");
+        Preconditions.checkState(!isEphemeralApp(packageName, userId),
+                "Ephemeral apps can't use ShortcutManager");
     }
 
     // Overridden in unit tests to execute r synchronously.
@@ -1854,6 +1855,25 @@
         return requestPinItem(packageName, userId, shortcut, null, resultIntent);
     }
 
+    @Override
+    public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)
+            throws RemoteException {
+        Preconditions.checkNotNull(shortcut);
+        Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
+        verifyCaller(packageName, userId);
+
+        final Intent ret;
+        synchronized (mLock) {
+            throwIfUserLockedL(userId);
+
+            // Send request to the launcher, if supported.
+            ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
+        }
+
+        verifyStates();
+        return ret;
+    }
+
     /**
      * Handles {@link #requestPinShortcut} and {@link ShortcutServiceInternal#requestPinAppWidget}.
      * After validating the caller, it passes the request to {@link #mShortcutRequestPinProcessor}.
@@ -3054,6 +3074,10 @@
         return (ai != null) && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
     }
 
+    private static boolean isEphemeralApp(@Nullable ApplicationInfo ai) {
+        return (ai != null) && ai.isEphemeralApp();
+    }
+
     private static boolean isInstalled(@Nullable PackageInfo pi) {
         return (pi != null) && isInstalled(pi.applicationInfo);
     }
@@ -3078,6 +3102,10 @@
         return getApplicationInfo(packageName, userId) != null;
     }
 
+    boolean isEphemeralApp(String packageName, int userId) {
+        return isEphemeralApp(getApplicationInfo(packageName, userId));
+    }
+
     @Nullable
     XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
         return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9fc70d6..9d2d9e5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -33,12 +33,10 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
@@ -67,6 +65,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManager.EnforcingUser;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
@@ -118,6 +117,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -162,7 +162,11 @@
     private static final String TAG_USER = "user";
     private static final String TAG_RESTRICTIONS = "restrictions";
     private static final String TAG_DEVICE_POLICY_RESTRICTIONS = "device_policy_restrictions";
+    private static final String TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS =
+            "device_policy_global_restrictions";
+    /** Legacy name for device owner id tag. */
     private static final String TAG_GLOBAL_RESTRICTION_OWNER_ID = "globalRestrictionOwnerUserId";
+    private static final String TAG_DEVICE_OWNER_USER_ID = "deviceOwnerUserId";
     private static final String TAG_ENTRY = "entry";
     private static final String TAG_VALUE = "value";
     private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
@@ -202,7 +206,7 @@
     @VisibleForTesting
     static final int MAX_RECENTLY_REMOVED_IDS_SIZE = 100;
 
-    private static final int USER_VERSION = 6;
+    private static final int USER_VERSION = 7;
 
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
@@ -267,7 +271,7 @@
 
     /**
      * User restrictions set via UserManager.  This doesn't include restrictions set by
-     * device owner / profile owners.
+     * device owner / profile owners. Only non-empty restriction bundles are stored.
      *
      * DO NOT Change existing {@link Bundle} in it.  When changing a restriction for a user,
      * a new {@link Bundle} should always be created and set.  This is because a {@link Bundle}
@@ -305,20 +309,21 @@
 
     /**
      * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
-     * that should be applied to all users, including guests.
+     * that should be applied to all users, including guests. Only non-empty restriction bundles are
+     * stored.
      */
     @GuardedBy("mRestrictionsLock")
-    private Bundle mDevicePolicyGlobalUserRestrictions;
+    private final SparseArray<Bundle> mDevicePolicyGlobalUserRestrictions = new SparseArray<>();
 
     /**
      * Id of the user that set global restrictions.
      */
     @GuardedBy("mRestrictionsLock")
-    private int mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL;
+    private int mDeviceOwnerUserId = UserHandle.USER_NULL;
 
     /**
      * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
-     * for each user.
+     * for each user. Only non-empty restriction bundles are stored.
      */
     @GuardedBy("mRestrictionsLock")
     private final SparseArray<Bundle> mDevicePolicyLocalUserRestrictions = new SparseArray<>();
@@ -711,7 +716,7 @@
             return null;
         }
         int parentUserId = profile.profileGroupId;
-        if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) {
+        if (parentUserId == userHandle || parentUserId == UserInfo.NO_PROFILE_GROUP_ID) {
             return null;
         } else {
             return getUserInfoLU(parentUserId);
@@ -1176,39 +1181,36 @@
     }
 
     /**
-     * See {@link UserManagerInternal#setDevicePolicyUserRestrictions(int, Bundle, Bundle)}
+     * See {@link UserManagerInternal#setDevicePolicyUserRestrictions}
      */
-    void setDevicePolicyUserRestrictionsInner(int userId, @NonNull Bundle local,
-            @Nullable Bundle global) {
-        Preconditions.checkNotNull(local);
-        boolean globalChanged = false;
-        boolean localChanged;
+    private void setDevicePolicyUserRestrictionsInner(int userId, @Nullable Bundle restrictions,
+            boolean isDeviceOwner, int cameraRestrictionScope) {
+        final Bundle global = new Bundle();
+        final Bundle local = new Bundle();
+
+        // Sort restrictions into local and global ensuring they don't overlap.
+        UserRestrictionsUtils.sortToGlobalAndLocal(restrictions, isDeviceOwner,
+                cameraRestrictionScope, global, local);
+
+        boolean globalChanged, localChanged;
         synchronized (mRestrictionsLock) {
-            if (global != null) {
-                // Update global.
-                globalChanged = !UserRestrictionsUtils.areEqual(
-                        mDevicePolicyGlobalUserRestrictions, global);
-                if (globalChanged) {
-                    mDevicePolicyGlobalUserRestrictions = global;
-                }
+            // Update global and local restrictions if they were changed.
+            globalChanged = updateRestrictionsIfNeededLR(
+                    userId, global, mDevicePolicyGlobalUserRestrictions);
+            localChanged = updateRestrictionsIfNeededLR(
+                    userId, local, mDevicePolicyLocalUserRestrictions);
+
+            if (isDeviceOwner) {
                 // Remember the global restriction owner userId to be able to make a distinction
                 // in getUserRestrictionSource on who set local policies.
-                mGlobalRestrictionOwnerUserId = userId;
+                mDeviceOwnerUserId = userId;
             } else {
-                if (mGlobalRestrictionOwnerUserId == userId) {
+                if (mDeviceOwnerUserId == userId) {
                     // When profile owner sets restrictions it passes null global bundle and we
                     // reset global restriction owner userId.
                     // This means this user used to have DO, but now the DO is gone and the user
                     // instead has PO.
-                    mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL;
-                }
-            }
-            {
-                // Update local.
-                final Bundle prev = mDevicePolicyLocalUserRestrictions.get(userId);
-                localChanged = !UserRestrictionsUtils.areEqual(prev, local);
-                if (localChanged) {
-                    mDevicePolicyLocalUserRestrictions.put(userId, local);
+                    mDeviceOwnerUserId = UserHandle.USER_NULL;
                 }
             }
         }
@@ -1220,12 +1222,9 @@
         }
         // Don't call them within the mRestrictionsLock.
         synchronized (mPackagesLock) {
-            if (localChanged) {
+            if (localChanged || globalChanged) {
                 writeUserLP(getUserDataNoChecks(userId));
             }
-            if (globalChanged) {
-                writeUserListLP();
-            }
         }
 
         synchronized (mRestrictionsLock) {
@@ -1237,11 +1236,30 @@
         }
     }
 
+    /**
+     * Updates restriction bundle for a given user in a given restriction array. If new bundle is
+     * empty, record is removed from the array.
+     * @return whether restrictions bundle is different from the old one.
+     */
+    private boolean updateRestrictionsIfNeededLR(int userId, @Nullable Bundle restrictions,
+            SparseArray<Bundle> restrictionsArray) {
+        final boolean changed =
+                !UserRestrictionsUtils.areEqual(restrictionsArray.get(userId), restrictions);
+        if (changed) {
+            if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+                restrictionsArray.put(userId, restrictions);
+            } else {
+                restrictionsArray.delete(userId);
+            }
+        }
+        return changed;
+    }
+
     @GuardedBy("mRestrictionsLock")
     private Bundle computeEffectiveUserRestrictionsLR(int userId) {
         final Bundle baseRestrictions =
                 UserRestrictionsUtils.nonNull(mBaseUserRestrictions.get(userId));
-        final Bundle global = mDevicePolicyGlobalUserRestrictions;
+        final Bundle global = UserRestrictionsUtils.mergeAll(mDevicePolicyGlobalUserRestrictions);
         final Bundle local = mDevicePolicyLocalUserRestrictions.get(userId);
 
         if (UserRestrictionsUtils.isEmpty(global) && UserRestrictionsUtils.isEmpty(local)) {
@@ -1299,39 +1317,58 @@
      */
     @Override
     public int getUserRestrictionSource(String restrictionKey, int userId) {
-        checkManageUsersPermission("getUserRestrictionSource");
+        List<EnforcingUser> enforcingUsers = getUserRestrictionSources(restrictionKey,  userId);
+        // Get "bitwise or" of restriction sources for all enforcing users.
         int result = UserManager.RESTRICTION_NOT_SET;
+        for (int i = enforcingUsers.size() - 1; i >= 0; i--) {
+            result |= enforcingUsers.get(i).getUserRestrictionSource();
+        }
+        return result;
+    }
+
+    @Override
+    public List<EnforcingUser> getUserRestrictionSources(
+            String restrictionKey, @UserIdInt int userId) {
+        checkManageUsersPermission("getUserRestrictionSource");
 
         // Shortcut for the most common case
         if (!hasUserRestriction(restrictionKey, userId)) {
-            return result;
+            return Collections.emptyList();
         }
 
+        final List<EnforcingUser> result = new ArrayList<>();
+
+        // Check if it is base restriction.
         if (hasBaseUserRestriction(restrictionKey, userId)) {
-            result |= UserManager.RESTRICTION_SOURCE_SYSTEM;
+            result.add(new EnforcingUser(
+                    UserHandle.USER_NULL, UserManager.RESTRICTION_SOURCE_SYSTEM));
         }
 
-        synchronized(mRestrictionsLock) {
-            Bundle localRestrictions = mDevicePolicyLocalUserRestrictions.get(userId);
-            if (!UserRestrictionsUtils.isEmpty(localRestrictions)
-                    && localRestrictions.getBoolean(restrictionKey)) {
-                // Local restrictions may have been set by device owner the userId of which is
-                // stored in mGlobalRestrictionOwnerUserId.
-                if (mGlobalRestrictionOwnerUserId == userId) {
-                    result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
-                } else {
-                    result |= UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
+        synchronized (mRestrictionsLock) {
+            // Check if it is set by profile owner.
+            Bundle profileOwnerRestrictions = mDevicePolicyLocalUserRestrictions.get(userId);
+            if (UserRestrictionsUtils.contains(profileOwnerRestrictions, restrictionKey)) {
+                result.add(getEnforcingUserLocked(userId));
+            }
+
+            // Iterate over all users who enforce global restrictions.
+            for (int i = mDevicePolicyGlobalUserRestrictions.size() - 1; i >= 0; i--) {
+                Bundle globalRestrictions = mDevicePolicyGlobalUserRestrictions.valueAt(i);
+                int profileUserId = mDevicePolicyGlobalUserRestrictions.keyAt(i);
+                if (UserRestrictionsUtils.contains(globalRestrictions, restrictionKey)) {
+                    result.add(getEnforcingUserLocked(profileUserId));
                 }
             }
-            if (!UserRestrictionsUtils.isEmpty(mDevicePolicyGlobalUserRestrictions)
-                    && mDevicePolicyGlobalUserRestrictions.getBoolean(restrictionKey)) {
-                result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
-            }
         }
-
         return result;
     }
 
+    private EnforcingUser getEnforcingUserLocked(@UserIdInt int userId) {
+        int source = mDeviceOwnerUserId == userId ? UserManager.RESTRICTION_SOURCE_DEVICE_OWNER
+                : UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
+        return new EnforcingUser(userId, source);
+    }
+
     /**
      * @return UserRestrictions that are in effect currently.  This always returns a new
      * {@link Bundle}.
@@ -1374,28 +1411,26 @@
      * Optionally updating user restrictions, calculate the effective user restrictions and also
      * propagate to other services and system settings.
      *
-     * @param newRestrictions User restrictions to set.
+     * @param newBaseRestrictions User restrictions to set.
      *      If null, will not update user restrictions and only does the propagation.
      * @param userId target user ID.
      */
     @GuardedBy("mRestrictionsLock")
     private void updateUserRestrictionsInternalLR(
-            @Nullable Bundle newRestrictions, int userId) {
-
+            @Nullable Bundle newBaseRestrictions, int userId) {
         final Bundle prevAppliedRestrictions = UserRestrictionsUtils.nonNull(
                 mAppliedUserRestrictions.get(userId));
 
         // Update base restrictions.
-        if (newRestrictions != null) {
-            // If newRestrictions == the current one, it's probably a bug.
+        if (newBaseRestrictions != null) {
+            // If newBaseRestrictions == the current one, it's probably a bug.
             final Bundle prevBaseRestrictions = mBaseUserRestrictions.get(userId);
 
-            Preconditions.checkState(prevBaseRestrictions != newRestrictions);
+            Preconditions.checkState(prevBaseRestrictions != newBaseRestrictions);
             Preconditions.checkState(mCachedEffectiveUserRestrictions.get(userId)
-                    != newRestrictions);
+                    != newBaseRestrictions);
 
-            if (!UserRestrictionsUtils.areEqual(prevBaseRestrictions, newRestrictions)) {
-                mBaseUserRestrictions.put(userId, newRestrictions);
+            if (updateRestrictionsIfNeededLR(userId, newBaseRestrictions, mBaseUserRestrictions)) {
                 scheduleWriteUser(getUserDataNoChecks(userId));
             }
         }
@@ -1530,7 +1565,7 @@
         }
         synchronized(mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
-            if (!userInfo.canHaveProfile()) {
+            if (userInfo == null || !userInfo.canHaveProfile()) {
                 return false;
             }
             int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
@@ -1746,7 +1781,9 @@
                 }
             }
 
-            final Bundle newDevicePolicyGlobalUserRestrictions = new Bundle();
+            // Pre-O global user restriction were stored as a single bundle (as opposed to per-user
+            // currently), take care of it in case of upgrade.
+            Bundle oldDevicePolicyGlobalUserRestrictions = null;
 
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (type == XmlPullParser.START_TAG) {
@@ -1771,29 +1808,30 @@
                             if (type == XmlPullParser.START_TAG) {
                                 if (parser.getName().equals(TAG_RESTRICTIONS)) {
                                     synchronized (mGuestRestrictions) {
-                                        UserRestrictionsUtils
-                                                .readRestrictions(parser, mGuestRestrictions);
+                                        mGuestRestrictions.putAll(
+                                                UserRestrictionsUtils.readRestrictions(parser));
                                     }
                                 }
                                 break;
                             }
                         }
-                    } else if (name.equals(TAG_DEVICE_POLICY_RESTRICTIONS)) {
-                        UserRestrictionsUtils.readRestrictions(parser,
-                                newDevicePolicyGlobalUserRestrictions);
-                    } else if (name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) {
+                    } else if (name.equals(TAG_DEVICE_OWNER_USER_ID)
+                            // Legacy name, should only be encountered when upgrading from pre-O.
+                            || name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) {
                         String ownerUserId = parser.getAttributeValue(null, ATTR_ID);
                         if (ownerUserId != null) {
-                            mGlobalRestrictionOwnerUserId = Integer.parseInt(ownerUserId);
+                            mDeviceOwnerUserId = Integer.parseInt(ownerUserId);
                         }
+                    } else if (name.equals(TAG_DEVICE_POLICY_RESTRICTIONS)) {
+                        // Should only happen when upgrading from pre-O (version < 7).
+                        oldDevicePolicyGlobalUserRestrictions =
+                                UserRestrictionsUtils.readRestrictions(parser);
                     }
                 }
             }
-            synchronized (mRestrictionsLock) {
-                mDevicePolicyGlobalUserRestrictions = newDevicePolicyGlobalUserRestrictions;
-            }
+
             updateUserIds();
-            upgradeIfNecessaryLP();
+            upgradeIfNecessaryLP(oldDevicePolicyGlobalUserRestrictions);
         } catch (IOException | XmlPullParserException e) {
             fallbackToSingleUserLP();
         } finally {
@@ -1803,8 +1841,9 @@
 
     /**
      * Upgrade steps between versions, either for fixing bugs or changing the data format.
+     * @param oldGlobalUserRestrictions Pre-O global device policy restrictions.
      */
-    private void upgradeIfNecessaryLP() {
+    private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) {
         final int originalVersion = mUserVersion;
         int userVersion = mUserVersion;
         if (userVersion < 1) {
@@ -1855,6 +1894,23 @@
             userVersion = 6;
         }
 
+        if (userVersion < 7) {
+            // Previously only one user could enforce global restrictions, now it is per-user.
+            synchronized (mRestrictionsLock) {
+                if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+                        && mDeviceOwnerUserId != UserHandle.USER_NULL) {
+                    mDevicePolicyGlobalUserRestrictions.put(
+                            mDeviceOwnerUserId, oldGlobalUserRestrictions);
+                }
+                // ENSURE_VERIFY_APPS is now enforced globally even if put by profile owner, so move
+                // it from local to global bundle for all users who set it.
+                UserRestrictionsUtils.moveRestriction(UserManager.ENSURE_VERIFY_APPS,
+                        mDevicePolicyLocalUserRestrictions, mDevicePolicyGlobalUserRestrictions
+                );
+            }
+            userVersion = 7;
+        }
+
         if (userVersion < USER_VERSION) {
             Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
                     + USER_VERSION);
@@ -1893,8 +1949,10 @@
             Log.e(LOG_TAG, "Couldn't find resource: config_defaultFirstUserRestrictions", e);
         }
 
-        synchronized (mRestrictionsLock) {
-            mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions);
+        if (!restrictions.isEmpty()) {
+            synchronized (mRestrictionsLock) {
+                mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions);
+            }
         }
 
         updateUserIds();
@@ -2004,6 +2062,9 @@
             UserRestrictionsUtils.writeRestrictions(serializer,
                     mDevicePolicyLocalUserRestrictions.get(userInfo.id),
                     TAG_DEVICE_POLICY_RESTRICTIONS);
+            UserRestrictionsUtils.writeRestrictions(serializer,
+                    mDevicePolicyGlobalUserRestrictions.get(userInfo.id),
+                    TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
         }
 
         if (userData.account != null) {
@@ -2057,13 +2118,9 @@
                         .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
             }
             serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
-            synchronized (mRestrictionsLock) {
-                UserRestrictionsUtils.writeRestrictions(serializer,
-                        mDevicePolicyGlobalUserRestrictions, TAG_DEVICE_POLICY_RESTRICTIONS);
-            }
-            serializer.startTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID);
-            serializer.attribute(null, ATTR_ID, Integer.toString(mGlobalRestrictionOwnerUserId));
-            serializer.endTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID);
+            serializer.startTag(null, TAG_DEVICE_OWNER_USER_ID);
+            serializer.attribute(null, ATTR_ID, Integer.toString(mDeviceOwnerUserId));
+            serializer.endTag(null, TAG_DEVICE_OWNER_USER_ID);
             int[] userIdsToWrite;
             synchronized (mUsersLock) {
                 userIdsToWrite = new int[mUsers.size()];
@@ -2125,8 +2182,9 @@
         String seedAccountName = null;
         String seedAccountType = null;
         PersistableBundle seedAccountOptions = null;
-        Bundle baseRestrictions = new Bundle();
-        Bundle localRestrictions = new Bundle();
+        Bundle baseRestrictions = null;
+        Bundle localRestrictions = null;
+        Bundle globalRestrictions = null;
 
         XmlPullParser parser = Xml.newPullParser();
         parser.setInput(is, StandardCharsets.UTF_8.name());
@@ -2187,9 +2245,11 @@
                         name = parser.getText();
                     }
                 } else if (TAG_RESTRICTIONS.equals(tag)) {
-                    UserRestrictionsUtils.readRestrictions(parser, baseRestrictions);
+                    baseRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                 } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
-                    UserRestrictionsUtils.readRestrictions(parser, localRestrictions);
+                    localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+                } else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) {
+                    globalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                 } else if (TAG_ACCOUNT.equals(tag)) {
                     type = parser.next();
                     if (type == XmlPullParser.TEXT) {
@@ -2224,8 +2284,15 @@
         userData.seedAccountOptions = seedAccountOptions;
 
         synchronized (mRestrictionsLock) {
-            mBaseUserRestrictions.put(id, baseRestrictions);
-            mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
+            if (baseRestrictions != null) {
+                mBaseUserRestrictions.put(id, baseRestrictions);
+            }
+            if (localRestrictions != null) {
+                mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
+            }
+            if (globalRestrictions != null) {
+                mDevicePolicyGlobalUserRestrictions.put(id, globalRestrictions);
+            }
         }
         return userData;
     }
@@ -2278,6 +2345,12 @@
     }
 
     @Override
+    public boolean removeUserEvenWhenDisallowed(@UserIdInt int userHandle) {
+        checkManageOrCreateUsersPermission("Only the system can remove users");
+        return removeUserUnchecked(userHandle);
+    }
+
+    @Override
     public UserInfo createUser(String name, int flags) {
         checkManageOrCreateUsersPermission(flags);
         return createUserInternal(name, flags, UserHandle.USER_NULL);
@@ -2731,6 +2804,10 @@
             mAppliedUserRestrictions.remove(userHandle);
             mCachedEffectiveUserRestrictions.remove(userHandle);
             mDevicePolicyLocalUserRestrictions.remove(userHandle);
+            if (mDevicePolicyGlobalUserRestrictions.get(userHandle) != null) {
+                mDevicePolicyGlobalUserRestrictions.remove(userHandle);
+                applyUserRestrictionsForAllUsersLR();
+            }
         }
         // Update the user list
         synchronized (mPackagesLock) {
@@ -3060,8 +3137,6 @@
                 applyUserRestrictionsLR(userId);
             }
         }
-
-        maybeInitializeDemoMode(userId);
     }
 
     /**
@@ -3100,29 +3175,6 @@
         scheduleWriteUser(userData);
     }
 
-    private void maybeInitializeDemoMode(int userId) {
-        if (UserManager.isDeviceInDemoMode(mContext) && userId != UserHandle.USER_SYSTEM) {
-            String demoLauncher =
-                    mContext.getResources().getString(
-                            com.android.internal.R.string.config_demoModeLauncherComponent);
-            if (!TextUtils.isEmpty(demoLauncher)) {
-                ComponentName componentToEnable = ComponentName.unflattenFromString(demoLauncher);
-                String demoLauncherPkg = componentToEnable.getPackageName();
-                try {
-                    final IPackageManager iPm = AppGlobals.getPackageManager();
-                    iPm.setComponentEnabledSetting(componentToEnable,
-                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
-                            /* userId= */ userId);
-                    iPm.setApplicationEnabledSetting(demoLauncherPkg,
-                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
-                            /* userId= */ userId, null);
-                } catch (RemoteException re) {
-                    // Internal, shouldn't happen
-                }
-            }
-        }
-    }
-
     /**
      * Returns the next available user id, filling in any holes in the ids.
      */
@@ -3420,6 +3472,9 @@
                     synchronized (mRestrictionsLock) {
                         UserRestrictionsUtils.dumpRestrictions(
                                 pw, "      ", mBaseUserRestrictions.get(userInfo.id));
+                        pw.println("    Device policy global restrictions:");
+                        UserRestrictionsUtils.dumpRestrictions(
+                                pw, "      ", mDevicePolicyGlobalUserRestrictions.get(userInfo.id));
                         pw.println("    Device policy local restrictions:");
                         UserRestrictionsUtils.dumpRestrictions(
                                 pw, "      ", mDevicePolicyLocalUserRestrictions.get(userInfo.id));
@@ -3448,13 +3503,7 @@
                 }
             }
             pw.println();
-            pw.println("  Device policy global restrictions:");
-            synchronized (mRestrictionsLock) {
-                UserRestrictionsUtils
-                        .dumpRestrictions(pw, "    ", mDevicePolicyGlobalUserRestrictions);
-            }
-            pw.println();
-            pw.println("  Global restrictions owner id:" + mGlobalRestrictionOwnerUserId);
+            pw.println("  Device owner id:" + mDeviceOwnerUserId);
             pw.println();
             pw.println("  Guest restrictions:");
             synchronized (mGuestRestrictions) {
@@ -3508,10 +3557,10 @@
 
     private class LocalService extends UserManagerInternal {
         @Override
-        public void setDevicePolicyUserRestrictions(int userId, @NonNull Bundle localRestrictions,
-                @Nullable Bundle globalRestrictions) {
-            UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId, localRestrictions,
-                    globalRestrictions);
+        public void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions,
+                boolean isDeviceOwner, int cameraRestrictionScope) {
+            UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId, restrictions,
+                isDeviceOwner, cameraRestrictionScope);
         }
 
         @Override
@@ -3525,8 +3574,10 @@
         public void setBaseUserRestrictionsByDpmsForMigration(
                 int userId, Bundle baseRestrictions) {
             synchronized (mRestrictionsLock) {
-                mBaseUserRestrictions.put(userId, new Bundle(baseRestrictions));
-                invalidateEffectiveUserRestrictionsLR(userId);
+                if (updateRestrictionsIfNeededLR(
+                        userId, new Bundle(baseRestrictions), mBaseUserRestrictions)) {
+                    invalidateEffectiveUserRestrictionsLR(userId);
+                }
             }
 
             final UserData userData = getUserDataNoChecks(userId);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index f5b8669..d301463 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -30,11 +30,13 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
@@ -117,9 +119,10 @@
     );
 
     /**
-     * User restrictions that can not be set by profile owners.
+     * User restrictions that cannot be set by profile owners of secondary users. When set by DO
+     * they will be applied to all users.
      */
-    private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
+    private static final Set<String> PRIMARY_USER_ONLY_RESTRICTIONS = Sets.newArraySet(
             UserManager.DISALLOW_BLUETOOTH,
             UserManager.DISALLOW_USB_FILE_TRANSFER,
             UserManager.DISALLOW_CONFIG_TETHERING,
@@ -163,6 +166,13 @@
             UserManager.DISALLOW_ADD_MANAGED_PROFILE
     );
 
+    /*
+     * Special user restrictions that are always applied to all users no matter who sets them.
+     */
+    private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet(
+            UserManager.ENSURE_VERIFY_APPS
+    );
+
     /**
      * Throws {@link IllegalArgumentException} if the given restriction name is invalid.
      */
@@ -205,6 +215,12 @@
         }
     }
 
+    public static Bundle readRestrictions(XmlPullParser parser) {
+        final Bundle result = new Bundle();
+        readRestrictions(parser, result);
+        return result;
+    }
+
     /**
      * @return {@code in} itself when it's not null, or an empty bundle (which can writable).
      */
@@ -217,6 +233,14 @@
     }
 
     /**
+     * Returns {@code true} if given bundle is not null and contains {@code true} for a given
+     * restriction.
+     */
+    public static boolean contains(@Nullable Bundle in, String restriction) {
+        return in != null && in.getBoolean(restriction);
+    }
+
+    /**
      * Creates a copy of the {@code in} Bundle.  If {@code in} is null, it'll return an empty
      * bundle.
      *
@@ -241,6 +265,22 @@
     }
 
     /**
+     * Merges a sparse array of restrictions bundles into one.
+     */
+    @Nullable
+    public static Bundle mergeAll(SparseArray<Bundle> restrictions) {
+        if (restrictions.size() == 0) {
+            return null;
+        } else {
+            final Bundle result = new Bundle();
+            for (int i = 0; i < restrictions.size(); i++) {
+                merge(result, restrictions.valueAt(i));
+            }
+            return result;
+        }
+    }
+
+    /**
      * @return true if a restriction is settable by device owner.
      */
     public static boolean canDeviceOwnerChange(String restriction) {
@@ -254,7 +294,7 @@
     public static boolean canProfileOwnerChange(String restriction, int userId) {
         return !IMMUTABLE_BY_OWNERS.contains(restriction)
                 && !(userId != UserHandle.USER_SYSTEM
-                    && DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction));
+                    && PRIMARY_USER_ONLY_RESTRICTIONS.contains(restriction));
     }
 
     /**
@@ -269,8 +309,15 @@
      * Takes restrictions that can be set by device owner, and sort them into what should be applied
      * globally and what should be applied only on the current user.
      */
-    public static void sortToGlobalAndLocal(@Nullable Bundle in, @NonNull Bundle global,
-            @NonNull Bundle local) {
+    public static void sortToGlobalAndLocal(@Nullable Bundle in, boolean isDeviceOwner,
+            int cameraRestrictionScope,
+            @NonNull Bundle global, @NonNull Bundle local) {
+        // Camera restriction (as well as all others) goes to at most one bundle.
+        if (cameraRestrictionScope == UserManagerInternal.CAMERA_DISABLED_GLOBALLY) {
+            global.putBoolean(UserManager.DISALLOW_CAMERA, true);
+        } else if (cameraRestrictionScope == UserManagerInternal.CAMERA_DISABLED_LOCALLY) {
+            local.putBoolean(UserManager.DISALLOW_CAMERA, true);
+        }
         if (in == null || in.size() == 0) {
             return;
         }
@@ -278,7 +325,7 @@
             if (!in.getBoolean(key)) {
                 continue;
             }
-            if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)) {
+            if (isGlobal(isDeviceOwner, key)) {
                 global.putBoolean(key, true);
             } else {
                 local.putBoolean(key, true);
@@ -287,6 +334,15 @@
     }
 
     /**
+     * Whether given user restriction should be enforced globally.
+     */
+    private static boolean isGlobal(boolean isDeviceOwner, String key) {
+        return (isDeviceOwner &&
+                (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key)|| GLOBAL_RESTRICTIONS.contains(key)))
+                || PROFILE_GLOBAL_RESTRICTIONS.contains(key);
+    }
+
+    /**
      * @return true if two Bundles contain the same user restriction.
      * A null bundle and an empty bundle are considered to be equal.
      */
@@ -485,4 +541,29 @@
             pw.println(prefix + "null");
         }
     }
+
+    /**
+     * Moves a particular restriction from one array of bundles to another, e.g. for all users.
+     */
+    public static void moveRestriction(String restrictionKey, SparseArray<Bundle> srcRestrictions,
+            SparseArray<Bundle> destRestrictions) {
+        for (int i = 0; i < srcRestrictions.size(); i++) {
+            int key = srcRestrictions.keyAt(i);
+            Bundle from = srcRestrictions.valueAt(i);
+            if (contains(from, restrictionKey)) {
+                from.remove(restrictionKey);
+                Bundle to = destRestrictions.get(key);
+                if (to == null) {
+                    to = new Bundle();
+                    destRestrictions.append(key, to);
+                }
+                to.putBoolean(restrictionKey, true);
+                // Don't keep empty bundles.
+                if (from.isEmpty()) {
+                    srcRestrictions.removeAt(i);
+                    i--;
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
new file mode 100644
index 0000000..133881a
--- /dev/null
+++ b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.server.policy;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.ContentObserver;
+import android.media.AudioAttributes;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import android.widget.Toast;
+import com.android.internal.R;
+
+import java.util.List;
+
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+
+/**
+ * Class to help manage the accessibility shortcut
+ */
+public class AccessibilityShortcutController {
+    private static final String TAG = "AccessibilityShortcutController";
+
+    private final Context mContext;
+    private AlertDialog mAlertDialog;
+    private boolean mIsShortcutEnabled;
+    // Visible for testing
+    public FrameworkObjectProvider mFrameworkObjectProvider = new FrameworkObjectProvider();
+
+    public static String getTargetServiceComponentNameString(
+            Context context, int userId) {
+        final String currentShortcutServiceId = Settings.Secure.getStringForUser(
+                context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                userId);
+        if (currentShortcutServiceId != null) {
+            return currentShortcutServiceId;
+        }
+        return context.getString(R.string.config_defaultAccessibilityService);
+    }
+
+    public AccessibilityShortcutController(Context context, Handler handler) {
+        mContext = context;
+
+        // Keep track of state of shortcut
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE),
+                false,
+                new ContentObserver(handler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        onSettingsChanged();
+                    }
+                },
+                UserHandle.USER_ALL);
+        updateShortcutEnabled();
+    }
+
+    public boolean isAccessibilityShortcutAvailable() {
+        return mIsShortcutEnabled;
+    }
+
+    public void onSettingsChanged() {
+        updateShortcutEnabled();
+    }
+
+    /**
+     * Called when the accessibility shortcut is activated
+     */
+    public void performAccessibilityShortcut() {
+        Slog.d(TAG, "Accessibility shortcut activated");
+        final ContentResolver cr = mContext.getContentResolver();
+        final int userId = ActivityManager.getCurrentUser();
+        final int dialogAlreadyShown = Settings.Secure.getIntForUser(
+                cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
+        final Ringtone tone =
+                RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_NOTIFICATION_URI);
+        if (tone != null) {
+            tone.setAudioAttributes(new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
+                .build());
+            tone.play();
+        }
+        if (dialogAlreadyShown == 0) {
+            // The first time, we show a warning rather than toggle the service to give the user a
+            // chance to turn off this feature before stuff gets enabled.
+            mAlertDialog = createShortcutWarningDialog(userId);
+            if (mAlertDialog == null) {
+                return;
+            }
+            Window w = mAlertDialog.getWindow();
+            WindowManager.LayoutParams attr = w.getAttributes();
+            attr.type = TYPE_KEYGUARD_DIALOG;
+            w.setAttributes(attr);
+            mAlertDialog.show();
+            Settings.Secure.putIntForUser(
+                    cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1, userId);
+        } else {
+            if (mAlertDialog != null) {
+                mAlertDialog.dismiss();
+                mAlertDialog = null;
+            }
+
+            // Show a toast alerting the user to what's happening
+            final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+            if (serviceInfo == null) {
+                Slog.e(TAG, "Accessibility shortcut set to invalid service");
+                return;
+            }
+            String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+                    ? R.string.accessibility_shortcut_disabling_service
+                    : R.string.accessibility_shortcut_enabling_service);
+            String toastMessage = String.format(toastMessageFormatString,
+                    serviceInfo.getResolveInfo()
+                            .loadLabel(mContext.getPackageManager()).toString());
+            mFrameworkObjectProvider.makeToastFromText(mContext, toastMessage, Toast.LENGTH_LONG)
+                    .show();
+
+            mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)
+                    .performAccessibilityShortcut();
+        }
+    }
+
+    private void updateShortcutEnabled() {
+        mIsShortcutEnabled = !TextUtils.isEmpty(getTargetServiceComponentNameString(
+                mContext, UserHandle.myUserId()));
+    }
+
+    private AlertDialog createShortcutWarningDialog(int userId) {
+        final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+
+        if (serviceInfo == null) {
+            return null;
+        }
+
+        final String warningMessage = String.format(
+                mContext.getString(R.string.accessibility_shortcut_toogle_warning),
+                serviceInfo.getResolveInfo().loadLabel(mContext.getPackageManager()).toString());
+        final AlertDialog alertDialog = mFrameworkObjectProvider.getAlertDialogBuilder(mContext)
+                .setTitle(R.string.accessibility_shortcut_warning_dialog_title)
+                .setMessage(warningMessage)
+                .setCancelable(false)
+                .setPositiveButton(R.string.leave_accessibility_shortcut_on, null)
+                .setNegativeButton(R.string.disable_accessibility_shortcut,
+                        (DialogInterface d, int which) -> {
+                            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                                    Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "",
+                                    userId);
+                        })
+                .setOnCancelListener((DialogInterface d) -> {
+                    // If canceled, treat as if the dialog has never been shown
+                    Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
+                })
+                .create();
+        return alertDialog;
+    }
+
+    private AccessibilityServiceInfo getInfoForTargetService() {
+        final String currentShortcutServiceString = getTargetServiceComponentNameString(
+                mContext, UserHandle.myUserId());
+        if (currentShortcutServiceString == null) {
+            return null;
+        }
+        AccessibilityManager accessibilityManager =
+                mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
+        return accessibilityManager.getInstalledServiceInfoWithComponentName(
+                        ComponentName.unflattenFromString(currentShortcutServiceString));
+    }
+
+    private boolean isServiceEnabled(AccessibilityServiceInfo serviceInfo) {
+        AccessibilityManager accessibilityManager =
+                mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
+        return accessibilityManager.getEnabledAccessibilityServiceList(
+                AccessibilityServiceInfo.FEEDBACK_ALL_MASK).contains(serviceInfo);
+    }
+
+    // Class to allow mocking of static framework calls
+    public static class FrameworkObjectProvider {
+        public AccessibilityManager getAccessibilityManagerInstance(Context context) {
+            return AccessibilityManager.getInstance(context);
+        }
+
+        public AlertDialog.Builder getAlertDialogBuilder(Context context) {
+            return new AlertDialog.Builder(context);
+        }
+
+        public Toast makeToastFromText(Context context, CharSequence charSequence, int duration) {
+            return Toast.makeText(context, charSequence, duration);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/policy/EnableAccessibilityController.java b/services/core/java/com/android/server/policy/EnableAccessibilityController.java
deleted file mode 100644
index 6b203a9..0000000
--- a/services/core/java/com/android/server/policy/EnableAccessibilityController.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.server.policy;
-
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.pm.ServiceInfo;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.speech.tts.TextToSpeech;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.IWindowManager;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.WindowManagerInternal;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-
-import com.android.internal.R;
-import com.android.server.LocalServices;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-public class EnableAccessibilityController {
-    private static final String TAG = "EnableAccessibilityController";
-
-    private static final int SPEAK_WARNING_DELAY_MILLIS = 2000;
-    private static final int ENABLE_ACCESSIBILITY_DELAY_MILLIS = 6000;
-
-    public static final int MESSAGE_SPEAK_WARNING = 1;
-    public static final int MESSAGE_SPEAK_ENABLE_CANCELED = 2;
-    public static final int MESSAGE_ENABLE_ACCESSIBILITY = 3;
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MESSAGE_SPEAK_WARNING: {
-                    String text = mContext.getString(R.string.continue_to_enable_accessibility);
-                    mTts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
-                } break;
-                case MESSAGE_SPEAK_ENABLE_CANCELED: {
-                    String text = mContext.getString(R.string.enable_accessibility_canceled);
-                    mTts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
-                } break;
-                case MESSAGE_ENABLE_ACCESSIBILITY: {
-                    enableAccessibility();
-                    mTone.play();
-                    mTts.speak(mContext.getString(R.string.accessibility_enabled),
-                            TextToSpeech.QUEUE_FLUSH, null);
-                } break;
-            }
-        }
-    };
-
-    private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager
-            .Stub.asInterface(ServiceManager.getService("accessibility"));
-
-
-    private final Context mContext;
-    private final Runnable mOnAccessibilityEnabledCallback;
-    private final UserManager mUserManager;
-    private final TextToSpeech mTts;
-    private final Ringtone mTone;
-
-    private final float mTouchSlop;
-
-    private boolean mDestroyed;
-    private boolean mCanceled;
-
-    private float mFirstPointerDownX;
-    private float mFirstPointerDownY;
-    private float mSecondPointerDownX;
-    private float mSecondPointerDownY;
-
-    public EnableAccessibilityController(Context context, Runnable onAccessibilityEnabledCallback) {
-        mContext = context;
-        mOnAccessibilityEnabledCallback = onAccessibilityEnabledCallback;
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        mTts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
-            @Override
-            public void onInit(int status) {
-                if (mDestroyed) {
-                    mTts.shutdown();
-                }
-            }
-        });
-        mTone = RingtoneManager.getRingtone(context, Settings.System.DEFAULT_NOTIFICATION_URI);
-        mTone.setStreamType(AudioManager.STREAM_MUSIC);
-        mTouchSlop = context.getResources().getDimensionPixelSize(
-                R.dimen.accessibility_touch_slop);
-    }
-
-    public static boolean canEnableAccessibilityViaGesture(Context context) {
-        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(context);
-        // Accessibility is enabled and there is an enabled speaking
-        // accessibility service, then we have nothing to do.
-        if (accessibilityManager.isEnabled()
-                && !accessibilityManager.getEnabledAccessibilityServiceList(
-                        AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty()) {
-            return false;
-        }
-        // If the global gesture is enabled and there is a speaking service
-        // installed we are good to go, otherwise there is nothing to do.
-        return Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1
-                && !getInstalledSpeakingAccessibilityServices(context).isEmpty();
-    }
-
-    public static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices(
-            Context context) {
-        List<AccessibilityServiceInfo> services = new ArrayList<AccessibilityServiceInfo>();
-        services.addAll(AccessibilityManager.getInstance(context)
-                .getInstalledAccessibilityServiceList());
-        Iterator<AccessibilityServiceInfo> iterator = services.iterator();
-        while (iterator.hasNext()) {
-            AccessibilityServiceInfo service = iterator.next();
-            if ((service.feedbackType & AccessibilityServiceInfo.FEEDBACK_SPOKEN) == 0) {
-                iterator.remove();
-            }
-        }
-        return services;
-    }
-
-    public void onDestroy() {
-        mDestroyed = true;
-    }
-
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
-                && event.getPointerCount() == 2) {
-            mFirstPointerDownX = event.getX(0);
-            mFirstPointerDownY = event.getY(0);
-            mSecondPointerDownX = event.getX(1);
-            mSecondPointerDownY = event.getY(1);
-            mHandler.sendEmptyMessageDelayed(MESSAGE_SPEAK_WARNING,
-                    SPEAK_WARNING_DELAY_MILLIS);
-            mHandler.sendEmptyMessageDelayed(MESSAGE_ENABLE_ACCESSIBILITY,
-                   ENABLE_ACCESSIBILITY_DELAY_MILLIS);
-            return true;
-        }
-        return false;
-    }
-
-    public boolean onTouchEvent(MotionEvent event) {
-        final int pointerCount = event.getPointerCount();
-        final int action = event.getActionMasked();
-        if (mCanceled) {
-            if (action == MotionEvent.ACTION_UP) {
-                mCanceled = false;
-            }
-            return true;
-        }
-        switch (action) {
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                if (pointerCount > 2) {
-                    cancel();
-                }
-            } break;
-            case MotionEvent.ACTION_MOVE: {
-                final float firstPointerMove = MathUtils.dist(event.getX(0),
-                        event.getY(0), mFirstPointerDownX, mFirstPointerDownY);
-                if (Math.abs(firstPointerMove) > mTouchSlop) {
-                    cancel();
-                }
-                final float secondPointerMove = MathUtils.dist(event.getX(1),
-                        event.getY(1), mSecondPointerDownX, mSecondPointerDownY);
-                if (Math.abs(secondPointerMove) > mTouchSlop) {
-                    cancel();
-                }
-            } break;
-            case MotionEvent.ACTION_POINTER_UP:
-            case MotionEvent.ACTION_CANCEL: {
-                cancel();
-            } break;
-        }
-        return true;
-    }
-
-    private void cancel() {
-        mCanceled = true;
-        if (mHandler.hasMessages(MESSAGE_SPEAK_WARNING)) {
-            mHandler.removeMessages(MESSAGE_SPEAK_WARNING);
-        } else if (mHandler.hasMessages(MESSAGE_ENABLE_ACCESSIBILITY)) {
-            mHandler.sendEmptyMessage(MESSAGE_SPEAK_ENABLE_CANCELED);
-        }
-        mHandler.removeMessages(MESSAGE_ENABLE_ACCESSIBILITY);
-    }
-
-    private void enableAccessibility() {
-        if (enableAccessibility(mContext)) {
-            mOnAccessibilityEnabledCallback.run();
-        }
-    }
-
-    public static boolean enableAccessibility(Context context) {
-        final IAccessibilityManager accessibilityManager = IAccessibilityManager
-                .Stub.asInterface(ServiceManager.getService("accessibility"));
-        final WindowManagerInternal windowManager = LocalServices.getService(
-                WindowManagerInternal.class);
-        final UserManager userManager = (UserManager) context.getSystemService(
-                Context.USER_SERVICE);
-        ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
-        if (componentName == null) {
-            return false;
-        }
-
-        boolean keyguardLocked = windowManager.isKeyguardLocked();
-        final boolean hasMoreThanOneUser = userManager.getUsers().size() > 1;
-        try {
-            if (!keyguardLocked || !hasMoreThanOneUser) {
-                final int userId = ActivityManager.getCurrentUser();
-                accessibilityManager.enableAccessibilityService(componentName, userId);
-            } else if (keyguardLocked) {
-                accessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved(
-                        componentName, true /* enableTouchExploration */);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "cannot enable accessibilty: " + e);
-        }
-
-        return true;
-    }
-
-    public static void disableAccessibility(Context context) {
-        final IAccessibilityManager accessibilityManager = IAccessibilityManager
-                .Stub.asInterface(ServiceManager.getService("accessibility"));
-        ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
-        if (componentName == null) {
-            return;
-        }
-
-        final int userId = ActivityManager.getCurrentUser();
-        try {
-            accessibilityManager.disableAccessibilityService(componentName, userId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "cannot disable accessibility " + e);
-        }
-    }
-
-    public static boolean isAccessibilityEnabled(Context context) {
-        final AccessibilityManager accessibilityManager =
-                context.getSystemService(AccessibilityManager.class);
-        List enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
-                AccessibilityServiceInfo.FEEDBACK_SPOKEN);
-        return enabledServices != null && !enabledServices.isEmpty();
-    }
-
-    @Nullable
-    public static ComponentName getInstalledSpeakingAccessibilityServiceComponent(
-            Context context) {
-        List<AccessibilityServiceInfo> services =
-                getInstalledSpeakingAccessibilityServices(context);
-        if (services.isEmpty()) {
-            return null;
-        }
-
-        ServiceInfo serviceInfo = services.get(0).getResolveInfo().serviceInfo;
-        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
-    }
-}
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index d4adcc4..335a230 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -44,7 +44,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -59,12 +58,9 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.TypedValue;
-import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -1194,21 +1190,14 @@
 
     private static final class GlobalActionsDialog extends Dialog implements DialogInterface {
         private final Context mContext;
-        private final int mWindowTouchSlop;
         private final AlertController mAlert;
         private final MyAdapter mAdapter;
 
-        private EnableAccessibilityController mEnableAccessibilityController;
-
-        private boolean mIntercepted;
-        private boolean mCancelOnUp;
-
         public GlobalActionsDialog(Context context, AlertParams params) {
             super(context, getDialogTheme(context));
             mContext = getContext();
             mAlert = AlertController.create(mContext, this, getWindow());
             mAdapter = (MyAdapter) params.mAdapter;
-            mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
             params.apply(mAlert);
         }
 
@@ -1221,76 +1210,10 @@
 
         @Override
         protected void onStart() {
-            // If global accessibility gesture can be performed, we will take care
-            // of dismissing the dialog on touch outside. This is because the dialog
-            // is dismissed on the first down while the global gesture is a long press
-            // with two fingers anywhere on the screen.
-            if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) {
-                mEnableAccessibilityController = new EnableAccessibilityController(mContext,
-                        new Runnable() {
-                    @Override
-                    public void run() {
-                        dismiss();
-                    }
-                });
-                super.setCanceledOnTouchOutside(false);
-            } else {
-                mEnableAccessibilityController = null;
-                super.setCanceledOnTouchOutside(true);
-            }
-
+            super.setCanceledOnTouchOutside(true);
             super.onStart();
         }
 
-        @Override
-        protected void onStop() {
-            if (mEnableAccessibilityController != null) {
-                mEnableAccessibilityController.onDestroy();
-            }
-            super.onStop();
-        }
-
-        @Override
-        public boolean dispatchTouchEvent(MotionEvent event) {
-            if (mEnableAccessibilityController != null) {
-                final int action = event.getActionMasked();
-                if (action == MotionEvent.ACTION_DOWN) {
-                    View decor = getWindow().getDecorView();
-                    final int eventX = (int) event.getX();
-                    final int eventY = (int) event.getY();
-                    if (eventX < -mWindowTouchSlop
-                            || eventY < -mWindowTouchSlop
-                            || eventX >= decor.getWidth() + mWindowTouchSlop
-                            || eventY >= decor.getHeight() + mWindowTouchSlop) {
-                        mCancelOnUp = true;
-                    }
-                }
-                try {
-                    if (!mIntercepted) {
-                        mIntercepted = mEnableAccessibilityController.onInterceptTouchEvent(event);
-                        if (mIntercepted) {
-                            final long now = SystemClock.uptimeMillis();
-                            event = MotionEvent.obtain(now, now,
-                                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-                            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-                            mCancelOnUp = true;
-                        }
-                    } else {
-                        return mEnableAccessibilityController.onTouchEvent(event);
-                    }
-                } finally {
-                    if (action == MotionEvent.ACTION_UP) {
-                        if (mCancelOnUp) {
-                            cancel();
-                        }
-                        mCancelOnUp = false;
-                        mIntercepted = false;
-                    }
-                }
-            }
-            return super.dispatchTouchEvent(event);
-        }
-
         public ListView getListView() {
             return mAlert.getListView();
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4b2b184..4350ed9 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -19,6 +19,8 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.content.Context.DISPLAY_SERVICE;
+import static android.content.Context.WINDOW_SERVICE;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -140,6 +142,7 @@
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
@@ -149,8 +152,6 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.IAudioService;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
 import android.media.session.MediaSessionLegacyHelper;
 import android.os.Binder;
 import android.os.Build;
@@ -441,6 +442,9 @@
     /** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
     boolean mEnableShiftMenuBugReports = false;
 
+    /** Controller that supports enabling an AccessibilityService by holding down the volume keys */
+    private AccessibilityShortcutController mAccessibilityShortcutController;
+
     boolean mSafeMode;
     WindowState mStatusBar = null;
     int mStatusBarHeight;
@@ -748,7 +752,10 @@
     private boolean mScreenshotChordVolumeDownKeyTriggered;
     private long mScreenshotChordVolumeDownKeyTime;
     private boolean mScreenshotChordVolumeDownKeyConsumed;
-    private boolean mScreenshotChordVolumeUpKeyTriggered;
+    private boolean mA11yShortcutChordVolumeUpKeyTriggered;
+    private long mA11yShortcutChordVolumeUpKeyTime;
+    private boolean mA11yShortcutChordVolumeUpKeyConsumed;
+
     private boolean mScreenshotChordPowerKeyTriggered;
     private long mScreenshotChordPowerKeyTime;
 
@@ -794,6 +801,7 @@
     private static final int MSG_BACK_LONG_PRESS = 18;
     private static final int MSG_DISPOSE_INPUT_CONSUMER = 19;
     private static final int MSG_BACK_DELAYED_PRESS = 20;
+    private static final int MSG_ACCESSIBILITY_SHORTCUT = 21;
 
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -869,6 +877,9 @@
                     backMultiPressAction((Long) msg.obj, msg.arg1);
                     finishBackKeyPress();
                     break;
+                case MSG_ACCESSIBILITY_SHORTCUT:
+                    accessibilityShortcutActivated();
+                    break;
             }
         }
     }
@@ -1213,7 +1224,7 @@
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
         mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
-                || mScreenshotChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
+                || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
         if (!mPowerKeyHandled) {
             if (interactive) {
                 // When interactive, we're already awake.
@@ -1406,9 +1417,7 @@
             break;
         case LONG_PRESS_POWER_GLOBAL_ACTIONS:
             mPowerKeyHandled = true;
-            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
-                performAuditoryFeedbackForAccessibilityIfNeed();
-            }
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
             showGlobalActionsInternal();
             break;
         case LONG_PRESS_POWER_SHUT_OFF:
@@ -1439,6 +1448,10 @@
         }
     }
 
+    private void accessibilityShortcutActivated() {
+        mAccessibilityShortcutController.performAccessibilityShortcut();
+    }
+
     private void disposeInputConsumer(InputConsumer inputConsumer) {
         if (inputConsumer != null) {
             inputConsumer.dismiss();
@@ -1484,7 +1497,7 @@
     private void interceptScreenshotChord() {
         if (mScreenshotChordEnabled
                 && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
-                && !mScreenshotChordVolumeUpKeyTriggered) {
+                && !mA11yShortcutChordVolumeUpKeyTriggered) {
             final long now = SystemClock.uptimeMillis();
             if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
                     && now <= mScreenshotChordPowerKeyTime
@@ -1497,6 +1510,22 @@
         }
     }
 
+    private void interceptAccessibilityShortcutChord() {
+        if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable()
+                && mScreenshotChordVolumeDownKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered
+                && !mScreenshotChordPowerKeyTriggered) {
+            final long now = SystemClock.uptimeMillis();
+            if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
+                    && now <= mA11yShortcutChordVolumeUpKeyTime
+                    + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
+                mScreenshotChordVolumeDownKeyConsumed = true;
+                mA11yShortcutChordVolumeUpKeyConsumed = true;
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
+                        ViewConfiguration.get(mContext).getAccessibilityShortcutKeyTimeout());
+            }
+        }
+    }
+
     private long getScreenshotChordLongPressDelay() {
         if (mKeyguardDelegate.isShowing()) {
             // Double the time it takes to take a screenshot from the keyguard
@@ -1510,13 +1539,15 @@
         mHandler.removeCallbacks(mScreenshotRunnable);
     }
 
+    private void cancelPendingAccessibilityShortcutAction() {
+        mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+    }
+
     private final Runnable mEndCallLongPress = new Runnable() {
         @Override
         public void run() {
             mEndCallKeyHandled = true;
-            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
-                performAuditoryFeedbackForAccessibilityIfNeed();
-            }
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
             showGlobalActionsInternal();
         }
     };
@@ -1698,7 +1729,8 @@
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
-
+        mAccessibilityShortcutController =
+                new AccessibilityShortcutController(mContext, new Handler());
         // Init display burn-in protection
         boolean burnInProtectionEnabled = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableBurnInProtection);
@@ -2225,8 +2257,7 @@
             }
             lp.format = PixelFormat.TRANSLUCENT;
             lp.setTitle("PointerLocation");
-            WindowManager wm = (WindowManager)
-                    mContext.getSystemService(Context.WINDOW_SERVICE);
+            WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
             lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
             wm.addView(mPointerLocationView, lp);
             mWindowManagerFuncs.registerPointerEventListener(mPointerLocationView);
@@ -2236,7 +2267,7 @@
     private void disablePointerLocation() {
         if (mPointerLocationView != null) {
             mWindowManagerFuncs.unregisterPointerEventListener(mPointerLocationView);
-            WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+            WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
             wm.removeView(mPointerLocationView);
             mPointerLocationView = null;
         }
@@ -2797,7 +2828,7 @@
     @Override
     public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
             CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
-            int logo, int windowFlags, Configuration overrideConfig) {
+            int logo, int windowFlags, Configuration overrideConfig, int displayId) {
         if (!SHOW_SPLASH_SCREENS) {
             return null;
         }
@@ -2898,7 +2929,13 @@
 
             params.setTitle("Splash Screen " + packageName);
 
-            wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+            // Obtain proper context to launch on the right display.
+            final Context displayContext = getDisplayContext(context, displayId);
+            if (displayContext == null) {
+                // Can't show splash screen on requested display, so skip showing at all.
+                return null;
+            }
+            wm = (WindowManager) displayContext.getSystemService(WINDOW_SERVICE);
             view = win.getDecorView();
 
             if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
@@ -2928,6 +2965,24 @@
         return null;
     }
 
+    /** Obtain proper context for showing splash screen on the provided display. */
+    private Context getDisplayContext(Context context, int displayId) {
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            // The default context fits.
+            return context;
+        }
+
+        final DisplayManager dm = (DisplayManager) context.getSystemService(DISPLAY_SERVICE);
+        final Display targetDisplay = dm.getDisplay(displayId);
+        if (targetDisplay == null) {
+            // Failed to obtain the non-default display where splash screen should be shown,
+            // lets not show at all.
+            return null;
+        }
+
+        return context.createDisplayContext(targetDisplay);
+    }
+
     /**
      * Preflight adding a window to the system.
      *
@@ -3251,6 +3306,33 @@
             }
         }
 
+        // If an accessibility shortcut might be partially complete, hold off dispatching until we
+        // know if it is complete or not
+        if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable()
+                && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
+            if (mScreenshotChordVolumeDownKeyTriggered ^ mA11yShortcutChordVolumeUpKeyTriggered) {
+                final long now = SystemClock.uptimeMillis();
+                final long timeoutTime = (mScreenshotChordVolumeDownKeyTriggered
+                        ? mScreenshotChordVolumeDownKeyTime : mA11yShortcutChordVolumeUpKeyTime)
+                        + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
+                if (now < timeoutTime) {
+                    return timeoutTime - now;
+                }
+            }
+            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mScreenshotChordVolumeDownKeyConsumed) {
+                if (!down) {
+                    mScreenshotChordVolumeDownKeyConsumed = false;
+                }
+                return -1;
+            }
+            if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) {
+                if (!down) {
+                    mA11yShortcutChordVolumeUpKeyConsumed = false;
+                }
+                return -1;
+            }
+        }
+
         // Cancel any pending meta actions if we see any other keys being pressed between the down
         // of the meta key and its corresponding up.
         if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
@@ -3481,6 +3563,9 @@
                 dispatchDirectAudioEvent(event);
                 return -1;
             }
+        } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) {
+            // Pass through keyboard navigation keys.
+            return 0;
         }
 
         // Toggle Caps Lock on META-ALT.
@@ -5760,22 +5845,32 @@
                             mScreenshotChordVolumeDownKeyConsumed = false;
                             cancelPendingPowerKeyAction();
                             interceptScreenshotChord();
+                            if (!keyguardActive) {
+                                interceptAccessibilityShortcutChord();
+                            }
                         }
                     } else {
                         mScreenshotChordVolumeDownKeyTriggered = false;
                         cancelPendingScreenshotChordAction();
+                        cancelPendingAccessibilityShortcutAction();
                     }
                 } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
                     if (down) {
-                        if (interactive && !mScreenshotChordVolumeUpKeyTriggered
+                        if (interactive && !mA11yShortcutChordVolumeUpKeyTriggered
                                 && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
-                            mScreenshotChordVolumeUpKeyTriggered = true;
+                            mA11yShortcutChordVolumeUpKeyTriggered = true;
+                            mA11yShortcutChordVolumeUpKeyTime = event.getDownTime();
+                            mA11yShortcutChordVolumeUpKeyConsumed = false;
                             cancelPendingPowerKeyAction();
                             cancelPendingScreenshotChordAction();
+                            if (!keyguardActive) {
+                                interceptAccessibilityShortcutChord();
+                            }
                         }
                     } else {
-                        mScreenshotChordVolumeUpKeyTriggered = false;
+                        mA11yShortcutChordVolumeUpKeyTriggered = false;
                         cancelPendingScreenshotChordAction();
+                        cancelPendingAccessibilityShortcutAction();
                     }
                 }
                 if (down) {
@@ -5804,9 +5899,8 @@
                                 && (result & ACTION_PASS_TO_USER) == 0) {
                             // If we are in call but we decided not to pass the key to
                             // the application, just pass it to the session service.
-
-                            MediaSessionLegacyHelper.getHelper(mContext)
-                                    .sendVolumeKeyEvent(event, false);
+                            MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
+                                    event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
                             break;
                         }
                     }
@@ -5819,8 +5913,8 @@
                     // If we aren't passing to the user and no one else
                     // handled it send it to the session manager to
                     // figure out.
-                    MediaSessionLegacyHelper.getHelper(mContext)
-                            .sendVolumeKeyEvent(event, true);
+                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
+                            event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
                 }
                 break;
             }
@@ -5863,6 +5957,8 @@
             }
 
             case KeyEvent.KEYCODE_POWER: {
+                // Any activity on the power button stops the accessibility shortcut
+                cancelPendingAccessibilityShortcutAction();
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
                 if (down) {
@@ -7416,31 +7512,11 @@
         }
     }
 
-    private void performAuditoryFeedbackForAccessibilityIfNeed() {
-        if (!isGlobalAccessibilityGestureEnabled()) {
-            return;
-        }
-        AudioManager audioManager = (AudioManager) mContext.getSystemService(
-                Context.AUDIO_SERVICE);
-        if (audioManager.isSilentMode()) {
-            return;
-        }
-        Ringtone ringTone = RingtoneManager.getRingtone(mContext,
-                Settings.System.DEFAULT_NOTIFICATION_URI);
-        ringTone.setStreamType(AudioManager.STREAM_MUSIC);
-        ringTone.play();
-    }
-
     private boolean isTheaterModeEnabled() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.THEATER_MODE_ON, 0) == 1;
     }
 
-    private boolean isGlobalAccessibilityGestureEnabled() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1;
-    }
-
     private boolean areSystemNavigationKeysEnabled() {
         return Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8aefebc..238866a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -57,6 +57,8 @@
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.util.EventLog;
+import android.util.KeyValueListParser;
+import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -69,6 +71,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.EventLogTags;
+import com.android.server.RescueParty;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.Watchdog;
@@ -520,6 +523,65 @@
     // True if we are currently in VR Mode.
     private boolean mIsVrModeEnabled;
 
+    /**
+     * All times are in milliseconds. These constants are kept synchronized with the system
+     * global Settings. Any access to this class or its fields should be done while
+     * holding the PowerManagerService.mLock lock.
+     */
+    private final class Constants extends ContentObserver {
+        // Key names stored in the settings value.
+        private static final String KEY_NO_CACHED_WAKE_LOCKS = "no_cached_wake_locks";
+
+        private static final boolean DEFAULT_NO_CACHED_WAKE_LOCKS = true;
+
+        // Prevent processes that are cached from holding wake locks?
+        public boolean NO_CACHED_WAKE_LOCKS = DEFAULT_NO_CACHED_WAKE_LOCKS;
+
+        private ContentResolver mResolver;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        public Constants(Handler handler) {
+            super(handler);
+        }
+
+        public void start(ContentResolver resolver) {
+            mResolver = resolver;
+            mResolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.POWER_MANAGER_CONSTANTS), false, this);
+            updateConstants();
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updateConstants();
+        }
+
+        private void updateConstants() {
+            synchronized (mLock) {
+                try {
+                    mParser.setString(Settings.Global.getString(mResolver,
+                            Settings.Global.POWER_MANAGER_CONSTANTS));
+                } catch (IllegalArgumentException e) {
+                    // Failed to parse the settings string, log this and move on
+                    // with defaults.
+                    Slog.e(TAG, "Bad alarm manager settings", e);
+                }
+
+                NO_CACHED_WAKE_LOCKS = mParser.getBoolean(KEY_NO_CACHED_WAKE_LOCKS,
+                        DEFAULT_NO_CACHED_WAKE_LOCKS);
+            }
+        }
+
+        void dump(PrintWriter pw) {
+            pw.println("  Settings " + Settings.Global.POWER_MANAGER_CONSTANTS + ":");
+
+            pw.print("    "); pw.print(KEY_NO_CACHED_WAKE_LOCKS); pw.print("=");
+            pw.println(NO_CACHED_WAKE_LOCKS);
+        }
+    }
+
+    final Constants mConstants;
+
     private native void nativeInit();
 
     private static native void nativeAcquireSuspendBlocker(String name);
@@ -536,6 +598,7 @@
                 Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
         mHandlerThread.start();
         mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+        mConstants = new Constants(mHandler);
 
         synchronized (mLock) {
             mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
@@ -614,6 +677,9 @@
                     mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
                     mPolicy);
 
+            final ContentResolver resolver = mContext.getContentResolver();
+            mConstants.start(resolver);
+
             mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
                     createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
                     mHandler);
@@ -627,7 +693,6 @@
                     mDisplayPowerCallbacks, mHandler, sensorManager);
 
             // Register for settings changes.
-            final ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.SCREENSAVER_ENABLED),
                     false, mSettingsObserver, UserHandle.USER_ALL);
@@ -2549,7 +2614,14 @@
     private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
             final String reason, boolean wait) {
         if (mHandler == null || !mSystemReady) {
-            throw new IllegalStateException("Too early to call shutdown() or reboot()");
+            if (RescueParty.isAttemptingFactoryReset()) {
+                // If we're stuck in a really low-level reboot loop, and a
+                // rescue party is trying to prompt the user for a factory data
+                // reset, we must GET TO DA CHOPPA!
+                PowerManagerService.lowLevelReboot(reason);
+            } else {
+                throw new IllegalStateException("Too early to call shutdown() or reboot()");
+            }
         }
 
         Runnable runnable = new Runnable() {
@@ -2786,7 +2858,7 @@
                             state.mProcState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
                         disabled = true;
                     }
-                } else {
+                } else if (mConstants.NO_CACHED_WAKE_LOCKS) {
                     disabled = !wakeLock.mUidState.mActive &&
                             wakeLock.mUidState.mProcState
                                     != ActivityManager.PROCESS_STATE_NONEXISTENT &&
@@ -2996,6 +3068,7 @@
         final WirelessChargerDetector wcd;
         synchronized (mLock) {
             pw.println("Power Manager State:");
+            mConstants.dump(pw);
             pw.println("  mDirty=0x" + Integer.toHexString(mDirty));
             pw.println("  mWakefulness=" + PowerManagerInternal.wakefulnessToString(mWakefulness));
             pw.println("  mWakefulnessChanging=" + mWakefulnessChanging);
diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index 23be9a3..5a1f473 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -16,79 +16,95 @@
 
 package com.android.server.storage;
 
-import android.annotation.CallSuper;
-import android.annotation.WorkerThread;
-import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.OsConstants;
-import android.util.Log;
-import com.android.internal.os.AppFuseMount;
+import com.android.internal.util.Preconditions;
 import libcore.io.IoUtils;
-
 import java.io.File;
-import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.BlockingQueue;
 
-public class AppFuseBridge implements Runnable {
-    private static final String TAG = AppFuseBridge.class.getSimpleName();
-
-    private final FileDescriptor mDeviceFd;
-    private final FileDescriptor mProxyFd;
-    private final CountDownLatch mMountLatch = new CountDownLatch(1);
+/**
+ * Runnable that delegates FUSE command from the kernel to application.
+ * run() blocks until all opened files on the FUSE mount point are closed. So this should be run in
+ * a separated thread.
+ */
+public class AppFuseBridge implements Runnable, AutoCloseable {
+    public static final String TAG = "AppFuseBridge";
 
     /**
-     * @param deviceFd FD of /dev/fuse. Ownership of fd is taken by AppFuseBridge.
-     * @param proxyFd FD of socket pair. Ownership of fd is taken by AppFuseBridge.
+     * The path AppFuse is mounted to.
+     * The first number is UID who is mounting the FUSE.
+     * THe second number is mount ID.
+     * The path must be sync with vold.
      */
-    private AppFuseBridge(FileDescriptor deviceFd, FileDescriptor proxyFd) {
-        mDeviceFd = deviceFd;
+    private static final String APPFUSE_MOUNT_NAME_TEMPLATE = "/mnt/appfuse/%d_%d";
+
+    private final IMountScope mMountScope;
+    private final ParcelFileDescriptor mProxyFd;
+    private final BlockingQueue<Boolean> mChannel;
+
+    /**
+     * @param mountScope Listener to unmount mount point.
+     * @param proxyFd FD of socket pair. Ownership of FD is taken by AppFuseBridge.
+     * @param channel Channel that the runnable send mount result to.
+     */
+    public AppFuseBridge(
+            IMountScope mountScope, ParcelFileDescriptor proxyFd, BlockingQueue<Boolean> channel) {
+        Preconditions.checkNotNull(mountScope);
+        Preconditions.checkNotNull(proxyFd);
+        Preconditions.checkNotNull(channel);
+        mMountScope = mountScope;
         mProxyFd = proxyFd;
-    }
-
-    public static AppFuseMount startMessageLoop(
-            int uid,
-            String name,
-            FileDescriptor deviceFd,
-            Handler handler,
-            ParcelFileDescriptor.OnCloseListener listener)
-                    throws IOException, ErrnoException, InterruptedException {
-        final FileDescriptor localFd = new FileDescriptor();
-        final FileDescriptor remoteFd = new FileDescriptor();
-        // Needs to specify OsConstants.SOCK_SEQPACKET to keep message boundaries.
-        Os.socketpair(OsConstants.AF_UNIX, OsConstants.SOCK_SEQPACKET, 0, remoteFd, localFd);
-
-        // Caller must invoke #start() after instantiate AppFuseBridge.
-        // Otherwise FDs will be leaked.
-        final AppFuseBridge bridge = new AppFuseBridge(deviceFd, localFd);
-        final Thread thread = new Thread(bridge, TAG);
-        thread.start();
-        try {
-            bridge.mMountLatch.await();
-        } catch (InterruptedException error) {
-            throw error;
-        }
-        return new AppFuseMount(
-                new File("/mnt/appfuse/" + uid + "_" + name),
-                ParcelFileDescriptor.fromFd(remoteFd, handler, listener));
+        mChannel = channel;
     }
 
     @Override
     public void run() {
-        // deviceFd and proxyFd must be closed in native_start_loop.
-        final int deviceFd = mDeviceFd.getInt$();
-        final int proxyFd = mProxyFd.getInt$();
-        mDeviceFd.setInt$(-1);
-        mProxyFd.setInt$(-1);
-        native_start_loop(deviceFd, proxyFd);
+        try {
+            // deviceFd and proxyFd must be closed in native_start_loop.
+            native_start_loop(
+                    mMountScope.getDeviceFileDescriptor().detachFd(),
+                    mProxyFd.detachFd());
+        } finally {
+            close();
+        }
+    }
+
+    public static ParcelFileDescriptor openFile(int uid, int mountId, int fileId, int mode)
+            throws FileNotFoundException {
+        final File mountPoint = getMountPoint(uid, mountId);
+        try {
+            if (Os.stat(mountPoint.getPath()).st_ino != 1) {
+                throw new FileNotFoundException("Could not find bridge mount point.");
+            }
+        } catch (ErrnoException e) {
+            throw new FileNotFoundException(
+                    "Failed to stat mount point: " + mountPoint.getParent());
+        }
+        return ParcelFileDescriptor.open(new File(mountPoint, String.valueOf(fileId)), mode);
+    }
+
+    private static File getMountPoint(int uid, int mountId) {
+        return new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE,  uid, mountId));
+    }
+
+    @Override
+    public void close() {
+        IoUtils.closeQuietly(mMountScope);
+        IoUtils.closeQuietly(mProxyFd);
+        // Invoke countDown here in case where close is invoked before mount.
+        mChannel.offer(false);
     }
 
     // Used by com_android_server_storage_AppFuse.cpp.
     private void onMount() {
-        mMountLatch.countDown();
+        mChannel.offer(true);
+    }
+
+    public static interface IMountScope extends AutoCloseable {
+        ParcelFileDescriptor getDeviceFileDescriptor();
     }
 
     private native boolean native_start_loop(int deviceFd, int proxyFd);
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 90c711a..a3837b2 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -338,9 +338,11 @@
         mTotalMemory = (long)mDataFileStats.getBlockCount() *
                         mDataFileStats.getBlockSize();
         mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
-        mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
-        mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
         mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
diff --git a/services/core/java/com/android/server/storage/DiskStatsFileLogger.java b/services/core/java/com/android/server/storage/DiskStatsFileLogger.java
index 22299df..0094ab5 100644
--- a/services/core/java/com/android/server/storage/DiskStatsFileLogger.java
+++ b/services/core/java/com/android/server/storage/DiskStatsFileLogger.java
@@ -18,6 +18,7 @@
 
 import android.content.pm.PackageStats;
 import android.os.Environment;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -118,7 +119,7 @@
         long appSizeSum = 0L;
         long cacheSizeSum = 0L;
         boolean isExternal = Environment.isExternalStorageEmulated();
-        for (Map.Entry<String, PackageStats> entry : mergePackagesAcrossUsers().entrySet()) {
+        for (Map.Entry<String, PackageStats> entry : filterOnlyPrimaryUser().entrySet()) {
             PackageStats stat = entry.getValue();
             long appSize = stat.codeSize + stat.dataSize;
             long cacheSize = stat.cacheSize;
@@ -141,14 +142,17 @@
     }
 
     /**
-     * A given package may exist for multiple users with distinct sizes. This function merges
-     * the duplicated packages together and sums up their sizes to get the actual totals for the
-     * package.
+     * A given package may exist for multiple users with distinct sizes. This function filters
+     * the packages that do not belong to user 0 out to ensure that we get good stats for a subset.
      * @return A mapping of package name to merged package stats.
      */
-    private ArrayMap<String, PackageStats> mergePackagesAcrossUsers() {
+    private ArrayMap<String, PackageStats> filterOnlyPrimaryUser() {
         ArrayMap<String, PackageStats> packageMap = new ArrayMap<>();
         for (PackageStats stat : mPackageStats) {
+            if (stat.userHandle != UserHandle.USER_SYSTEM) {
+                continue;
+            }
+
             PackageStats existingStats = packageMap.get(stat.packageName);
             if (existingStats != null) {
                 existingStats.cacheSize += stat.cacheSize;
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index e7c5384..6c1648c 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -321,6 +321,19 @@
     }
 
     /**
+     * @see android.service.trust.TrustAgentService#onUnlockLockout(int)
+     */
+    public void onUnlockLockout(int timeoutMs) {
+        try {
+            if (mTrustAgentService != null) {
+                mTrustAgentService.onUnlockLockout(timeoutMs);
+            }
+        } catch (RemoteException e) {
+            onError(e);
+        }
+    }
+
+    /**
      * @see android.service.trust.TrustAgentService#onDeviceLocked()
      */
     public void onDeviceLocked() {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index c69b87c..71b725e 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -105,6 +105,7 @@
     private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 10;
     private static final int MSG_UNLOCK_USER = 11;
     private static final int MSG_STOP_USER = 12;
+    private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
 
     private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
 
@@ -335,13 +336,16 @@
 
                 if (!mStrongAuthTracker.canAgentsRunForUser(userInfo.id)) {
                     int flag = mStrongAuthTracker.getStrongAuthForUser(userInfo.id);
-                    if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
-                        || !directUnlock) {
-                        if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                            + ": prevented by StrongAuthTracker = 0x"
-                            + Integer.toHexString(mStrongAuthTracker.getStrongAuthForUser(
-                            userInfo.id)));
-                        continue;
+                    if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) {
+                        if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+                            || !directUnlock) {
+                            if (DEBUG)
+                                Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+                                    + ": prevented by StrongAuthTracker = 0x"
+                                    + Integer.toHexString(mStrongAuthTracker.getStrongAuthForUser(
+                                    userInfo.id)));
+                            continue;
+                        }
                     }
                 }
 
@@ -650,6 +654,15 @@
         }
     }
 
+    private void dispatchUnlockLockout(int timeoutMs, int userId) {
+        for (int i = 0; i < mActiveAgents.size(); i++) {
+            AgentInfo info = mActiveAgents.valueAt(i);
+            if (info.userId == userId) {
+                info.agent.onUnlockLockout(timeoutMs);
+            }
+        }
+    }
+
     // Listeners
 
     private void addListener(ITrustListener listener) {
@@ -745,6 +758,13 @@
         }
 
         @Override
+        public void reportUnlockLockout(int timeoutMs, int userId) throws RemoteException {
+            enforceReportPermission();
+            mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_LOCKOUT, timeoutMs, userId)
+                    .sendToTarget();
+        }
+
+        @Override
         public void reportEnabledTrustAgentsChanged(int userId) throws RemoteException {
             enforceReportPermission();
             // coalesce refresh messages.
@@ -975,6 +995,9 @@
                 case MSG_DISPATCH_UNLOCK_ATTEMPT:
                     dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
                     break;
+                case MSG_DISPATCH_UNLOCK_LOCKOUT:
+                    dispatchUnlockLockout(msg.arg1, msg.arg2);
+                    break;
                 case MSG_ENABLED_AGENTS_CHANGED:
                     refreshAgentList(UserHandle.USER_ALL);
                     // This is also called when the security mode of a user changes.
diff --git a/services/core/java/com/android/server/vr/CompatibilityDisplay.java b/services/core/java/com/android/server/vr/CompatibilityDisplay.java
new file mode 100644
index 0000000..15edaaf
--- /dev/null
+++ b/services/core/java/com/android/server/vr/CompatibilityDisplay.java
@@ -0,0 +1,119 @@
+
+package com.android.server.vr;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.vr.IVrStateCallbacks;
+import android.service.vr.IVrManager;
+import android.util.Log;
+
+import com.android.server.vr.VrManagerService;
+
+/**
+ * Creates a 2D Virtual Display while VR Mode is enabled. This display will be used to run and
+ * render 2D app within a VR experience. For example, bringing up the 2D calculator app in VR.
+ */
+class CompatibilityDisplay {
+    private final static String TAG = "CompatDisplay";
+    private final static boolean DEBUG = false;
+
+    // TODO: Go over these values and figure out what is best
+    private final static int HEIGHT = 960;
+    private final static int WIDTH = 720;
+    private final static int DPI = 320;
+
+    private final DisplayManager mDisplayManager;
+    private final IVrManager mVrManager;
+
+    // TODO: Lock initially created when VrStateCallback was connected through Binder. This may not
+    // be necessary with the direct access to VrManager.
+    private final Object vdLock = new Object();
+
+    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+        @Override
+        public void onVrStateChanged(boolean enabled) {
+            if (enabled != mIsVrModeEnabled) {
+                mIsVrModeEnabled = enabled;
+                if (enabled) {
+                    // TODO: Consider not creating the display until ActivityManager needs one on
+                    // which to display a 2D application.
+                    startVirtualDisplay();
+                } else {
+                    stopVirtualDisplay();
+                }
+            }
+        }
+    };
+
+    private VirtualDisplay mVirtualDisplay;
+    private boolean mIsVrModeEnabled;
+
+    public CompatibilityDisplay(DisplayManager displayManager, IVrManager vrManager) {
+        mDisplayManager = displayManager;
+        mVrManager = vrManager;
+    }
+
+    /**
+     * Initializes the compabilitiy display by listening to VR mode changes.
+     */
+    public void init() {
+        startVrModeListener();
+    }
+
+    private void startVrModeListener() {
+        if (mVrManager != null) {
+            try {
+                mVrManager.registerListener(mVrStateCallbacks);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not register VR State listener.", e);
+            }
+        }
+    }
+
+    private void startVirtualDisplay() {
+        if (DEBUG) {
+            Log.d(TAG, "Starting VD, DM:" + mDisplayManager);
+        }
+
+        if (mDisplayManager == null) {
+            Log.w(TAG, "Cannot create virtual display because mDisplayManager == null");
+            return;
+        }
+
+        synchronized (vdLock) {
+            if (mVirtualDisplay != null) {
+                Log.e(TAG, "Starting the virtual display when one already exists", new Exception());
+                return;
+            }
+
+            mVirtualDisplay = mDisplayManager.createVirtualDisplay("VR 2D Display", WIDTH, HEIGHT,
+                    DPI,
+                    null /* Surface */, 0 /* flags */);
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "VD created: " + mVirtualDisplay);
+        }
+    }
+
+    private void stopVirtualDisplay() {
+        if (DEBUG) {
+            Log.i(TAG, "Santos, stopping VD");
+        }
+
+        synchronized (vdLock) {
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.release();
+                mVirtualDisplay = null;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 84cf0c6..e8385fc 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -31,6 +31,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -51,6 +52,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
+
 import com.android.internal.R;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
@@ -86,9 +88,9 @@
  * {@link android.app.Activity#setVrModeEnabled)}.  An application may also implement a service to
  * be run while in VR mode by implementing {@link android.service.vr.VrListenerService}.
  *
- * @see {@link android.service.vr.VrListenerService}
- * @see {@link com.android.server.vr.VrManagerInternal}
- * @see {@link com.android.server.vr.VrStateListener}
+ * @see android.service.vr.VrListenerService
+ * @see com.android.server.vr.VrManagerInternal
+ * @see com.android.server.vr.VrStateListener
  *
  * @hide
  */
@@ -136,6 +138,7 @@
     private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
     /** Tracks the state of the screen and keyguard UI.*/
     private int mSystemSleepFlags = FLAG_NONE;
+    private CompatibilityDisplay mCompatibilityDisplay;
 
     private static final int MSG_VR_STATE_CHANGE = 0;
     private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
@@ -493,6 +496,11 @@
 
                 mComponentObserver.rebuildAll();
             }
+
+            DisplayManager dm =
+                    (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
+            mCompatibilityDisplay = new CompatibilityDisplay(dm, mVrManager);
+            mCompatibilityDisplay.init();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             synchronized (mLock) {
                 mVrModeAllowed = true;
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 7ba95a4..a0381f7 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -35,6 +35,7 @@
 import android.provider.Settings;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
+import android.webkit.UserPackage;
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewZygote;
@@ -271,21 +272,31 @@
     }
 
     @Override
-    public void setMultiProcessEnabledFromContext(Context context) {
-        boolean enableMultiProcess = false;
-        try {
-            enableMultiProcess = Settings.Global.getInt(context.getContentResolver(),
-                    Settings.Global.WEBVIEW_MULTIPROCESS) == 1;
-        } catch (Settings.SettingNotFoundException ex) {
-        }
+    public List<UserPackage> getPackageInfoForProviderAllUsers(Context context,
+            WebViewProviderInfo configInfo) {
+        return UserPackage.getPackageInfosAllUsers(context, configInfo.packageName, PACKAGE_FLAGS);
+    }
+
+    @Override
+    public int getMultiProcessSetting(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                                      Settings.Global.WEBVIEW_MULTIPROCESS, 0);
+    }
+
+    @Override
+    public void setMultiProcessSetting(Context context, int value) {
+        Settings.Global.putInt(context.getContentResolver(),
+                               Settings.Global.WEBVIEW_MULTIPROCESS, value);
+    }
+
+    @Override
+    public void notifyZygote(boolean enableMultiProcess) {
         WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
     }
 
     @Override
-    public void registerContentObserver(Context context, ContentObserver contentObserver) {
-        context.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.WEBVIEW_MULTIPROCESS),
-                false, contentObserver);
+    public boolean isMultiProcessDefaultEnabled() {
+        return true;
     }
 
     // flags declaring we want extra info from the package manager for webview providers
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 2d7a998..d57edcd 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -20,8 +20,11 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.ContentObserver;
+import android.webkit.UserPackage;
 import android.webkit.WebViewProviderInfo;
 
+import java.util.List;
+
 /**
  * System interface for the WebViewUpdateService.
  * This interface provides a way to test the WebView preparation mechanism - during normal use this
@@ -49,7 +52,17 @@
     public boolean systemIsDebuggable();
     public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
             throws NameNotFoundException;
+    /**
+     * Get the PackageInfos of all users for the package represented by {@param configInfo}.
+     * @return an array of UserPackages for a certain package, each UserPackage being belonging to a
+     *         certain user. The returned array can contain null PackageInfos if the given package
+     *         is uninstalled for some user.
+     */
+    public List<UserPackage> getPackageInfoForProviderAllUsers(Context context,
+            WebViewProviderInfo configInfo);
 
-    public void setMultiProcessEnabledFromContext(Context context);
-    public void registerContentObserver(Context context, ContentObserver contentObserver);
+    public int getMultiProcessSetting(Context context);
+    public void setMultiProcessSetting(Context context, int value);
+    public void notifyZygote(boolean enableMultiProcess);
+    public boolean isMultiProcessDefaultEnabled();
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 0a7454f..4a105e1 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -94,6 +94,9 @@
                         case Intent.ACTION_USER_ADDED:
                             mImpl.handleNewUser(userId);
                             break;
+                        case Intent.ACTION_USER_REMOVED:
+                            mImpl.handleUserRemoved(userId);
+                            break;
                     }
                 }
         };
@@ -112,6 +115,7 @@
 
         IntentFilter userAddedFilter = new IntentFilter();
         userAddedFilter.addAction(Intent.ACTION_USER_ADDED);
+        userAddedFilter.addAction(Intent.ACTION_USER_REMOVED);
         getContext().registerReceiverAsUser(mWebViewUpdatedReceiver, UserHandle.ALL,
                 userAddedFilter, null /* broadcast permission */, null /* handler */);
 
@@ -261,6 +265,32 @@
             }
         }
 
+        @Override // Binder call
+        public boolean isMultiProcessEnabled() {
+            return WebViewUpdateService.this.mImpl.isMultiProcessEnabled();
+        }
+
+        @Override // Binder call
+        public void enableMultiProcess(boolean enable) {
+            if (getContext().checkCallingPermission(
+                        android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                String msg = "Permission Denial: enableMultiProcess() from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid()
+                        + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+
+            long callingId = Binder.clearCallingIdentity();
+            try {
+                WebViewUpdateService.this.mImpl.enableMultiProcess(enable);
+            } finally {
+                Binder.restoreCallingIdentity(callingId);
+            }
+        }
+
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 1a77c68..fedd55a9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -20,12 +20,11 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
-import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.util.Base64;
 import android.util.Slog;
+import android.webkit.UserPackage;
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewProviderResponse;
@@ -77,7 +76,6 @@
 
     private SystemInterface mSystemInterface;
     private WebViewUpdater mWebViewUpdater;
-    private SettingsObserver mSettingsObserver;
     final private Context mContext;
 
     public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
@@ -97,41 +95,47 @@
     void prepareWebViewInSystemServer() {
         updateFallbackStateOnBoot();
         mWebViewUpdater.prepareWebViewInSystemServer();
-
-        // Register for changes in the multiprocess developer option. This has to be done
-        // here, since the update service gets created before the ContentResolver service.
-        mSettingsObserver = new SettingsObserver();
+        mSystemInterface.notifyZygote(isMultiProcessEnabled());
     }
 
     private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
         for (WebViewProviderInfo provider : providers) {
             if (provider.availableByDefault && !provider.isFallback) {
-                try {
-                    PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
-                    if (isInstalledPackage(packageInfo) && isEnabledPackage(packageInfo)
-                            && mWebViewUpdater.isValidProvider(provider, packageInfo)) {
-                        return true;
-                    }
-                } catch (NameNotFoundException e) {
-                    // A non-existent provider is neither valid nor enabled
+                // userPackages can contain null objects.
+                List<UserPackage> userPackages =
+                        mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
+                if (isInstalledAndEnabledForAllUsers(userPackages) &&
+                        // Checking validity of the package for the system user (rather than all
+                        // users) since package validity depends not on the user but on the package
+                        // itself.
+                        mWebViewUpdater.isValidProvider(provider,
+                                userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo())) {
+                    return true;
                 }
             }
         }
         return false;
     }
 
-    /**
-     * Called when a new user has been added to update the state of its fallback package.
-     */
     void handleNewUser(int userId) {
-        if (!mSystemInterface.isFallbackLogicEnabled()) return;
+        handleUserChange();
+    }
 
-        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
-        if (fallbackProvider == null) return;
+    void handleUserRemoved(int userId) {
+        handleUserChange();
+    }
 
-        mSystemInterface.enablePackageForUser(fallbackProvider.packageName,
-                !existsValidNonFallbackProvider(webviewProviders), userId);
+    /**
+     * Called when a user was added or removed to ensure fallback logic and WebView preparation are
+     * triggered. This has to be done since the WebView package we use depends on the enabled-state
+     * of packages for all users (so adding or removing a user might cause us to change package).
+     */
+    private void handleUserChange() {
+        if (mSystemInterface.isFallbackLogicEnabled()) {
+            updateFallbackState(mSystemInterface.getWebViewPackages());
+        }
+        // Potentially trigger package-changing logic.
+        mWebViewUpdater.updateCurrentWebViewPackage(null);
     }
 
     void notifyRelroCreationCompleted() {
@@ -147,7 +151,7 @@
     }
 
     WebViewProviderInfo[] getValidWebViewPackages() {
-        return mWebViewUpdater.getValidAndInstalledWebViewPackages();
+        return mWebViewUpdater.getValidWebViewPackages();
     }
 
     WebViewProviderInfo[] getWebViewPackages() {
@@ -166,7 +170,7 @@
         if (!mSystemInterface.isFallbackLogicEnabled()) return;
 
         WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-        updateFallbackState(webviewProviders, true);
+        updateFallbackState(webviewProviders);
     }
 
     /**
@@ -191,35 +195,23 @@
             }
         }
         if (!changedPackageAvailableByDefault) return;
-        updateFallbackState(webviewProviders, false);
+        updateFallbackState(webviewProviders);
     }
 
-    private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) {
+    private void updateFallbackState(WebViewProviderInfo[] webviewProviders) {
         // If there exists a valid and enabled non-fallback package - disable the fallback
         // package, otherwise, enable it.
         WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
         if (fallbackProvider == null) return;
         boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
 
-        boolean isFallbackEnabled = false;
-        try {
-            isFallbackEnabled = isEnabledPackage(
-                    mSystemInterface.getPackageInfoForProvider(fallbackProvider));
-        } catch (NameNotFoundException e) {
-            // No fallback package installed -> early out.
-            return;
-        }
-
-        if (existsValidNonFallbackProvider
-                // During an OTA the primary user's WebView state might differ from other users', so
-                // ignore the state of that user during boot.
-                && (isFallbackEnabled || isBoot)) {
+        List<UserPackage> userPackages =
+                mSystemInterface.getPackageInfoForProviderAllUsers(mContext, fallbackProvider);
+        if (existsValidNonFallbackProvider && !isDisabledForAllUsers(userPackages)) {
             mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext,
                     fallbackProvider.packageName);
         } else if (!existsValidNonFallbackProvider
-                // During an OTA the primary user's WebView state might differ from other users', so
-                // ignore the state of that user during boot.
-                && (!isFallbackEnabled || isBoot)) {
+                && !isInstalledAndEnabledForAllUsers(userPackages)) {
             // Enable the fallback package for all users.
             mSystemInterface.enablePackageForAllUsers(mContext,
                     fallbackProvider.packageName, true);
@@ -247,6 +239,38 @@
                 && packageName.equals(fallbackProvider.packageName));
     }
 
+    boolean isMultiProcessEnabled() {
+        PackageInfo current = getCurrentWebViewPackage();
+        if (current == null) return false;
+        int currentVersion = current.versionCode;
+        int settingValue = mSystemInterface.getMultiProcessSetting(mContext);
+        if (mSystemInterface.isMultiProcessDefaultEnabled()) {
+            // Multiprocess should be enabled unless the user has turned it off manually for this
+            // version or newer, as we want to re-enable it when it's updated to get fresh
+            // bug reports.
+            return settingValue > -currentVersion;
+        } else {
+            // Multiprocess should not be enabled, unless the user has turned it on manually for
+            // any version.
+            return settingValue > 0;
+        }
+    }
+
+    void enableMultiProcess(boolean enable) {
+        // The value we store for the setting is the version code of the current package, if it's
+        // enabled, or the negation of the version code of the current package, if it's disabled.
+        // Users who have a setting from before this scheme was implemented will have it set to 0 or
+        // 1 instead.
+        PackageInfo current = getCurrentWebViewPackage();
+        int currentVersion = current != null ? current.versionCode : 1;
+        mSystemInterface.setMultiProcessSetting(mContext,
+                                                enable ? currentVersion : -currentVersion);
+        mSystemInterface.notifyZygote(enable);
+        if (current != null) {
+            mSystemInterface.killPackageDependents(current.packageName);
+        }
+    }
+
     /**
      * Class that decides what WebView implementation to use and prepares that implementation for
      * use.
@@ -369,38 +393,8 @@
          * or the replacement are done).
          */
         public String changeProviderAndSetting(String newProviderName) {
-            PackageInfo oldPackage = null;
-            PackageInfo newPackage = null;
-            boolean providerChanged = false;
-            synchronized(mLock) {
-                oldPackage = mCurrentWebViewPackage;
-                mSystemInterface.updateUserSetting(mContext, newProviderName);
-
-                try {
-                    newPackage = findPreferredWebViewPackage();
-                    providerChanged = (oldPackage == null)
-                            || !newPackage.packageName.equals(oldPackage.packageName);
-                } catch (WebViewPackageMissingException e) {
-                    mCurrentWebViewPackage = null;
-                    Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
-                            "package " + e);
-                    // If we don't perform the user change but don't have an installed WebView
-                    // package, we will have changed the setting and it will be used when a package
-                    // is available.
-                    return "";
-                }
-                // Perform the provider change if we chose a new provider
-                if (providerChanged) {
-                    onWebViewProviderChanged(newPackage);
-                }
-            }
-            // Kill apps using the old provider only if we changed provider
-            if (providerChanged && oldPackage != null) {
-                mSystemInterface.killPackageDependents(oldPackage.packageName);
-            }
-            // Return the new provider, this is not necessarily the one we were asked to switch to
-            // But the persistent setting will now be pointing to the provider we were asked to
-            // switch to anyway
+            PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
+            if (newPackage == null) return "";
             return newPackage.packageName;
         }
 
@@ -430,15 +424,14 @@
             }
         }
 
-        private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos(boolean onlyInstalled) {
+        private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
             WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
             List<ProviderAndPackageInfo> providers = new ArrayList<>();
             for(int n = 0; n < allProviders.length; n++) {
                 try {
                     PackageInfo packageInfo =
                         mSystemInterface.getPackageInfoForProvider(allProviders[n]);
-                    if ((!onlyInstalled || isInstalledPackage(packageInfo))
-                            && isValidProvider(allProviders[n], packageInfo)) {
+                    if (isValidProvider(allProviders[n], packageInfo)) {
                         providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
                     }
                 } catch (NameNotFoundException e) {
@@ -451,9 +444,8 @@
         /**
          * Fetch only the currently valid WebView packages.
          **/
-        public WebViewProviderInfo[] getValidAndInstalledWebViewPackages() {
-            ProviderAndPackageInfo[] providersAndPackageInfos =
-                getValidWebViewPackagesAndInfos(true /* only fetch installed packages */);
+        public WebViewProviderInfo[] getValidWebViewPackages() {
+            ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
             WebViewProviderInfo[] providers =
                 new WebViewProviderInfo[providersAndPackageInfos.length];
             for(int n = 0; n < providersAndPackageInfos.length; n++) {
@@ -480,39 +472,49 @@
          *
          */
         private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
-            ProviderAndPackageInfo[] providers =
-                getValidWebViewPackagesAndInfos(false /* onlyInstalled */);
+            ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
 
             String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
 
-            // If the user has chosen provider, use that
+            // If the user has chosen provider, use that (if it's installed and enabled for all
+            // users).
             for (ProviderAndPackageInfo providerAndPackage : providers) {
-                if (providerAndPackage.provider.packageName.equals(userChosenProvider)
-                        && isInstalledPackage(providerAndPackage.packageInfo)
-                        && isEnabledPackage(providerAndPackage.packageInfo)) {
-                    return providerAndPackage.packageInfo;
+                if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
+                    // userPackages can contain null objects.
+                    List<UserPackage> userPackages =
+                            mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+                                    providerAndPackage.provider);
+                    if (isInstalledAndEnabledForAllUsers(userPackages)) {
+                        return providerAndPackage.packageInfo;
+                    }
                 }
             }
 
             // User did not choose, or the choice failed; use the most stable provider that is
-            // installed and enabled for the device owner, and available by default (not through
+            // installed and enabled for all users, and available by default (not through
             // user choice).
             for (ProviderAndPackageInfo providerAndPackage : providers) {
-                if (providerAndPackage.provider.availableByDefault
-                        && isInstalledPackage(providerAndPackage.packageInfo)
-                        && isEnabledPackage(providerAndPackage.packageInfo)) {
-                    return providerAndPackage.packageInfo;
+                if (providerAndPackage.provider.availableByDefault) {
+                    // userPackages can contain null objects.
+                    List<UserPackage> userPackages =
+                            mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+                                    providerAndPackage.provider);
+                    if (isInstalledAndEnabledForAllUsers(userPackages)) {
+                        return providerAndPackage.packageInfo;
+                    }
                 }
             }
 
             // Could not find any installed and enabled package either, use the most stable and
             // default-available provider.
+            // TODO(gsennton) remove this when we have a functional WebView stub.
             for (ProviderAndPackageInfo providerAndPackage : providers) {
                 if (providerAndPackage.provider.availableByDefault) {
                     return providerAndPackage.packageInfo;
                 }
             }
 
+            // This should never happen during normal operation (only with modified system images).
             mAnyWebViewInstalled = false;
             throw new WebViewPackageMissingException("Could not find a loadable WebView package");
         }
@@ -695,6 +697,48 @@
                         mAnyWebViewInstalled));
             }
         }
+
+        /**
+         * Update the current WebView package.
+         * @param newProviderName the package to switch to, null if no package has been explicitly
+         * chosen.
+         */
+        public PackageInfo updateCurrentWebViewPackage(String newProviderName) {
+            PackageInfo oldPackage = null;
+            PackageInfo newPackage = null;
+            boolean providerChanged = false;
+            synchronized(mLock) {
+                oldPackage = mCurrentWebViewPackage;
+
+                if (newProviderName != null) {
+                    mSystemInterface.updateUserSetting(mContext, newProviderName);
+                }
+
+                try {
+                    newPackage = findPreferredWebViewPackage();
+                    providerChanged = (oldPackage == null)
+                            || !newPackage.packageName.equals(oldPackage.packageName);
+                } catch (WebViewPackageMissingException e) {
+                    // If updated the Setting but don't have an installed WebView package, the
+                    // Setting will be used when a package is available.
+                    mCurrentWebViewPackage = null;
+                    Slog.e(TAG, "Couldn't find WebView package to use " + e);
+                    return null;
+                }
+                // Perform the provider change if we chose a new provider
+                if (providerChanged) {
+                    onWebViewProviderChanged(newPackage);
+                }
+            }
+            // Kill apps using the old provider only if we changed provider
+            if (providerChanged && oldPackage != null) {
+                mSystemInterface.killPackageDependents(oldPackage.packageName);
+            }
+            // Return the new provider, this is not necessarily the one we were asked to switch to,
+            // but the persistent setting will now be pointing to the provider we were asked to
+            // switch to anyway.
+            return newPackage;
+        }
     }
 
     private static boolean providerHasValidSignature(WebViewProviderInfo provider,
@@ -723,45 +767,27 @@
     }
 
     /**
-     * Returns whether the given package is enabled.
-     * This state can be changed by the user from Settings->Apps
+     * Return true iff {@param packageInfos} point to only installed and enabled packages.
+     * The given packages {@param packageInfos} should all be pointing to the same package, but each
+     * PackageInfo representing a different user's package.
      */
-    private static boolean isEnabledPackage(PackageInfo packageInfo) {
-        return packageInfo.applicationInfo.enabled;
+    private static boolean isInstalledAndEnabledForAllUsers(
+            List<UserPackage> userPackages) {
+        for (UserPackage userPackage : userPackages) {
+            if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
+                return false;
+            }
+        }
+        return true;
     }
 
-    /**
-     * Return true if the package is installed and not hidden
-     */
-    private static boolean isInstalledPackage(PackageInfo packageInfo) {
-        return (((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0)
-            && ((packageInfo.applicationInfo.privateFlags
-                        & ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
-    }
-
-    /**
-     * Watches for changes in the WEBVIEW_MULTIPROCESS setting and lets
-     * the WebViewZygote know, so it can start or stop the zygote process
-     * appropriately.
-     */
-    private class SettingsObserver extends ContentObserver {
-        SettingsObserver() {
-            super(new Handler());
-
-            mSystemInterface.registerContentObserver(mContext, this);
-
-            // Push the current value of the setting immediately.
-            notifyZygote();
+    private static boolean isDisabledForAllUsers(List<UserPackage> userPackages) {
+        for (UserPackage userPackage : userPackages) {
+            if (userPackage.getPackageInfo() != null && userPackage.isEnabledPackage()) {
+                return false;
+            }
         }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            notifyZygote();
-        }
-
-        private void notifyZygote() {
-            mSystemInterface.setMultiProcessEnabledFromContext(mContext);
-        }
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index a900702..27e0f29 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -56,7 +56,7 @@
     private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
 
     private final IApplicationToken mToken;
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Handler mHandler;
 
     private final Runnable mOnWindowsDrawn = () -> {
         if (mListener == null) {
@@ -87,7 +87,6 @@
 
     private final Runnable mRemoveStartingWindow = () -> {
         StartingSurface surface = null;
-        StartingData data = null;
         synchronized (mWindowMap) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " + mContainer
                     + ": startingWindow=" + mContainer.startingWindow
@@ -97,14 +96,13 @@
             }
             if (mContainer.startingWindow != null) {
                 surface = mContainer.startingSurface;
-                data = mContainer.startingData;
                 mContainer.startingData = null;
                 mContainer.startingSurface = null;
                 mContainer.startingWindow = null;
                 mContainer.startingDisplayed = false;
             }
         }
-        if (data != null && surface != null) {
+        if (surface != null) {
             try {
                 surface.remove();
             } catch (Exception e) {
@@ -115,12 +113,14 @@
 
     private final Runnable mAddStartingWindow = () -> {
         final StartingData startingData;
+        final AppWindowToken container;
 
         synchronized (mWindowMap) {
             if (mContainer == null) {
                 return;
             }
             startingData = mContainer.startingData;
+            container = mContainer;
         }
 
         if (startingData == null) {
@@ -129,41 +129,40 @@
         }
 
         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
-                + this + ": startingData=" + mContainer.startingData);
+                + this + ": startingData=" + container.startingData);
 
         StartingSurface surface = null;
         try {
-            surface = startingData.createStartingSurface();
+            surface = startingData.createStartingSurface(container);
         } catch (Exception e) {
             Slog.w(TAG_WM, "Exception when adding starting window", e);
         }
         if (surface != null) {
             boolean abort = false;
             synchronized(mWindowMap) {
-                if (mContainer.removed || mContainer.startingData == null) {
+                if (container.removed || container.startingData == null) {
                     // If the window was successfully added, then
                     // we need to remove it.
-                    if (mContainer.startingWindow != null) {
+                    if (container.startingWindow != null) {
                         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                                "Aborted starting " + mContainer
-                                        + ": removed=" + mContainer.removed
-                                        + " startingData=" + mContainer.startingData);
+                                "Aborted starting " + container
+                                        + ": removed=" + container.removed
+                                        + " startingData=" + container.startingData);
+                        container.startingWindow = null;
+                        container.startingData = null;
                         abort = true;
                     }
                 } else {
-                    mContainer.startingSurface = surface;
+                    container.startingSurface = surface;
                 }
                 if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
                         "Added starting " + mContainer
                                 + ": startingWindow="
-                                + mContainer.startingWindow + " startingView="
-                                + mContainer.startingSurface);
+                                + container.startingWindow + " startingView="
+                                + container.startingSurface);
             }
             if (abort) {
-                mRemoveStartingWindow.run();
-            if (mContainer == null) {
-                return;
-            }
+                surface.remove();
             }
         }
     };
@@ -187,6 +186,7 @@
             int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
             WindowManagerService service) {
         super(listener, service);
+        mHandler = new Handler(service.mH.getLooper());
         mToken = token;
         synchronized(mWindowMap) {
             AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
@@ -465,7 +465,7 @@
             }
 
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
-            mContainer.startingData = new SplashScreenStartingData(mService, mContainer, pkg, theme,
+            mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
                     compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                     mContainer.getMergedOverrideConfiguration());
             scheduleAddStartingWindow();
@@ -493,14 +493,13 @@
 
     private boolean createSnapshot() {
         final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
-                mContainer.mTask);
+                mContainer.mTask.mTaskId, mContainer.mTask.mUserId, false /* restoreFromDisk */);
 
         if (snapshot == null) {
             return false;
         }
 
-        mContainer.startingData = new SnapshotStartingData(mService, mContainer,
-                snapshot.getSnapshot());
+        mContainer.startingData = new SnapshotStartingData(mService, snapshot.getSnapshot());
         scheduleAddStartingWindow();
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 10d1d8b..079dc8f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -375,6 +375,7 @@
                 // affected.
                 mService.getDefaultDisplayContentLocked().getDockedDividerController()
                         .notifyAppVisibilityChanged();
+                mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
             }
         }
 
@@ -445,6 +446,7 @@
 
         mService.mOpeningApps.remove(this);
         mService.mUnknownAppVisibilityController.appRemoved(this);
+        mService.mTaskSnapshotController.onAppRemoved(this);
         waitingToShow = false;
         if (mService.mClosingApps.contains(this)) {
             delayed = true;
@@ -673,7 +675,7 @@
             // well there is no point now.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData");
             startingData = null;
-        } else if (mChildren.size() == 1 && startingSurface != null) {
+        } else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) {
             // If this is the last window except for a starting transition window,
             // we need to get rid of the starting transition.
             if (getController() != null) {
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index b99dda1..015c084 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -77,6 +77,8 @@
         boolean dimFullscreen();
         /** Returns the display info. of the dim layer user. */
         DisplayInfo getDisplayInfo();
+        /** Returns true if the dim layer user is currently attached to a display */
+        boolean isAttachedToDisplay();
         /** Gets the bounds of the dim layer user. */
         void getDimBounds(Rect outBounds);
         String toShortString();
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 04ae72f..2ec2dba 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -191,8 +191,21 @@
         boolean result = false;
 
         for (int i = mState.size() - 1; i >= 0; i--) {
-            DimLayer.DimLayerUser user = mState.keyAt(i);
-            DimLayerState state = mState.valueAt(i);
+            final DimLayer.DimLayerUser user = mState.keyAt(i);
+            final DimLayerState state = mState.valueAt(i);
+
+            if (!user.isAttachedToDisplay()) {
+                // Leaked dim user that is no longer attached to the display. Go ahead and clean it
+                // clean-up and log what happened.
+                // TODO: This is a work around for b/34395537 as the dim user should have cleaned-up
+                // it self when it was detached from the display. Need to investigate how the dim
+                // user is leaking...
+                Slog.wtfStack(TAG_WM, "Leaked dim user=" + user.toShortString()
+                        + " state=" + state);
+                removeDimLayerUser(user);
+                continue;
+            }
+
             // We have to check that we are actually the shared fullscreen layer
             // for this path. If we began as non fullscreen and became fullscreen
             // (e.g. Docked stack closing), then we may not be the shared layer
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 47003fa..3a74ded 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -57,6 +57,7 @@
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
@@ -69,6 +70,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
@@ -634,6 +636,13 @@
      */
     DisplayContent(Display display, WindowManagerService service,
             WindowLayersController layersController, WallpaperController wallpaperController) {
+
+        if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
+            throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+                    + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
+                    + " new=" + display);
+        }
+
         mDisplay = display;
         mDisplayId = display.getDisplayId();
         mLayersController = layersController;
@@ -957,75 +966,43 @@
         out.set(mContentRect);
     }
 
-    /**
-     * Adds the stack to this display.
-     * @see WindowManagerService#addStackToDisplay(int, int, boolean)
-     */
-    Rect addStackToDisplay(int stackId, boolean onTop) {
-        boolean attachedToDisplay = false;
-        TaskStack stack = mService.mStackIdToStack.get(stackId);
-        if (stack == null) {
-            if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
-                    + mDisplayId);
+    TaskStack addStackToDisplay(int stackId, boolean onTop) {
+        if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
+                + mDisplayId);
 
-            stack = getStackById(stackId);
-            if (stack != null) {
-                // It's already attached to the display...clear mDeferRemoval and move stack to
-                // appropriate z-order on display as needed.
-                stack.mDeferRemoval = false;
-                // We're not moving the display to front when we're adding stacks, only when
-                // requested to change the position of stack explicitly.
-                mTaskStackContainers.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, stack,
-                        false /* includingParents */);
-                attachedToDisplay = true;
-            } else {
-                stack = new TaskStack(mService, stackId);
-            }
-
-            mService.mStackIdToStack.put(stackId, stack);
-            if (stackId == DOCKED_STACK_ID) {
-                mDividerControllerLocked.notifyDockedStackExistsChanged(true);
-            }
+        TaskStack stack = getStackById(stackId);
+        if (stack != null) {
+            // It's already attached to the display...clear mDeferRemoval and move stack to
+            // appropriate z-order on display as needed.
+            stack.mDeferRemoval = false;
+            // We're not moving the display to front when we're adding stacks, only when
+            // requested to change the position of stack explicitly.
+            mTaskStackContainers.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, stack,
+                    false /* includingParents */);
         } else {
-            final DisplayContent currentDC = stack.getDisplayContent();
-            if (currentDC != null) {
-                throw new IllegalStateException("Trying to add stackId=" + stackId
-                        + "to displayId=" + mDisplayId + ", but it's already attached to displayId="
-                        + currentDC.getDisplayId());
-            }
-        }
-
-        if (!attachedToDisplay) {
+            stack = new TaskStack(mService, stackId);
             mTaskStackContainers.addStackToDisplay(stack, onTop);
         }
 
-        if (stack.getRawFullscreen()) {
-            return null;
+        if (stackId == DOCKED_STACK_ID) {
+            mDividerControllerLocked.notifyDockedStackExistsChanged(true);
         }
-        final Rect bounds = new Rect();
-        stack.getRawBounds(bounds);
-        return bounds;
+        return stack;
     }
 
-    /** Removes the stack from the display and prepares for changing the parent. */
-    private void removeStackFromDisplay(TaskStack stack) {
-        mTaskStackContainers.removeStackFromDisplay(stack);
-    }
-
-    /** Moves the stack to this display and returns the updated bounds. */
-    Rect moveStackToDisplay(TaskStack stack) {
-        final DisplayContent currentDisplayContent = stack.getDisplayContent();
-        if (currentDisplayContent == null) {
+    void moveStackToDisplay(TaskStack stack) {
+        final DisplayContent prevDc = stack.getDisplayContent();
+        if (prevDc == null) {
             throw new IllegalStateException("Trying to move stackId=" + stack.mStackId
                     + " which is not currently attached to any display");
         }
-        if (stack.getDisplayContent().getDisplayId() == mDisplayId) {
+        if (prevDc.getDisplayId() == mDisplayId) {
             throw new IllegalArgumentException("Trying to move stackId=" + stack.mStackId
                     + " to its current displayId=" + mDisplayId);
         }
 
-        currentDisplayContent.removeStackFromDisplay(stack);
-        return addStackToDisplay(stack.mStackId, true /* onTop */);
+        prevDc.mTaskStackContainers.removeStackFromDisplay(stack);
+        mTaskStackContainers.addStackToDisplay(stack, true /* onTop */);
     }
 
     @Override
@@ -1172,7 +1149,7 @@
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
             mDimLayerController.close();
-            if (mDisplayId == DEFAULT_DISPLAY) {
+            if (mDisplayId == DEFAULT_DISPLAY && mService.canDispatchPointerEvents()) {
                 mService.unregisterPointerEventListener(mTapDetector);
                 mService.unregisterPointerEventListener(mService.mMousePositionTracker);
             }
@@ -1250,7 +1227,7 @@
         final WindowState imeWin = mService.mInputMethodWindow;
         final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
                 && !mDividerControllerLocked.isImeHideRequested();
-        final boolean dockVisible = mService.isStackVisibleLocked(DOCKED_STACK_ID);
+        final boolean dockVisible = isStackVisible(DOCKED_STACK_ID);
         final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
         final int imeDockSide = (dockVisible && imeTargetStack != null) ?
                 imeTargetStack.getDockSide() : DOCKED_INVALID;
@@ -1437,11 +1414,17 @@
         return "Display " + mDisplayId + " name=\"" + mDisplayInfo.name + "\"";
     }
 
+    /** Checks if stack with provided id is visible on this display. */
+    boolean isStackVisible(int stackId) {
+        final TaskStack stack = getStackById(stackId);
+        return (stack != null && stack.isVisible());
+    }
+
     /**
      * @return The docked stack, but only if it is visible, and {@code null} otherwise.
      */
     TaskStack getDockedStackLocked() {
-        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack stack = getStackById(DOCKED_STACK_ID);
         return (stack != null && stack.isVisible()) ? stack : null;
     }
 
@@ -1450,7 +1433,7 @@
      * visible.
      */
     TaskStack getDockedStackIgnoringVisibility() {
-        return mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        return getStackById(DOCKED_STACK_ID);
     }
 
     /** Find the visible, touch-deliverable window under the given point */
@@ -1482,6 +1465,13 @@
 
     boolean canAddToastWindowForUid(int uid) {
         // We allow one toast window per UID being shown at a time.
+        // Also if the app is focused adding more than one toast at
+        // a time for better backwards compatibility.
+        final WindowState focusedWindowForUid = getWindow(w ->
+                w.mOwnerUid == uid && w.isFocused());
+        if (focusedWindowForUid != null) {
+            return true;
+        }
         final WindowState win = getWindow(w ->
                 w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == uid && !w.mPermanentlyHidden
                 && !w.mWindowRemovalAllowed);
@@ -1519,6 +1509,9 @@
         }
     }
 
+    // TODO: This should probably be called any time a visual change is made to the hierarchy like
+    // moving containers or resizing them. Need to investigate the best way to have it automatically
+    // happen so we don't run into issues with programmers forgetting to do it.
     void layoutAndAssignWindowLayersIfNeeded() {
         mService.mWindowsChanged = true;
         setLayoutNeeded();
@@ -2246,10 +2239,11 @@
                 }
 
                 // Don't include wallpaper in bounds calculation
-                if (includeDecor && !stackBounds.isEmpty()) {
-                    frame.set(stackBounds);
-                } else if (includeDecor) {
-                    mutableIncludeFullDisplay.value = true;
+                if (!mutableIncludeFullDisplay.value && includeDecor) {
+                    final TaskStack stack = w.getStack();
+                    if (stack != null) {
+                        stack.getBounds(frame);
+                    }
                 } else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) {
                     final Rect wf = w.mFrame;
                     final Rect cr = w.mContentInsets;
@@ -2268,7 +2262,7 @@
                 final boolean foundTargetWs =
                         (w.mAppToken != null && w.mAppToken.token == appToken)
                                 || (mScreenshotApplicationState.appWin != null && wallpaperOnly);
-                if (foundTargetWs && w.isDisplayedLw() && winAnim.getShown()) {
+                if (foundTargetWs && winAnim.getShown()) {
                     mScreenshotApplicationState.screenshotReady = true;
                 }
 
@@ -2427,6 +2421,27 @@
         }
     }
 
+    void setExitingTokensHasVisible(boolean hasVisible) {
+        for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
+            mExitingTokens.get(i).hasVisible = hasVisible;
+        }
+
+        // Initialize state of exiting applications.
+        mTaskStackContainers.setExitingTokensHasVisible(hasVisible);
+    }
+
+    void removeExistingTokensIfPossible() {
+        for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
+            final WindowToken token = mExitingTokens.get(i);
+            if (!token.hasVisible) {
+                mExitingTokens.remove(i);
+            }
+        }
+
+        // Time to remove any exiting applications?
+        mTaskStackContainers.removeExistingAppTokensIfPossible();
+    }
+
     static final class TaskForResizePointSearchResult {
         boolean searchDone;
         Task taskForResize;
@@ -2565,9 +2580,7 @@
                     : requestedPosition >= topChildPosition;
             int targetPosition = requestedPosition;
 
-            if (toTop
-                    && mService.isStackVisibleLocked(PINNED_STACK_ID)
-                    && stack.mStackId != PINNED_STACK_ID) {
+            if (toTop && isStackVisible(PINNED_STACK_ID) && stack.mStackId != PINNED_STACK_ID) {
                 // The pinned stack is always the top most stack (always-on-top) when it is visible.
                 TaskStack topStack = mChildren.get(topChildPosition);
                 if (topStack.mStackId != PINNED_STACK_ID) {
@@ -2577,8 +2590,8 @@
                 // So, stack is moved just below the pinned stack.
                 // When we're adding a new stack the target is the current pinned stack position.
                 // When we're positioning an existing stack the target is the position below pinned
-                // stack, because WindowContainer#positionAt() first removes element and then adds it
-                // to specified place.
+                // stack, because WindowContainer#positionAt() first removes element and then adds
+                // it to specified place.
                 targetPosition = adding ? topChildPosition : topChildPosition - 1;
             }
 
@@ -2638,10 +2651,38 @@
             return false;
         }
 
+        void setExitingTokensHasVisible(boolean hasVisible) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
+                for (int j = appTokens.size() - 1; j >= 0; --j) {
+                    appTokens.get(j).hasVisible = hasVisible;
+                }
+            }
+        }
+
+        void removeExistingAppTokensIfPossible() {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
+                for (int j = appTokens.size() - 1; j >= 0; --j) {
+                    final AppWindowToken token = appTokens.get(j);
+                    if (!token.hasVisible && !mService.mClosingApps.contains(token)
+                            && (!token.mIsExiting || token.isEmpty())) {
+                        // Make sure there is no animation running on this token, so any windows
+                        // associated with it will be removed as soon as their animations are
+                        // complete.
+                        token.mAppAnimator.clearAnimation();
+                        token.mAppAnimator.animating = false;
+                        if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
+                                "performLayout: App token exiting now removed" + token);
+                        token.removeIfPossible();
+                    }
+                }
+            }
+        }
+
         @Override
         int getOrientation() {
-            if (mService.isStackVisibleLocked(DOCKED_STACK_ID)
-                    || mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
+            if (isStackVisible(DOCKED_STACK_ID) || isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
                 // Apps and their containers are not allowed to specify an orientation while the
                 // docked or freeform stack is visible...except for the home stack/task if the
                 // docked stack is minimized and it actually set something.
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index e6bc7f4..aaf724e 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -457,8 +457,7 @@
     void registerDockedStackListener(IDockedStackListener listener) {
         mDockedStackListeners.register(listener);
         notifyDockedDividerVisibilityChanged(wasVisible());
-        notifyDockedStackExistsChanged(
-                mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
+        notifyDockedStackExistsChanged(mDisplayContent.getDockedStackIgnoringVisibility() != null);
         notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
         notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
 
@@ -466,7 +465,7 @@
 
     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
         mService.openSurfaceTransaction();
-        final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
+        final TaskStack stack = mDisplayContent.getStackById(targetStackId);
         final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
         boolean visibleAndValid = visible && stack != null && dockedStack != null;
         if (visibleAndValid) {
@@ -544,8 +543,8 @@
         if (homeTask == null || !isWithinDisplay(homeTask)) {
             return;
         }
-        final TaskStack fullscreenStack
-                = mService.mStackIdToStack.get(FULLSCREEN_WORKSPACE_STACK_ID);
+        final TaskStack fullscreenStack =
+                mDisplayContent.getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
         final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
         final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisible())
                 || (homeStack.hasMultipleTaskWithHomeTaskNotTop());
@@ -739,7 +738,7 @@
     }
 
     private boolean animateForMinimizedDockedStack(long now) {
-        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
         if (!mAnimationStarted) {
             mAnimationStarted = true;
             mAnimationStartTime = now;
@@ -835,6 +834,11 @@
     }
 
     @Override
+    public boolean isAttachedToDisplay() {
+        return mDisplayContent != null;
+    }
+
+    @Override
     public void getDimBounds(Rect outBounds) {
         // This dim layer user doesn't need this.
     }
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 36520a9..1ae987f 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -148,7 +148,7 @@
             mDragApplicationHandle.dispatchingTimeoutNanos =
                     WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
-            mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
+            mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
                     display.getDisplayId());
             mDragWindowHandle.name = "drag";
             mDragWindowHandle.inputChannel = mServerChannel;
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 24783bc..b92bfb9 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -48,7 +48,8 @@
         mApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
-        mWindowHandle = new InputWindowHandle(mApplicationHandle, null, Display.DEFAULT_DISPLAY);
+        mWindowHandle = new InputWindowHandle(mApplicationHandle, null, null,
+                Display.DEFAULT_DISPLAY);
         mWindowHandle.name = name;
         mWindowHandle.inputChannel = mServerChannel;
         mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f754775..e039583 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -77,6 +77,8 @@
     // Array of window handles to provide to the input dispatcher.
     private InputWindowHandle[] mInputWindowHandles;
     private int mInputWindowHandleCount;
+    private InputWindowHandle mFocusedInputWindowHandle;
+
     private boolean mAddInputConsumerHandle;
     private boolean mAddPipInputConsumerHandle;
     private boolean mAddWallpaperInputConsumerHandle;
@@ -327,12 +329,16 @@
                     + child + ", " + inputWindowHandle);
         }
         addInputWindowHandle(inputWindowHandle);
+        if (hasFocus) {
+            mFocusedInputWindowHandle = inputWindowHandle;
+        }
     }
 
     private void clearInputWindowHandlesLw() {
         while (mInputWindowHandleCount != 0) {
             mInputWindowHandles[--mInputWindowHandleCount] = null;
         }
+        mFocusedInputWindowHandle = null;
     }
 
     void setUpdateInputWindowsNeededLw() {
@@ -609,7 +615,7 @@
             }
 
             // Send windows to native code.
-            mService.mInputManager.setInputWindows(mInputWindowHandles);
+            mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle);
 
             clearInputWindowHandlesLw();
         }
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 34633c2..08f9b45 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -82,6 +82,7 @@
 
     // Used to calculate stack bounds across rotations
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
+    private final Rect mStableInsets = new Rect();
 
     // The size and position information that describes where the pinned stack will go by default.
     private int mDefaultStackGravity;
@@ -250,10 +251,12 @@
     }
 
     /**
-     * @return the repositioned PIP bounds given it's pre-change bounds, and the new display info.
+     * @return the repositioned PIP bounds given it's pre-change bounds, and the new display
+     *         content.
      */
-    Rect onDisplayChanged(Rect preChangeStackBounds, DisplayInfo displayInfo) {
+    Rect onDisplayChanged(Rect preChangeStackBounds, DisplayContent displayContent) {
         final Rect postChangeStackBounds = new Rect(preChangeStackBounds);
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         if (!mDisplayInfo.equals(displayInfo)) {
             // Calculate the snap fraction of the current stack along the old movement bounds, and
             // then update the stack bounds to the same fraction along the rotated movement bounds.
@@ -269,8 +272,9 @@
             if (mIsMinimized) {
                 final Point displaySize = new Point(mDisplayInfo.logicalWidth,
                         mDisplayInfo.logicalHeight);
+                mService.getStableInsetsLocked(displayContent.getDisplayId(), mStableInsets);
                 mSnapAlgorithm.applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds,
-                        displaySize);
+                        displaySize, mStableInsets);
             }
         }
         return postChangeStackBounds;
@@ -329,7 +333,9 @@
      */
     void setActions(List<RemoteAction> actions) {
         mActions.clear();
-        mActions.addAll(actions);
+        if (actions != null) {
+            mActions.addAll(actions);
+        }
         notifyActionsChanged(mActions);
     }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 0cc6c70..22abf30 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -382,6 +382,17 @@
         }
     }
 
+    TaskStack getStackById(int stackId) {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final DisplayContent dc = mChildren.get(i);
+            final TaskStack stack = dc.getStackById(stackId);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
     void setSecureSurfaceState(int userId, boolean disabled) {
         forAllWindows((w) -> {
             if (w.mHasSurface && userId == UserHandle.getUserId(w.mOwnerUid)) {
@@ -535,18 +546,7 @@
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
-            for (i = displayContent.mExitingTokens.size() - 1; i >= 0; i--) {
-                displayContent.mExitingTokens.get(i).hasVisible = false;
-            }
-        }
-
-        for (int stackNdx = mService.mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
-            // Initialize state of exiting applications.
-            final AppTokenList exitingAppTokens =
-                    mService.mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
-            for (int tokenNdx = exitingAppTokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                exitingAppTokens.get(tokenNdx).hasVisible = false;
-            }
+            displayContent.setExitingTokensHasVisible(false);
         }
 
         mHoldScreen = null;
@@ -681,33 +681,7 @@
         // Time to remove any exiting tokens?
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
-            ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens;
-            for (i = exitingTokens.size() - 1; i >= 0; i--) {
-                WindowToken token = exitingTokens.get(i);
-                if (!token.hasVisible) {
-                    exitingTokens.remove(i);
-                }
-            }
-        }
-
-        // Time to remove any exiting applications?
-        for (int stackNdx = mService.mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
-            // Initialize state of exiting applications.
-            final AppTokenList exitingAppTokens =
-                    mService.mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
-            for (i = exitingAppTokens.size() - 1; i >= 0; i--) {
-                final AppWindowToken token = exitingAppTokens.get(i);
-                if (!token.hasVisible && !mService.mClosingApps.contains(token) &&
-                        (!token.mIsExiting || token.isEmpty())) {
-                    // Make sure there is no animation running on this token, so any windows
-                    // associated with it will be removed as soon as their animations are complete
-                    token.mAppAnimator.clearAnimation();
-                    token.mAppAnimator.animating = false;
-                    if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
-                            "performLayout: App token exiting now removed" + token);
-                    token.removeIfPossible();
-                }
-            }
+            displayContent.removeExistingTokensIfPossible();
         }
 
         if (wallpaperDestroyed) {
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index 9d97a0c..e73d4d2 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -27,16 +27,14 @@
     private final WindowManagerService mService;
     private final GraphicBuffer mSnapshot;
 
-    SnapshotStartingData(WindowManagerService service, AppWindowToken appWindowToken,
-            GraphicBuffer snapshot) {
-        super(service, appWindowToken);
+    SnapshotStartingData(WindowManagerService service, GraphicBuffer snapshot) {
+        super(service);
         mService = service;
         mSnapshot = snapshot;
     }
 
     @Override
-    StartingSurface createStartingSurface() {
-        return mService.mTaskSnapshotController.createStartingSurface(
-                mAppWindowToken, mSnapshot);
+    StartingSurface createStartingSurface(AppWindowToken atoken) {
+        return mService.mTaskSnapshotController.createStartingSurface(atoken, mSnapshot);
     }
 }
diff --git a/services/core/java/com/android/server/wm/SplashScreenStartingData.java b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
index 664e600..4b14f86 100644
--- a/services/core/java/com/android/server/wm/SplashScreenStartingData.java
+++ b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
@@ -35,11 +35,10 @@
     private final int mWindowFlags;
     private final Configuration mMergedOverrideConfiguration;
 
-    SplashScreenStartingData(WindowManagerService service, AppWindowToken appWindowToken,
-            String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
-            int labelRes, int icon, int logo, int windowFlags,
-            Configuration mergedOverrideConfiguration) {
-        super(service, appWindowToken);
+    SplashScreenStartingData(WindowManagerService service, String pkg, int theme,
+            CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
+            int logo, int windowFlags, Configuration mergedOverrideConfiguration) {
+        super(service);
         mPkg = pkg;
         mTheme = theme;
         mCompatInfo = compatInfo;
@@ -52,9 +51,9 @@
     }
 
     @Override
-    StartingSurface createStartingSurface() {
-        return mService.mPolicy.addSplashScreen(mAppWindowToken.token, mPkg, mTheme, mCompatInfo,
+    StartingSurface createStartingSurface(AppWindowToken atoken) {
+        return mService.mPolicy.addSplashScreen(atoken.token, mPkg, mTheme, mCompatInfo,
                 mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,
-                mMergedOverrideConfiguration);
+                mMergedOverrideConfiguration, atoken.getDisplayContent().getDisplayId());
     }
 }
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
new file mode 100644
index 0000000..9a6f3eb5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.app.RemoteAction;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Slog;
+import android.util.SparseArray;
+import com.android.server.UiThread;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+/**
+ * Controller for the stack container. This is created by activity manager to link activity stacks
+ * to the stack container they use in window manager.
+ *
+ * Test class: {@link StackWindowControllerTests}
+ */
+public class StackWindowController
+        extends WindowContainerController<TaskStack, StackWindowListener> {
+
+    final int mStackId;
+
+    private final H mHandler;
+
+    public StackWindowController(int stackId, StackWindowListener listener,
+            int displayId, boolean onTop, Rect outBounds) {
+        this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+    }
+
+    @VisibleForTesting
+    public StackWindowController(int stackId, StackWindowListener listener,
+            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
+        super(listener, service);
+        mStackId = stackId;
+        mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
+
+        synchronized (mWindowMap) {
+            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+            if (dc == null) {
+                throw new IllegalArgumentException("Trying to add stackId=" + stackId
+                        + " to unknown displayId=" + displayId);
+            }
+
+            final TaskStack stack = dc.addStackToDisplay(stackId, onTop);
+            stack.setController(this);
+            getRawBounds(outBounds);
+        }
+    }
+
+    @Override
+    public void removeContainer() {
+        synchronized (mWindowMap) {
+            if (mContainer != null) {
+                mContainer.removeIfPossible();
+                super.removeContainer();
+            }
+        }
+    }
+
+    public void reparent(int displayId, Rect outStackBounds) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
+                        + " to displayId=" + displayId);
+            }
+
+            final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
+            if (targetDc == null) {
+                throw new IllegalArgumentException("Trying to move stackId=" + mStackId
+                        + " to unknown displayId=" + displayId);
+            }
+
+            targetDc.moveStackToDisplay(mContainer);
+            getRawBounds(outStackBounds);
+        }
+    }
+
+    public void positionChildAt(TaskWindowContainerController child, int position, Rect bounds,
+            Configuration overrideConfig) {
+        synchronized (mWindowMap) {
+            if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child
+                    + " at " + position);
+            if (child.mContainer == null) {
+                if (DEBUG_STACK) Slog.i(TAG_WM,
+                        "positionChildAt: could not find task=" + this);
+                return;
+            }
+            if (mContainer == null) {
+                if (DEBUG_STACK) Slog.i(TAG_WM,
+                        "positionChildAt: could not find stack for task=" + mContainer);
+                return;
+            }
+            child.mContainer.positionAt(position, bounds, overrideConfig);
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+        }
+    }
+
+    public void positionChildAtTop(TaskWindowContainerController child, boolean includingParents) {
+        if (child == null) {
+            // TODO: Fix the call-points that cause this to happen.
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            final Task childTask = child.mContainer;
+            if (childTask == null) {
+                Slog.e(TAG_WM, "positionChildAtTop: task=" + child + " not found");
+                return;
+            }
+            mContainer.positionChildAt(POSITION_TOP, childTask, includingParents);
+
+            if (mService.mAppTransition.isTransitionSet()) {
+                childTask.setSendingToBottom(false);
+            }
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+        }
+    }
+
+    public void positionChildAtBottom(TaskWindowContainerController child) {
+        if (child == null) {
+            // TODO: Fix the call-points that cause this to happen.
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            final Task childTask = child.mContainer;
+            if (childTask == null) {
+                Slog.e(TAG_WM, "positionChildAtBottom: task=" + child + " not found");
+                return;
+            }
+            mContainer.positionChildAt(POSITION_BOTTOM, childTask, false /* includingParents */);
+
+            if (mService.mAppTransition.isTransitionSet()) {
+                childTask.setSendingToBottom(true);
+            }
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+        }
+    }
+
+    /**
+     * Re-sizes a stack and its containing tasks.
+     *
+     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
+     * @param configs Configurations for tasks in the resized stack, keyed by task id.
+     * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
+     * @return True if the stack is now fullscreen.
+     */
+    public boolean resize(Rect bounds, SparseArray<Configuration> configs,
+            SparseArray<Rect> taskBounds, SparseArray<Rect> taskTempInsetBounds) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
+            }
+            // We might trigger a configuration change. Save the current task bounds for freezing.
+            mContainer.prepareFreezingTaskBounds();
+            if (mContainer.setBounds(bounds, configs, taskBounds, taskTempInsetBounds)
+                    && mContainer.isVisible()) {
+                mContainer.getDisplayContent().setLayoutNeeded();
+                mService.mWindowPlacerLocked.performSurfacePlacement();
+            }
+            return mContainer.getRawFullscreen();
+        }
+    }
+
+    // TODO: This and similar methods should be separated into PinnedStackWindowContainerController
+    public void animateResizePinnedStack(final Rect bounds, final int animationDuration) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("Pinned stack container not found :(");
+            }
+            final Rect originalBounds = new Rect();
+            mContainer.getBounds(originalBounds);
+            mContainer.setAnimatingBounds(bounds);
+            UiThread.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    mService.mBoundsAnimationController.animateBounds(
+                            mContainer, originalBounds, bounds, animationDuration);
+                }
+            });
+        }
+    }
+
+    /** Sets the current picture-in-picture aspect ratio. */
+    public void setPictureInPictureAspectRatio(float aspectRatio) {
+        synchronized (mWindowMap) {
+            if (!mService.mSupportsPictureInPicture || mContainer == null) {
+                return;
+            }
+
+            final int displayId = mContainer.getDisplayContent().getDisplayId();
+            final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
+            animateResizePinnedStack(toBounds, -1 /* duration */);
+        }
+    }
+
+    public void getStackDockedModeBounds(Rect outBounds, boolean ignoreVisibility) {
+        synchronized (mWindowMap) {
+            if (mContainer != null) {
+                mContainer.getStackDockedModeBoundsLocked(outBounds, ignoreVisibility);
+                return;
+            }
+            outBounds.setEmpty();
+        }
+    }
+
+    public void prepareFreezingTaskBounds() {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
+                        + " not found.");
+            }
+            mContainer.prepareFreezingTaskBounds();
+        }
+    }
+
+    /** Sets the current picture-in-picture actions. */
+    public void setPictureInPictureActions(List<RemoteAction> actions) {
+        synchronized (mWindowMap) {
+            if (!mService.mSupportsPictureInPicture || mContainer == null) {
+                return;
+            }
+
+            mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
+        }
+    }
+
+    private void getRawBounds(Rect outBounds) {
+        if (mContainer.getRawFullscreen()) {
+            outBounds.setEmpty();
+        } else {
+            mContainer.getRawBounds(outBounds);
+        }
+    }
+
+    public void getBounds(Rect outBounds) {
+        synchronized (mWindowMap) {
+            if (mContainer != null) {
+                mContainer.getBounds(outBounds);
+                return;
+            }
+            outBounds.setEmpty();
+        }
+    }
+
+    public Rect getBoundsForNewConfiguration() {
+        synchronized(mWindowMap) {
+            final Rect outBounds = new Rect();
+            mContainer.getBoundsForNewConfiguration(outBounds);
+            return outBounds;
+        }
+    }
+
+    void requestResize(Rect bounds) {
+        mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
+    }
+
+    @Override
+    public String toString() {
+        return "{StackWindowController stackId=" + mStackId + "}";
+    }
+
+    private static final class H extends Handler {
+
+        static final int REQUEST_RESIZE = 0;
+
+        private final WeakReference<StackWindowController> mController;
+
+        H(WeakReference<StackWindowController> controller, Looper looper) {
+            super(looper);
+            mController = controller;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final StackWindowController controller = mController.get();
+            final StackWindowListener listener = (controller != null)
+                    ? controller.mListener : null;
+            if (listener == null) {
+                return;
+            }
+            switch (msg.what) {
+                case REQUEST_RESIZE:
+                    listener.requestResize((Rect) msg.obj);
+                    break;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/StackWindowListener.java b/services/core/java/com/android/server/wm/StackWindowListener.java
new file mode 100644
index 0000000..c763c17
--- /dev/null
+++ b/services/core/java/com/android/server/wm/StackWindowListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.graphics.Rect;
+
+/**
+ * Interface used by the creator of {@link StackWindowController} to listen to changes with
+ * the stack container.
+ */
+public interface StackWindowListener extends WindowContainerListener {
+
+    /** Called when the stack container would like its controller to resize. */
+    void requestResize(Rect bounds);
+}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index fcc4c3c..8c564bb 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -24,19 +24,18 @@
 public abstract class StartingData {
 
     protected final WindowManagerService mService;
-    protected final AppWindowToken mAppWindowToken;
 
-    protected StartingData(WindowManagerService service, AppWindowToken appWindowToken) {
+    protected StartingData(WindowManagerService service) {
         mService = service;
-        mAppWindowToken = appWindowToken;
     }
 
     /**
      * Creates the actual starting window surface. DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING
      * THIS METHOD.
      *
+     * @param atoken the app to add the starting window to
      * @return a class implementing {@link StartingSurface} for easy removal with
      *         {@link StartingSurface#remove}
      */
-    abstract StartingSurface createStartingSurface();
+    abstract StartingSurface createStartingSurface(AppWindowToken atoken);
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2d50e3a..d96e1ef 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,9 +27,9 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
 
 import android.app.ActivityManager.StackId;
+import android.app.ActivityManager.TaskDescription;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -81,6 +81,10 @@
     // Resize mode of the task. See {@link ActivityInfo#resizeMode}
     private int mResizeMode;
 
+    // Whether the task supports picture-in-picture.
+    // See {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE}
+    private boolean mSupportsPictureInPicture;
+
     // Whether the task is currently being drag-resized
     private boolean mDragResizing;
     private int mDragResizeMode;
@@ -90,8 +94,11 @@
     // Whether this task is an on-top launcher task, which is determined by the root activity.
     private boolean mIsOnTopLauncher;
 
+    private TaskDescription mTaskDescription;
+
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
-            Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode, boolean homeTask,
+            Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode,
+            boolean supportsPictureInPicture, boolean homeTask, TaskDescription taskDescription,
             TaskWindowContainerController controller) {
         mTaskId = taskId;
         mStack = stack;
@@ -99,9 +106,11 @@
         mService = service;
         mIsOnTopLauncher = isOnTopLauncher;
         mResizeMode = resizeMode;
+        mSupportsPictureInPicture = supportsPictureInPicture;
         mHomeTask = homeTask;
         setController(controller);
         setBounds(bounds, overrideConfig);
+        mTaskDescription = taskDescription;
     }
 
     DisplayContent getDisplayContent() {
@@ -186,8 +195,18 @@
         if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
                 + " from stack=" + mStack);
         EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "reParentTask");
+        final DisplayContent prevDisplayContent = getDisplayContent();
+
         getParent().removeChild(this);
         stack.addTask(this, position, showForAllUsers(), false /* moveParents */);
+
+        // Relayout display(s).
+        final DisplayContent displayContent = stack.getDisplayContent();
+        displayContent.setLayoutNeeded();
+        if (prevDisplayContent != displayContent) {
+            onDisplayChanged(displayContent);
+            prevDisplayContent.setLayoutNeeded();
+        }
     }
 
     /** @see com.android.server.am.ActivityManagerService#positionTaskInStack(int, int, int). */
@@ -313,7 +332,8 @@
     }
 
     boolean isResizeable() {
-        return ActivityInfo.isResizeableMode(mResizeMode) || mService.mForceResizableTasks;
+        return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
+                || mService.mForceResizableTasks;
     }
 
     /**
@@ -546,11 +566,10 @@
 
         displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
         if (setBounds(mTmpRect2, getOverrideConfiguration()) != BOUNDS_CHANGE_NONE) {
-            // Post message to inform activity manager of the bounds change simulating a one-way
-            // call. We do this to prevent a deadlock between window manager lock and activity
-            // manager lock been held.
-            mService.mH.obtainMessage(RESIZE_TASK, mTaskId,
-                    RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds).sendToTarget();
+            final TaskWindowContainerController controller = getController();
+            if (controller != null) {
+                controller.requestResize(mBounds, RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
+            }
         }
     }
 
@@ -618,7 +637,12 @@
 
     @Override
     public DisplayInfo getDisplayInfo() {
-        return mStack.getDisplayContent().getDisplayInfo();
+        return getDisplayContent().getDisplayInfo();
+    }
+
+    @Override
+    public boolean isAttachedToDisplay() {
+        return getDisplayContent() != null;
     }
 
     void forceWindowsScaleable(boolean force) {
@@ -632,12 +656,25 @@
         }
     }
 
+    void setTaskDescription(TaskDescription taskDescription) {
+        mTaskDescription = taskDescription;
+    }
+
+    TaskDescription getTaskDescription() {
+        return mTaskDescription;
+    }
+
     @Override
     boolean fillsParent() {
         return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId);
     }
 
     @Override
+    TaskWindowContainerController getController() {
+        return (TaskWindowContainerController) super.getController();
+    }
+
+    @Override
     public String toString() {
         return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}";
     }
@@ -668,6 +705,5 @@
             pw.println(triplePrefix + "Activity #" + i + " " + wtoken);
             wtoken.dump(pw, triplePrefix);
         }
-
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 267566b..90106a9 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -269,7 +269,7 @@
         mDragApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
-        mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
+        mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
                 mDisplay.getDisplayId());
         mDragWindowHandle.name = TAG;
         mDragWindowHandle.inputChannel = mServerChannel;
@@ -693,6 +693,11 @@
     }
 
     @Override
+    public boolean isAttachedToDisplay() {
+        return mTask != null && mTask.getDisplayContent() != null;
+    }
+
+    @Override
     public void getDimBounds(Rect out) {
         // This dim layer user doesn't need this.
     }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index 994a155..601bf28 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -19,6 +19,11 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager.TaskSnapshot;
 import android.util.ArrayMap;
+import android.util.LruCache;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * Caches snapshots. See {@link TaskSnapshotController}.
@@ -27,13 +32,139 @@
  */
 class TaskSnapshotCache {
 
-    private final ArrayMap<Task, TaskSnapshot> mCache = new ArrayMap<>();
+    // TODO: Make this more dynamic to accomodate for different clients.
+    private static final int RETRIEVAL_CACHE_SIZE = 4;
 
-    void putSnapshot(Task task, TaskSnapshot snapshot) {
-        mCache.put(task, snapshot);
+    private final WindowManagerService mService;
+    private final TaskSnapshotLoader mLoader;
+    private final ArrayMap<AppWindowToken, Integer> mAppTaskMap = new ArrayMap<>();
+    private final ArrayMap<Integer, CacheEntry> mRunningCache = new ArrayMap<>();
+    private final LruCache<Integer, TaskSnapshot> mRetrievalCache =
+            new LruCache<>(RETRIEVAL_CACHE_SIZE);
+
+    TaskSnapshotCache(WindowManagerService service, TaskSnapshotLoader loader) {
+        mService = service;
+        mLoader = loader;
     }
 
-    @Nullable TaskSnapshot getSnapshot(Task task) {
-        return mCache.get(task);
+    void putSnapshot(Task task, TaskSnapshot snapshot) {
+        final CacheEntry entry = mRunningCache.get(task.mTaskId);
+        if (entry != null) {
+            mAppTaskMap.remove(entry.topApp);
+        }
+        final AppWindowToken top = task.getTopChild();
+        mAppTaskMap.put(top, task.mTaskId);
+        mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, task.getTopChild()));
+        mRetrievalCache.put(task.mTaskId, snapshot);
+    }
+
+    /**
+     * If {@param restoreFromDisk} equals {@code true}, DO NOT HOLD THE WINDOW MANAGER LOCK!
+     */
+    @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk) {
+
+        synchronized (mService.mWindowMap) {
+            // Try the running cache.
+            final CacheEntry entry = mRunningCache.get(taskId);
+            if (entry != null) {
+                return entry.snapshot;
+            }
+
+            // Try the retrieval cache.
+            final TaskSnapshot snapshot = mRetrievalCache.get(taskId);
+            if (snapshot != null) {
+                return snapshot;
+            }
+        }
+
+        // Try to restore from disk if asked.
+        if (!restoreFromDisk) {
+            return null;
+        }
+        return tryRestoreFromDisk(taskId, userId);
+    }
+
+    /**
+     * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD!
+     */
+    private TaskSnapshot tryRestoreFromDisk(int taskId, int userId) {
+        final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId);
+        if (snapshot == null) {
+            return null;
+        }
+        synchronized (mService.mWindowMap) {
+            mRetrievalCache.put(taskId, snapshot);
+        }
+        return snapshot;
+    }
+
+    /**
+     * Called when an app token has been removed
+     */
+    void onAppRemoved(AppWindowToken wtoken) {
+        final Integer taskId = mAppTaskMap.get(wtoken);
+        if (taskId != null) {
+            removeRunningEntry(taskId);
+        }
+        if (wtoken.mTask != null) {
+            mRetrievalCache.remove(wtoken.mTask.mTaskId);
+        }
+    }
+
+    /**
+     * Callend when an app window token's process died.
+     */
+    void onAppDied(AppWindowToken wtoken) {
+        final Integer taskId = mAppTaskMap.get(wtoken);
+        if (taskId != null) {
+            removeRunningEntry(taskId);
+        }
+    }
+
+    void onTaskRemoved(int taskId) {
+        removeRunningEntry(taskId);
+        mRetrievalCache.remove(taskId);
+    }
+
+    private void removeRunningEntry(int taskId) {
+        final CacheEntry entry = mRunningCache.get(taskId);
+        if (entry != null) {
+            mAppTaskMap.remove(entry.topApp);
+            mRunningCache.remove(taskId);
+        }
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        final String doublePrefix = prefix + "  ";
+        final String triplePrefix = doublePrefix + "  ";
+        final String quadruplePrefix = triplePrefix + "  ";
+        pw.println(prefix + "SnapshotCache");
+        pw.println(doublePrefix + "RunningCache");
+        for (int i = mRunningCache.size() - 1; i >= 0; i--) {
+            final CacheEntry entry = mRunningCache.valueAt(i);
+            pw.println(triplePrefix + "Entry taskId=" + mRunningCache.keyAt(i));
+            pw.println(quadruplePrefix + "topApp=" + entry.topApp);
+            pw.println(quadruplePrefix + "snapshot=" + entry.snapshot);
+        }
+        pw.println(doublePrefix + "RetrievalCache");
+        final Map<Integer, TaskSnapshot> retrievalSnapshot = mRetrievalCache.snapshot();
+        for (Entry<Integer, TaskSnapshot> entry : retrievalSnapshot.entrySet()) {
+            pw.println(triplePrefix + "Entry taskId=" + entry.getKey());
+            pw.println(quadruplePrefix + "snapshot=" + entry.getValue());
+        }
+    }
+
+    private static final class CacheEntry {
+
+        /** The snapshot. */
+        final TaskSnapshot snapshot;
+
+        /** The app token that was on top of the task when the snapshot was taken */
+        final AppWindowToken topApp;
+
+        CacheEntry(TaskSnapshot snapshot, AppWindowToken topApp) {
+            this.snapshot = snapshot;
+            this.topApp = topApp;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 68aceae..2b74f84 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -17,23 +17,21 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
-import static android.graphics.Bitmap.Config.ARGB_8888;
-import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
-import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER;
-import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER;
-import static android.graphics.PixelFormat.RGBA_8888;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.TaskSnapshot;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.GraphicBuffer;
+import android.os.Environment;
 import android.util.ArraySet;
 import android.view.WindowManagerPolicy.StartingSurface;
 
+import com.google.android.collect.Sets;
+
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.PrintWriter;
+
 /**
  * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
  * put it into our cache. Internally we use gralloc buffers to be able to draw them wherever we
@@ -50,22 +48,47 @@
 class TaskSnapshotController {
 
     private final WindowManagerService mService;
-    private final TaskSnapshotCache mCache = new TaskSnapshotCache();
 
+    private final TaskSnapshotCache mCache;
+    private final TaskSnapshotPersister mPersister = new TaskSnapshotPersister(
+            Environment::getDataSystemCeDirectory);
+    private final TaskSnapshotLoader mLoader = new TaskSnapshotLoader(mPersister);
     private final ArraySet<Task> mTmpTasks = new ArraySet<>();
 
     TaskSnapshotController(WindowManagerService service) {
         mService = service;
+        mCache = new TaskSnapshotCache(mService, mLoader);
+    }
+
+    void systemReady() {
+        mPersister.start();
     }
 
     void onTransitionStarting() {
         if (!ENABLE_TASK_SNAPSHOTS) {
             return;
         }
+        handleClosingApps(mService.mClosingApps);
+    }
+
+
+    /**
+     * Called when the visibility of an app changes outside of the regular app transition flow.
+     */
+    void notifyAppVisibilityChanged(AppWindowToken appWindowToken, boolean visible) {
+        if (!ENABLE_TASK_SNAPSHOTS) {
+            return;
+        }
+        if (!visible) {
+            handleClosingApps(Sets.newArraySet(appWindowToken));
+        }
+    }
+
+    private void handleClosingApps(ArraySet<AppWindowToken> closingApps) {
 
         // We need to take a snapshot of the task if and only if all activities of the task are
         // either closing or hidden.
-        getClosingTasks(mService.mClosingApps, mTmpTasks);
+        getClosingTasks(closingApps, mTmpTasks);
         for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
             final Task task = mTmpTasks.valueAt(i);
             if (!canSnapshotTask(task)) {
@@ -74,12 +97,20 @@
             final TaskSnapshot snapshot = snapshotTask(task);
             if (snapshot != null) {
                 mCache.putSnapshot(task, snapshot);
+                mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+                if (task.getController() != null) {
+                    task.getController().reportSnapshotChanged(snapshot);
+                }
             }
         }
     }
 
-    @Nullable TaskSnapshot getSnapshot(Task task) {
-        return mCache.getSnapshot(task);
+    /**
+     * Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO HOLD THE WINDOW
+     * MANAGER LOCK WHEN CALLING THIS METHOD!
+     */
+    @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk) {
+        return mCache.getSnapshot(taskId, userId, restoreFromDisk);
     }
 
     /**
@@ -92,7 +123,7 @@
     }
 
     private TaskSnapshot snapshotTask(Task task) {
-        final AppWindowToken top = (AppWindowToken) task.getTop();
+        final AppWindowToken top = task.getTopChild();
         if (top == null) {
             return null;
         }
@@ -125,4 +156,34 @@
     private boolean canSnapshotTask(Task task) {
         return !StackId.isHomeOrRecentsStack(task.mStack.mStackId);
     }
+
+    /**
+     * Called when an {@link AppWindowToken} has been removed.
+     */
+    void onAppRemoved(AppWindowToken wtoken) {
+        mCache.onAppRemoved(wtoken);
+    }
+
+    /**
+     * Called when the process of an {@link AppWindowToken} has died.
+     */
+    void onAppDied(AppWindowToken wtoken) {
+        mCache.onAppDied(wtoken);
+    }
+
+    void notifyTaskRemovedFromRecents(int taskId, int userId) {
+        mCache.onTaskRemoved(taskId);
+        mPersister.onTaskRemovedFromRecents(taskId, userId);
+    }
+
+    /**
+     * See {@link TaskSnapshotPersister#removeObsoleteFiles}
+     */
+    void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
+        mPersister.removeObsoleteFiles(persistentTaskIds, runningUserIds);
+    }
+
+    void dump(PrintWriter pw, String prefix) {
+        mCache.dump(pw, prefix);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
new file mode 100644
index 0000000..4340822
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.GraphicBuffer;
+import android.graphics.Rect;
+import android.util.Slog;
+
+import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+/**
+ * Loads a persisted {@link TaskSnapshot} from disk.
+ * <p>
+ * Do not hold the window manager lock when accessing this class.
+ * <p>
+ * Test class: {@link TaskSnapshotPersisterLoaderTest}
+ */
+class TaskSnapshotLoader {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotLoader" : TAG_WM;
+
+    private final TaskSnapshotPersister mPersister;
+
+    TaskSnapshotLoader(TaskSnapshotPersister persister) {
+        mPersister = persister;
+    }
+
+    /**
+     * Loads a task from the disk.
+     * <p>
+     * Do not hold the window manager lock when calling this method, as we directly read data from
+     * disk here, which might be slow.
+     *
+     * @param taskId The id of the task to load.
+     * @param userId The id of the user the task belonged to.
+     * @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded.
+     */
+    TaskSnapshot loadTask(int taskId, int userId) {
+        final File protoFile = mPersister.getProtoFile(taskId, userId);
+        final File bitmapFile = mPersister.getBitmapFile(taskId, userId);
+        if (!protoFile.exists() || !bitmapFile.exists()) {
+            return null;
+        }
+        try {
+            final byte[] bytes = Files.readAllBytes(protoFile.toPath());
+            final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
+            final Options options = new Options();
+            options.inPreferredConfig = Config.HARDWARE;
+            final Bitmap bitmap = BitmapFactory.decodeFile(bitmapFile.getPath(), options);
+            if (bitmap == null) {
+                Slog.w(TAG, "Failed to load bitmap: " + bitmapFile.getPath());
+                return null;
+            }
+            final GraphicBuffer buffer = bitmap.createGraphicBufferHandle();
+            if (buffer == null) {
+                Slog.w(TAG, "Failed to retrieve gralloc buffer for bitmap: "
+                        + bitmapFile.getPath());
+                return null;
+            }
+            return new TaskSnapshot(buffer, proto.orientation,
+                    new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom));
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
+            return null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
new file mode 100644
index 0000000..3a06c38
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.annotation.TestApi;
+import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.AtomicFile;
+import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayDeque;
+
+/**
+ * Persists {@link TaskSnapshot}s to disk.
+ * <p>
+ * Test class: {@link TaskSnapshotPersisterLoaderTest}
+ */
+class TaskSnapshotPersister {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
+    private static final String SNAPSHOTS_DIRNAME = "snapshots";
+    private static final long DELAY_MS = 100;
+    private static final String PROTO_EXTENSION = ".proto";
+    private static final String BITMAP_EXTENSION = ".png";
+
+    @GuardedBy("mLock")
+    private final ArrayDeque<WriteQueueItem> mWriteQueue = new ArrayDeque<>();
+    @GuardedBy("mLock")
+    private boolean mQueueIdling;
+    private boolean mStarted;
+    private final Object mLock = new Object();
+    private final DirectoryResolver mDirectoryResolver;
+
+    /**
+     * The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was
+     * called.
+     */
+    @GuardedBy("mLock")
+    private final ArraySet<Integer> mPersistedTaskIdsSinceLastRemoveObsolete = new ArraySet<>();
+
+    TaskSnapshotPersister(DirectoryResolver resolver) {
+        mDirectoryResolver = resolver;
+    }
+
+    /**
+     * Starts persisting.
+     */
+    void start() {
+        if (!mStarted) {
+            mStarted = true;
+            mPersister.start();
+        }
+    }
+
+    /**
+     * Persists a snapshot of a task to disk.
+     *
+     * @param taskId The id of the task that needs to be persisted.
+     * @param userId The id of the user this tasks belongs to.
+     * @param snapshot The snapshot to persist.
+     */
+    void persistSnapshot(int taskId, int userId, TaskSnapshot snapshot) {
+        synchronized (mLock) {
+            mPersistedTaskIdsSinceLastRemoveObsolete.add(taskId);
+            sendToQueueLocked(new StoreWriteQueueItem(taskId, userId, snapshot));
+        }
+    }
+
+    /**
+     * Callend when a task has been removed.
+     *
+     * @param taskId The id of task that has been removed.
+     * @param userId The id of the user the task belonged to.
+     */
+    void onTaskRemovedFromRecents(int taskId, int userId) {
+        synchronized (mLock) {
+            mPersistedTaskIdsSinceLastRemoveObsolete.remove(taskId);
+            sendToQueueLocked(new DeleteWriteQueueItem(taskId, userId));
+        }
+    }
+
+    /**
+     * In case a write/delete operation was lost because the system crashed, this makes sure to
+     * clean up the directory to remove obsolete files.
+     *
+     * @param persistentTaskIds A set of task ids that exist in our in-memory model.
+     * @param runningUserIds The ids of the list of users that have tasks loaded in our in-memory
+     *                       model.
+     */
+    void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
+        synchronized (mLock) {
+            mPersistedTaskIdsSinceLastRemoveObsolete.clear();
+            sendToQueueLocked(new RemoveObsoleteFilesQueueItem(persistentTaskIds, runningUserIds));
+        }
+    }
+
+    @TestApi
+    void waitForQueueEmpty() {
+        while (true) {
+            synchronized (mLock) {
+                if (mWriteQueue.isEmpty() && mQueueIdling) {
+                    return;
+                }
+            }
+            SystemClock.sleep(100);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void sendToQueueLocked(WriteQueueItem item) {
+        mWriteQueue.offer(item);
+        mLock.notifyAll();
+    }
+
+    private File getDirectory(int userId) {
+        return new File(mDirectoryResolver.getSystemDirectoryForUser(userId), SNAPSHOTS_DIRNAME);
+    }
+
+    File getProtoFile(int taskId, int userId) {
+        return new File(getDirectory(userId), taskId + PROTO_EXTENSION);
+    }
+
+    File getBitmapFile(int taskId, int userId) {
+        return new File(getDirectory(userId), taskId + BITMAP_EXTENSION);
+    }
+
+    private boolean createDirectory(int userId) {
+        final File dir = getDirectory(userId);
+        return dir.exists() || dir.mkdirs();
+    }
+
+    private void deleteSnapshot(int taskId, int userId) {
+        final File protoFile = getProtoFile(taskId, userId);
+        final File bitmapFile = getBitmapFile(taskId, userId);
+        protoFile.delete();
+        bitmapFile.delete();
+    }
+
+    interface DirectoryResolver {
+        File getSystemDirectoryForUser(int userId);
+    }
+
+    private Thread mPersister = new Thread("TaskSnapshotPersister") {
+        public void run() {
+            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            while (true) {
+                WriteQueueItem next;
+                synchronized (mLock) {
+                    next = mWriteQueue.poll();
+                }
+                if (next != null) {
+                    next.write();
+                    SystemClock.sleep(DELAY_MS);
+                }
+                synchronized (mLock) {
+                    if (!mWriteQueue.isEmpty()) {
+                        continue;
+                    }
+                    try {
+                        mQueueIdling = true;
+                        mLock.wait();
+                        mQueueIdling = false;
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+        }
+    };
+
+    private abstract class WriteQueueItem {
+        abstract void write();
+    }
+
+    private class StoreWriteQueueItem extends WriteQueueItem {
+        private final int mTaskId;
+        private final int mUserId;
+        private final TaskSnapshot mSnapshot;
+
+        StoreWriteQueueItem(int taskId, int userId, TaskSnapshot snapshot) {
+            mTaskId = taskId;
+            mUserId = userId;
+            mSnapshot = snapshot;
+        }
+
+        @Override
+        void write() {
+            if (!createDirectory(mUserId)) {
+                Slog.e(TAG, "Unable to create snapshot directory for user dir="
+                        + getDirectory(mUserId));
+            }
+            boolean failed = false;
+            if (!writeProto()) {
+                failed = true;
+            }
+            if (!writeBuffer()) {
+                writeBuffer();
+                failed = true;
+            }
+            if (failed) {
+                deleteSnapshot(mTaskId, mUserId);
+            }
+        }
+
+        boolean writeProto() {
+            final TaskSnapshotProto proto = new TaskSnapshotProto();
+            proto.orientation = mSnapshot.getOrientation();
+            proto.insetLeft = mSnapshot.getContentInsets().left;
+            proto.insetTop = mSnapshot.getContentInsets().top;
+            proto.insetRight = mSnapshot.getContentInsets().right;
+            proto.insetBottom = mSnapshot.getContentInsets().bottom;
+            final byte[] bytes = TaskSnapshotProto.toByteArray(proto);
+            final File file = getProtoFile(mTaskId, mUserId);
+            final AtomicFile atomicFile = new AtomicFile(file);
+            FileOutputStream fos = null;
+            try {
+                fos = atomicFile.startWrite();
+                fos.write(bytes);
+                atomicFile.finishWrite(fos);
+            } catch (IOException e) {
+                atomicFile.failWrite(fos);
+                Slog.e(TAG, "Unable to open " + file + " for persisting. " + e);
+                return false;
+            }
+            return true;
+        }
+
+        boolean writeBuffer() {
+            final File file = getBitmapFile(mTaskId, mUserId);
+            final Bitmap bitmap = Bitmap.createHardwareBitmap(mSnapshot.getSnapshot());
+            try {
+                FileOutputStream fos = new FileOutputStream(file);
+                bitmap.compress(CompressFormat.PNG, 0 /* quality */, fos);
+                fos.close();
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to open " + file + " for persisting. " + e);
+                return false;
+            }
+            return true;
+        }
+    }
+
+    private class DeleteWriteQueueItem extends WriteQueueItem {
+        private final int mTaskId;
+        private final int mUserId;
+
+        DeleteWriteQueueItem(int taskId, int userId) {
+            mTaskId = taskId;
+            mUserId = userId;
+        }
+
+        @Override
+        void write() {
+            deleteSnapshot(mTaskId, mUserId);
+        }
+    }
+
+    @VisibleForTesting
+    class RemoveObsoleteFilesQueueItem extends WriteQueueItem {
+        private final ArraySet<Integer> mPersistentTaskIds;
+        private final int[] mRunningUserIds;
+
+        @VisibleForTesting
+        RemoveObsoleteFilesQueueItem(ArraySet<Integer> persistentTaskIds,
+                int[] runningUserIds) {
+            mPersistentTaskIds = persistentTaskIds;
+            mRunningUserIds = runningUserIds;
+        }
+
+        @Override
+        void write() {
+            final ArraySet<Integer> newPersistedTaskIds;
+            synchronized (mLock) {
+                newPersistedTaskIds = new ArraySet<>(mPersistedTaskIdsSinceLastRemoveObsolete);
+            }
+            for (int userId : mRunningUserIds) {
+                final File dir = getDirectory(userId);
+                final String[] files = dir.list();
+                if (files == null) {
+                    continue;
+                }
+                for (String file : files) {
+                    final int taskId = getTaskId(file);
+                    if (!mPersistentTaskIds.contains(taskId)
+                            && !newPersistedTaskIds.contains(taskId)) {
+                        new File(dir, file).delete();
+                    }
+                }
+            }
+        }
+
+        @VisibleForTesting
+        int getTaskId(String fileName) {
+            if (!fileName.endsWith(PROTO_EXTENSION) && !fileName.endsWith(BITMAP_EXTENSION)) {
+                return -1;
+            }
+            final int end = fileName.lastIndexOf('.');
+            if (end == -1) {
+                return -1;
+            }
+            try {
+                return Integer.parseInt(fileName.substring(0, end));
+            } catch (NumberFormatException e) {
+                return -1;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index c3e3141..cfcbbd0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -26,16 +26,19 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.GraphicBuffer;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Slog;
-import android.view.Display;
 import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.View;
@@ -44,6 +47,7 @@
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy.StartingSurface;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.view.BaseIWindow;
 
 /**
@@ -62,6 +66,7 @@
     private final WindowManagerService mService;
     private boolean mHasDrawn;
     private boolean mReportNextDraw;
+    private Paint mFillBackgroundPaint = new Paint();
 
     static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
             GraphicBuffer snapshot) {
@@ -74,6 +79,7 @@
         final Rect tmpRect = new Rect();
         final Rect tmpFrame = new Rect();
         final Configuration tmpConfiguration = new Configuration();
+        int fillBackgroundColor = Color.WHITE;
         synchronized (service.mWindowMap) {
             layoutParams.type = TYPE_APPLICATION_STARTING;
             layoutParams.format = snapshot.getFormat();
@@ -91,6 +97,12 @@
             layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                     | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
             layoutParams.setTitle(String.format(TITLE_FORMAT, token.mTask.mTaskId));
+            if (token.mTask != null) {
+                final TaskDescription taskDescription = token.mTask.getTaskDescription();
+                if (taskDescription != null) {
+                    fillBackgroundColor = taskDescription.getBackgroundColor();
+                }
+            }
         }
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
@@ -104,7 +116,7 @@
             // Local call.
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
-                surface);
+                surface, fillBackgroundColor);
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
@@ -117,11 +129,14 @@
         return snapshotSurface;
     }
 
-    private TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface) {
+    @VisibleForTesting
+    TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
+            int fillBackgroundColor) {
         mService = service;
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = window;
         mSurface = surface;
+        mFillBackgroundPaint.setColor(fillBackgroundColor);
     }
 
     @Override
@@ -137,7 +152,9 @@
 
         // TODO: Just wrap the buffer here without any copying.
         final Canvas c = mSurface.lockHardwareCanvas();
-        c.drawBitmap(Bitmap.createHardwareBitmap(snapshot), 0, 0, null);
+        final Bitmap b = Bitmap.createHardwareBitmap(snapshot);
+        fillEmptyBackground(c, b);
+        c.drawBitmap(b, 0, 0, null);
         mSurface.unlockCanvasAndPost(c);
         final boolean reportNextDraw;
         synchronized (mService.mWindowMap) {
@@ -147,6 +164,22 @@
         if (reportNextDraw) {
             reportDrawn();
         }
+        mSurface.release();
+    }
+
+    @VisibleForTesting
+    void fillEmptyBackground(Canvas c, Bitmap b) {
+        final boolean fillHorizontally = c.getWidth() > b.getWidth();
+        final boolean fillVertically = c.getHeight() > b.getHeight();
+        if (fillHorizontally) {
+            c.drawRect(b.getWidth(), 0, c.getWidth(), fillVertically
+                        ? b.getHeight()
+                        : c.getHeight(),
+                    mFillBackgroundPaint);
+        }
+        if (fillVertically) {
+            c.drawRect(0, b.getHeight(), c.getWidth(), c.getHeight(), mFillBackgroundPaint);
+        }
     }
 
     private void reportDrawn() {
@@ -160,7 +193,7 @@
         }
     }
 
-    private static Handler sHandler = new Handler() {
+    private static Handler sHandler = new Handler(Looper.getMainLooper()) {
 
         @Override
         public void handleMessage(Message msg) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index eeea532..d3eae8c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -35,17 +35,12 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
 
 import android.app.ActivityManager.StackId;
-import android.app.IActivityManager;
 import android.content.res.Configuration;
-import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Debug;
 import android.os.RemoteException;
 import android.util.EventLog;
 import android.util.Slog;
@@ -53,11 +48,9 @@
 import android.view.DisplayInfo;
 import android.view.Surface;
 
-import android.view.WindowManagerPolicy;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
-import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -206,14 +199,6 @@
         }
     }
 
-    boolean isFullscreenBounds(Rect bounds) {
-        if (mDisplayContent == null || bounds == null) {
-            return true;
-        }
-        mDisplayContent.getLogicalDisplayRect(mTmpRect);
-        return mTmpRect.equals(bounds);
-    }
-
     /**
      * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
      * the normal task bounds.
@@ -413,7 +398,7 @@
         switch (mStackId) {
             case PINNED_STACK_ID:
                 mTmpRect2 = mDisplayContent.getPinnedStackController().onDisplayChanged(mBounds,
-                        getDisplayInfo());
+                        mDisplayContent);
                 break;
             case DOCKED_STACK_ID:
                 repositionDockedStackAfterRotation(mTmpRect2);
@@ -658,7 +643,7 @@
 
         final Rect oldBounds = new Rect(mBounds);
         Rect bounds = null;
-        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack dockedStack = dc.getDockedStackIgnoringVisibility();
         if (mStackId == DOCKED_STACK_ID
                 || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId)
                         && !dockedStack.fillsParent())) {
@@ -684,7 +669,7 @@
         // Update the pinned stack controller after the display info is updated
         if (mStackId == PINNED_STACK_ID) {
             mDisplayContent.getPinnedStackController().onDisplayChanged(oldBounds,
-                    getDisplayInfo());
+                    mDisplayContent);
         }
 
         super.onDisplayChanged(dc);
@@ -697,7 +682,7 @@
             return;
         }
 
-        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack dockedStack = mDisplayContent.getDockedStackIgnoringVisibility();
         if (dockedStack == null) {
             // Not sure why you are calling this method when there is no docked stack...
             throw new IllegalStateException(
@@ -805,8 +790,12 @@
 
         final Rect bounds = new Rect();
         getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/);
-        mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID,
-                1 /*allowResizeInDockedMode*/, bounds).sendToTarget();
+        getController().requestResize(bounds);
+    }
+
+    @Override
+    StackWindowController getController() {
+        return (StackWindowController) super.getController();
     }
 
     @Override
@@ -1209,6 +1198,11 @@
     }
 
     @Override
+    public boolean isAttachedToDisplay() {
+        return mDisplayContent != null;
+    }
+
+    @Override
     public String toString() {
         return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
     }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index bbc9ed2..11667c0 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -16,13 +16,19 @@
 
 package com.android.server.wm;
 
+import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.util.EventLog;
 import android.util.Slog;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.ref.WeakReference;
+
 import static com.android.server.EventLogTags.WM_TASK_CREATED;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -37,30 +43,42 @@
  * Test class: {@link TaskWindowContainerControllerTests}
  */
 public class TaskWindowContainerController
-        extends WindowContainerController<Task, WindowContainerListener> {
+        extends WindowContainerController<Task, TaskWindowContainerListener> {
 
     private final int mTaskId;
+    private final H mHandler;
 
-    public TaskWindowContainerController(int taskId, int stackId, int userId, Rect bounds,
-            Configuration overrideConfig, int resizeMode, boolean homeTask, boolean isOnTopLauncher,
-            boolean toTop, boolean showForAllUsers) {
-        super(null, WindowManagerService.getInstance());
+    public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
+            StackWindowController stackController, int userId, Rect bounds,
+            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+            boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers,
+            TaskDescription taskDescription) {
+        this(taskId, listener, stackController, userId, bounds, overrideConfig, resizeMode,
+                supportsPictureInPicture, homeTask, isOnTopLauncher, toTop, showForAllUsers,
+                taskDescription, WindowManagerService.getInstance());
+    }
+
+    public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
+            StackWindowController stackController, int userId, Rect bounds,
+            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+            boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers,
+            TaskDescription taskDescription, WindowManagerService service) {
+        super(listener, service);
         mTaskId = taskId;
+        mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
 
         synchronized(mWindowMap) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "TaskWindowContainerController: taskId=" + taskId
-                    + " stackId=" + stackId + " bounds=" + bounds);
+                    + " stack=" + stackController + " bounds=" + bounds);
 
-            // TODO: Pass controller for the stack to get the container object when stack is
-            // switched to use controller.
-            final TaskStack stack = mService.mStackIdToStack.get(stackId);
+            final TaskStack stack = stackController.mContainer;
             if (stack == null) {
-                throw new IllegalArgumentException("TaskWindowContainerController: invalid stackId="
-                        + stackId);
+                throw new IllegalArgumentException("TaskWindowContainerController: invalid stack="
+                        + stackController);
             }
-            EventLog.writeEvent(WM_TASK_CREATED, taskId, stackId);
+            EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
             final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
-                    homeTask, isOnTopLauncher);
+                    supportsPictureInPicture, homeTask, isOnTopLauncher, taskDescription);
             final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
             stack.addTask(task, position, showForAllUsers, true /* moveParents */);
         }
@@ -68,10 +86,10 @@
 
     @VisibleForTesting
     Task createTask(int taskId, TaskStack stack, int userId, Rect bounds,
-            Configuration overrideConfig, int resizeMode, boolean homeTask,
-            boolean isOnTopLauncher) {
+            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+            boolean homeTask, boolean isOnTopLauncher, TaskDescription taskDescription) {
         return new Task(taskId, stack, userId, mService, bounds, overrideConfig, isOnTopLauncher,
-                resizeMode, homeTask, this);
+                resizeMode, supportsPictureInPicture, homeTask, taskDescription, this);
     }
 
     @Override
@@ -103,23 +121,22 @@
         }
     }
 
-    public void reparent(int stackId, int position) {
+    public void reparent(StackWindowController stackController, int position) {
         synchronized (mWindowMap) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
-                    + " to stackId=" + stackId + " at " + position);
+                    + " to stack=" + stackController + " at " + position);
             if (mContainer == null) {
                 if (DEBUG_STACK) Slog.i(TAG_WM,
                         "reparent: could not find taskId=" + mTaskId);
                 return;
             }
-            final TaskStack stack = mService.mStackIdToStack.get(stackId);
+            final TaskStack stack = stackController.mContainer;
             if (stack == null) {
-                throw new IllegalArgumentException("reparent: could not find stackId=" + stackId);
+                throw new IllegalArgumentException("reparent: could not find stack="
+                        + stackController);
             }
             mContainer.reparent(stack, position);
-            final DisplayContent displayContent = stack.getDisplayContent();
-            displayContent.setLayoutNeeded();
-            mService.mWindowPlacerLocked.performSurfacePlacement();
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
         }
     }
 
@@ -139,68 +156,11 @@
             }
 
             if (mContainer.resizeLocked(bounds, overrideConfig, forced) && relayout) {
-                mContainer.getDisplayContent().setLayoutNeeded();
-                mService.mWindowPlacerLocked.performSurfacePlacement();
+                mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
             }
         }
     }
 
-    // TODO: Move to positionChildAt() in stack controller once we have a stack controller.
-    public void positionAt(int position, Rect bounds, Configuration overrideConfig) {
-        synchronized (mWindowMap) {
-            if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning taskId=" + mTaskId
-                    + " at " + position);
-            if (mContainer == null) {
-                if (DEBUG_STACK) Slog.i(TAG_WM,
-                        "positionAt: could not find taskId=" + mTaskId);
-                return;
-            }
-            final TaskStack stack = mContainer.mStack;
-            if (stack == null) {
-                if (DEBUG_STACK) Slog.i(TAG_WM,
-                        "positionAt: could not find stack for task=" + mContainer);
-                return;
-            }
-            mContainer.positionAt(position, bounds, overrideConfig);
-            final DisplayContent displayContent = stack.getDisplayContent();
-            displayContent.setLayoutNeeded();
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-        }
-    }
-
-    // TODO: Replace with moveChildToTop in stack controller?
-    public void moveToTop(boolean includingParents) {
-        synchronized(mWindowMap) {
-            if (mContainer == null) {
-                Slog.e(TAG_WM, "moveToTop: taskId=" + mTaskId + " not found");
-                return;
-            }
-            final TaskStack stack = mContainer.mStack;
-            stack.positionChildAt(POSITION_TOP, mContainer, includingParents);
-
-            if (mService.mAppTransition.isTransitionSet()) {
-                mContainer.setSendingToBottom(false);
-            }
-            stack.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-        }
-    }
-
-    // TODO: Replace with moveChildToBottom in stack controller?
-    public void moveToBottom() {
-        synchronized(mWindowMap) {
-            if (mContainer == null) {
-                Slog.e(TAG_WM, "moveTaskToBottom: taskId=" + mTaskId + " not found");
-                return;
-            }
-            final TaskStack stack = mContainer.mStack;
-            stack.positionChildAt(POSITION_BOTTOM, mContainer, false /* includingParents */);
-            if (mService.mAppTransition.isTransitionSet()) {
-                mContainer.setSendingToBottom(true);
-            }
-            stack.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-        }
-    }
-
     public void getBounds(Rect bounds) {
         synchronized (mWindowMap) {
             if (mContainer != null) {
@@ -246,21 +206,57 @@
         }
     }
 
-    /**
-     * @return a graphic buffer representing a screenshot of a task
-     */
-    public TaskSnapshot getSnapshot() {
+    public void setTaskDescription(TaskDescription taskDescription) {
         synchronized (mWindowMap) {
             if (mContainer == null) {
-                Slog.w(TAG_WM, "getSnapshot: taskId " + mTaskId + " not found.");
-                return null;
+                Slog.w(TAG_WM, "setTaskDescription: taskId " + mTaskId + " not found.");
+                return;
             }
-            return mService.mTaskSnapshotController.getSnapshot(mContainer);
+            mContainer.setTaskDescription(taskDescription);
         }
     }
 
+    void reportSnapshotChanged(TaskSnapshot snapshot) {
+        mHandler.obtainMessage(H.REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
+    }
+
+    void requestResize(Rect bounds, int resizeMode) {
+        mHandler.obtainMessage(H.REQUEST_RESIZE, resizeMode, 0, bounds).sendToTarget();
+    }
+
     @Override
     public String toString() {
         return "{TaskWindowContainerController taskId=" + mTaskId + "}";
     }
+
+    private static final class H extends Handler {
+
+        static final int REPORT_SNAPSHOT_CHANGED = 0;
+        static final int REQUEST_RESIZE = 1;
+
+        private final WeakReference<TaskWindowContainerController> mController;
+
+        H(WeakReference<TaskWindowContainerController> controller, Looper looper) {
+            super(looper);
+            mController = controller;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final TaskWindowContainerController controller = mController.get();
+            final TaskWindowContainerListener listener = (controller != null)
+                    ? controller.mListener : null;
+            if (listener == null) {
+                return;
+            }
+            switch (msg.what) {
+                case REPORT_SNAPSHOT_CHANGED:
+                    listener.onSnapshotChanged((TaskSnapshot) msg.obj);
+                    break;
+                case REQUEST_RESIZE:
+                    listener.requestResize((Rect) msg.obj, msg.arg1);
+                    break;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerListener.java b/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
new file mode 100644
index 0000000..af67de3
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Rect;
+
+/**
+ * Interface used by the creator of {@link TaskWindowContainerController} to listen to changes with
+ * the task container.
+ */
+public interface TaskWindowContainerListener extends WindowContainerListener {
+
+    /** Called when the snapshot of this task has changed. */
+    void onSnapshotChanged(TaskSnapshot snapshot);
+
+    /** Called when the task container would like its controller to resize. */
+    void requestResize(Rect bounds, int resizeMode);
+}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index feceb8e..c32e689 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -447,7 +447,7 @@
 
     private void findWallpaperTarget(DisplayContent dc) {
         mFindResults.reset();
-        if (mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
+        if (dc.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
             // In freeform mode we set the wallpaper as its own target, so we don't need an
             // additional window to make it visible.
             mFindResults.setUseTopWallpaperAsTarget(true);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e231da8..5b96263 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -458,9 +458,9 @@
         return false;
     }
 
-    /** Returns the top child container or this container if there are no children. */
-    WindowContainer getTop() {
-        return mChildren.isEmpty() ? this : mChildren.peekLast();
+    /** Returns the top child container. */
+    E getTopChild() {
+        return mChildren.peekLast();
     }
 
     /** Returns true if there is still a removal being deferred */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0c8c10b..1987f90 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -97,6 +97,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
@@ -235,6 +236,8 @@
 import java.util.HashMap;
 import java.util.List;
 
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
@@ -703,12 +706,7 @@
 
     final WindowAnimator mAnimator;
 
-    private final BoundsAnimationController mBoundsAnimationController;
-
-    /** All of the TaskStacks in the window manager, unordered. For an ordered list call
-     * DisplayContent.getStacks(). */
-    // TODO: Don't believe this is needed with the WindowContainer model.
-    SparseArray<TaskStack> mStackIdToStack = new SparseArray<>();
+    final BoundsAnimationController mBoundsAnimationController;
 
     private final PointerEventDispatcher mPointerEventDispatcher;
 
@@ -1113,7 +1111,8 @@
                         + displayId + ".  Aborting.");
                 return WindowManagerGlobal.ADD_INVALID_DISPLAY;
             }
-            if (!displayContent.hasAccess(session.mUid)) {
+            if (!displayContent.hasAccess(session.mUid)
+                    && !mDisplayManagerInternal.isUidPresentOnDisplay(session.mUid, displayId)) {
                 Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
                         + "does not have access: " + displayId + ".  Aborting.");
                 return WindowManagerGlobal.ADD_INVALID_DISPLAY;
@@ -2569,16 +2568,6 @@
         }
     }
 
-    @Override
-    public Rect getBoundsForNewConfiguration(int stackId) {
-        synchronized(mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            final Rect outBounds = new Rect();
-            stack.getBoundsForNewConfiguration(outBounds);
-            return outBounds;
-        }
-    }
-
     void setFocusTaskRegionLocked() {
         final Task focusedTask = mFocusedApp != null ? mFocusedApp.mTask : null;
         if (focusedTask != null) {
@@ -2823,11 +2812,6 @@
         }
     }
 
-    boolean isStackVisibleLocked(int stackId) {
-        final TaskStack stack = mStackIdToStack.get(stackId);
-        return (stack != null && stack.isVisible());
-    }
-
     public void setDockedStackCreateState(int mode, Rect bounds) {
         synchronized (mWindowMap) {
             setDockedStackCreateStateLocked(mode, bounds);
@@ -2876,17 +2860,19 @@
             }
 
             final Rect stackBounds;
-            final DisplayContent displayContent;
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                return null;
+            }
+
+            final TaskStack stack = displayContent.getStackById(PINNED_STACK_ID);
             if (stack != null) {
                 // If the stack exists, then use its final bounds to calculate the new aspect ratio
                 // bounds.
-                displayContent = stack.getDisplayContent();
                 stackBounds = new Rect();
                 stack.getAnimatingBounds(stackBounds);
             } else {
                 // Otherwise, just calculate the aspect ratio bounds from the default bounds
-                displayContent = mRoot.getDisplayContent(displayId);
                 stackBounds = displayContent.getPinnedStackController().getDefaultBounds();
             }
             return displayContent.getPinnedStackController().getAspectRatioBounds(stackBounds,
@@ -2894,119 +2880,10 @@
         }
     }
 
-    /**
-     * Sets the current picture-in-picture aspect ratio.
-     */
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
-            if (!mSupportsPictureInPicture || stack == null) {
-                return;
-            }
-
-            final int displayId = stack.getDisplayContent().getDisplayId();
-            final Rect toBounds = getPictureInPictureBounds(displayId, aspectRatio);
-            animateResizePinnedStack(toBounds, -1 /* duration */);
-        }
-    }
-
-    /**
-     * Sets the current picture-in-picture actions.
-     */
-    public void setPictureInPictureActions(List<RemoteAction> actions) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
-            if (!mSupportsPictureInPicture || stack == null) {
-                return;
-            }
-
-            stack.getDisplayContent().getPinnedStackController().setActions(actions);
-        }
-    }
-
-    /**
-     * Place a TaskStack on a DisplayContent. Will create a new TaskStack if none is found with
-     * specified stackId.
-     * @param stackId The unique identifier of the new stack.
-     * @param displayId The unique identifier of the DisplayContent.
-     * @param onTop If true the stack will be place at the top of the display,
-     *              else at the bottom.
-     * @return The bounds that the stack has after adding. null means fullscreen.
-     */
-    public Rect addStackToDisplay(int stackId, int displayId, boolean onTop) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mWindowMap) {
-                final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                if (dc == null) {
-                    throw new IllegalArgumentException("Trying to add stackId=" + stackId
-                            + " to unknown displayId=" + displayId);
-                }
-
-                return dc.addStackToDisplay(stackId, onTop);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * Move a TaskStack from current DisplayContent to specified one.
-     * @param stackId The unique identifier of the new stack.
-     * @param displayId The unique identifier of the new display.
-     */
-    public Rect moveStackToDisplay(int stackId, int displayId) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mWindowMap) {
-                TaskStack stack = mStackIdToStack.get(stackId);
-                if (stack == null) {
-                    throw new IllegalArgumentException("Trying to move unknown stackId=" + stackId
-                            + " to displayId=" + displayId);
-                }
-
-                final DisplayContent targetDisplayContent = mRoot.getDisplayContent(displayId);
-                if (targetDisplayContent == null) {
-                    throw new IllegalArgumentException("Trying to move stackId=" + stackId
-                            + " to unknown displayId=" + displayId);
-                }
-
-                return targetDisplayContent.moveStackToDisplay(stack);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * Remove a TaskStack completely.
-     * @param stackId The unique identifier of the stack.
-     */
-    public void removeStack(int stackId) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack != null) {
-                stack.removeIfPossible();
-                mStackIdToStack.remove(stackId);
-            }
-        }
-    }
-
-    public void getStackDockedModeBounds(int stackId, Rect bounds, boolean ignoreVisibility) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack != null) {
-                stack.getStackDockedModeBoundsLocked(bounds, ignoreVisibility);
-                return;
-            }
-            bounds.setEmpty();
-        }
-    }
-
     @Override
     public void getStackBounds(int stackId, Rect bounds) {
         synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
+            final TaskStack stack = mRoot.getStackById(stackId);
             if (stack != null) {
                 stack.getBounds(bounds);
                 return;
@@ -3031,43 +2908,6 @@
     }
 
     /**
-     * Re-sizes a stack and its containing tasks.
-     * @param stackId Id of stack to resize.
-     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
-     * @param configs Configurations for tasks in the resized stack, keyed by task id.
-     * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
-     * @return True if the stack is now fullscreen.
-     * */
-    public boolean resizeStack(int stackId, Rect bounds,
-            SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
-            SparseArray<Rect> taskTempInsetBounds) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack == null) {
-                throw new IllegalArgumentException("resizeStack: stackId " + stackId
-                        + " not found.");
-            }
-            if (stack.setBounds(bounds, configs, taskBounds, taskTempInsetBounds)
-                    && stack.isVisible()) {
-                stack.getDisplayContent().setLayoutNeeded();
-                mWindowPlacerLocked.performSurfacePlacement();
-            }
-            return stack.getRawFullscreen();
-        }
-    }
-
-    public void prepareFreezingTaskBounds(int stackId) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack == null) {
-                throw new IllegalArgumentException("prepareFreezingTaskBounds: stackId " + stackId
-                        + " not found.");
-            }
-            stack.prepareFreezingTaskBounds();
-        }
-    }
-
-    /**
      * Starts deferring layout passes. Useful when doing multiple changes but to optimize
      * performance, only one layout pass should be done. This can be called multiple times, and
      * layouting will be resumed once the last caller has called {@link #continueSurfaceLayout}
@@ -3501,8 +3341,9 @@
 
             // Notify whether the docked stack exists for the current user
             final DisplayContent displayContent = getDefaultDisplayContentLocked();
-            displayContent.mDividerControllerLocked
-                    .notifyDockedStackExistsChanged(hasDockedTasksForUser(newUserId));
+            final TaskStack stack = displayContent.getDockedStackIgnoringVisibility();
+            displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
+                    stack != null && stack.hasTaskForUser(newUserId));
 
             // If the display is already prepared, update the density.
             // Otherwise, we'll update it when it's prepared.
@@ -3515,15 +3356,6 @@
         }
     }
 
-    /** Returns whether there is a docked task for the current user. */
-    boolean hasDockedTasksForUser(int userId) {
-        final TaskStack stack = mStackIdToStack.get(DOCKED_STACK_ID);
-        if (stack == null) {
-            return false;
-        }
-        return stack.hasTaskForUser(userId);
-    }
-
     /* Called by WindowState */
     boolean isCurrentProfileLocked(int userId) {
         if (userId == mCurrentUserId) return true;
@@ -3859,7 +3691,7 @@
 
     @Override
     public Bitmap screenshotWallpaper() {
-        if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
+        if (!checkCallingPermission(READ_FRAME_BUFFER,
                 "screenshotWallpaper()")) {
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
@@ -3880,7 +3712,7 @@
      */
     @Override
     public boolean requestAssistScreenshot(final IAssistScreenshotReceiver receiver) {
-        if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
+        if (!checkCallingPermission(READ_FRAME_BUFFER,
                 "requestAssistScreenshot()")) {
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
@@ -3899,6 +3731,24 @@
         return true;
     }
 
+    public TaskSnapshot getTaskSnapshot(int taskId, int userId) {
+        return mTaskSnapshotController.getSnapshot(taskId, userId, true /* restoreFromDisk */);
+    }
+
+    /**
+     * In case a task write/delete operation was lost because the system crashed, this makes sure to
+     * clean up the directory to remove obsolete files.
+     *
+     * @param persistentTaskIds A set of task ids that exist in our in-memory model.
+     * @param runningUserIds The ids of the list of users that have tasks loaded in our in-memory
+     *                       model.
+     */
+    public void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
+        synchronized (mWindowMap) {
+            mTaskSnapshotController.removeObsoleteTaskFiles(persistentTaskIds, runningUserIds);
+        }
+    }
+
     /**
      * Takes a snapshot of the screen.  In landscape mode this grabs the whole screen.
      * In portrait mode, it grabs the full screenshot.
@@ -5344,6 +5194,7 @@
 
     public void systemReady() {
         mPolicy.systemReady();
+        mTaskSnapshotController.systemReady();
     }
 
     // -------------------------------------------------------------
@@ -5394,8 +5245,6 @@
 
         public static final int UPDATE_DOCKED_STACK_DIVIDER = 41;
 
-        public static final int RESIZE_STACK = 42;
-        public static final int RESIZE_TASK = 43;
         public static final int TEAR_DOWN_DRAG_AND_DROP_INPUT = 44;
 
         public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
@@ -5821,23 +5670,6 @@
                     }
                 }
                 break;
-                case RESIZE_TASK: {
-                    try {
-                        mActivityManager.resizeTask(msg.arg1, (Rect) msg.obj, msg.arg2);
-                    } catch (RemoteException e) {
-                        // This will not happen since we are in the same process.
-                    }
-                }
-                break;
-                case RESIZE_STACK: {
-                    try {
-                        mActivityManager.resizeStack(
-                                msg.arg1, (Rect) msg.obj, msg.arg2 == 1, false, false, -1);
-                    } catch (RemoteException e) {
-                        // This will not happen since we are in the same process.
-                    }
-                }
-                break;
                 case WINDOW_REPLACEMENT_TIMEOUT: {
                     synchronized (mWindowMap) {
                         for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) {
@@ -6983,6 +6815,18 @@
         }
     }
 
+    /**
+     * Called when a task has been removed from the recent tasks list.
+     * <p>
+     * Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
+     * container may not exist when this happens.
+     */
+    public void notifyTaskRemovedFromRecents(int taskId, int userId) {
+        synchronized (mWindowMap) {
+            mTaskSnapshotController.notifyTaskRemovedFromRecents(taskId, userId);
+        }
+    }
+
     @Override
     public int getDockedDividerInsetsLw() {
         return getDefaultDisplayContentLocked().getDockedDividerController().getContentInsets();
@@ -7148,6 +6992,7 @@
 
         mInputMonitor.dump(pw, "  ");
         mUnknownAppVisibilityController.dump(pw, "  ");
+        mTaskSnapshotController.dump(pw, "  ");
 
         if (dumpAll) {
             pw.print("  mSystemDecorLayer="); pw.print(mSystemDecorLayer);
@@ -7610,26 +7455,6 @@
         }
     }
 
-    public void animateResizePinnedStack(final Rect bounds, final int animationDuration) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
-            if (stack == null) {
-                Slog.w(TAG, "animateResizePinnedStack: stackId " + PINNED_STACK_ID + " not found.");
-                return;
-            }
-            final Rect originalBounds = new Rect();
-            stack.getBounds(originalBounds);
-            stack.setAnimatingBounds(bounds);
-            UiThread.getHandler().post(new Runnable() {
-                @Override
-                public void run() {
-                    mBoundsAnimationController.animateBounds(
-                            stack, originalBounds, bounds, animationDuration);
-                }
-            });
-        }
-    }
-
     public void setForceResizableTasks(boolean forceResizableTasks) {
         synchronized (mWindowMap) {
             mForceResizableTasks = forceResizableTasks;
@@ -8087,7 +7912,8 @@
         @Override
         public boolean isStackVisible(int stackId) {
             synchronized (mWindowMap) {
-                return WindowManagerService.this.isStackVisibleLocked(stackId);
+                final DisplayContent dc = getDefaultDisplayContentLocked();
+                return dc.isStackVisible(stackId);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b1bf2c6..724aab1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -657,7 +657,8 @@
         mYOffset = 0;
         mLayer = 0;
         mInputWindowHandle = new InputWindowHandle(
-                mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, getDisplayId());
+                mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
+                    getDisplayId());
     }
 
     void attach() {
@@ -2304,13 +2305,17 @@
                     final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
+                        final DisplayContent dc = getDisplayContent();
+                        if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) {
+                            mService.mTaskSnapshotController.onAppDied(win.mAppToken);
+                        }
                         win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
                         if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
                             // The owner of the docked divider died :( We reset the docked stack,
                             // just in case they have the divider at an unstable position. Better
                             // also reset drag resizing state, because the owner can't do it
                             // anymore.
-                            final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+                            final TaskStack stack = dc.getDockedStackIgnoringVisibility();
                             if (stack != null) {
                                 stack.resetDockedStackToMiddle();
                             }
@@ -3195,8 +3200,10 @@
         if (task == null) {
             return false;
         }
+        if (!StackId.isStackAffectedByDragResizing(getStackId())) {
+            return false;
+        }
         if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
-
             // Floating windows never enter drag resize mode.
             return false;
         }
diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp
index 1f7bf4a0..e9c944d 100644
--- a/services/core/jni/com_android_server_ConsumerIrService.cpp
+++ b/services/core/jni/com_android_server_ConsumerIrService.cpp
@@ -36,7 +36,7 @@
 
 static jboolean halOpen(JNIEnv* /* env */, jobject /* obj */) {
     // TODO(b/31632518)
-    mHal = IConsumerIr::getService("consumerir");
+    mHal = IConsumerIr::getService();
     return mHal != nullptr;
 }
 
diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
index 17a6c297..545b3d7 100644
--- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
+++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
@@ -19,6 +19,7 @@
 #include "JNIHelp.h"
 #include "jni.h"
 
+#include <math.h>
 #include <stdlib.h>
 
 #include <android/hardware/thermal/1.0/IThermal.h>
@@ -56,10 +57,16 @@
     jmethodID initMethod;
 } gCpuUsageInfoClassInfo;
 
+jfloat gUndefinedTemperature;
+
 static sp<IThermal> gThermalModule;
 
 // ----------------------------------------------------------------------------
 
+float finalizeTemperature(float temperature) {
+    return isnan(temperature) ? gUndefinedTemperature : temperature;
+}
+
 static void nativeInit(JNIEnv* env, jobject obj) {
     // TODO(b/31632518)
     if (gThermalModule == nullptr) {
@@ -128,16 +135,16 @@
         if (static_cast<int>(list[i].type) == type) {
             switch (source) {
                 case TEMPERATURE_CURRENT:
-                    values[length++] = list[i].currentValue;
+                    values[length++] = finalizeTemperature(list[i].currentValue);
                     break;
                 case TEMPERATURE_THROTTLING:
-                    values[length++] = list[i].throttlingThreshold;
+                    values[length++] = finalizeTemperature(list[i].throttlingThreshold);
                     break;
                 case TEMPERATURE_SHUTDOWN:
-                    values[length++] = list[i].shutdownThreshold;
+                    values[length++] = finalizeTemperature(list[i].shutdownThreshold);
                     break;
                 case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
-                    values[length++] = list[i].vrThrottlingThreshold;
+                    values[length++] = finalizeTemperature(list[i].vrThrottlingThreshold);
                     break;
             }
         }
@@ -204,6 +211,12 @@
     gCpuUsageInfoClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
     gCpuUsageInfoClassInfo.initMethod = GetMethodIDOrDie(env, gCpuUsageInfoClassInfo.clazz,
                                                          "<init>", "(JJ)V");
+
+    clazz = env->FindClass("android/os/HardwarePropertiesManager");
+    jfieldID undefined_temperature_field = GetStaticFieldIDOrDie(env, clazz,
+                                                                 "UNDEFINED_TEMPERATURE", "F");
+    gUndefinedTemperature = env->GetStaticFloatField(clazz, undefined_temperature_field);
+
     return res;
 }
 
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 118562f..50bae794 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -43,7 +43,7 @@
     if (mHal != nullptr) {
         return;
     }
-    mHal = IVibrator::getService("vibrator");
+    mHal = IVibrator::getService();
 }
 
 static jboolean vibratorExists(JNIEnv* /* env */, jobject /* clazz */)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 27efd05..8baa96b 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -207,6 +207,7 @@
     void setPointerIconType(int32_t iconId);
     void reloadPointerIcons();
     void setCustomPointerIcon(const SpriteIcon& icon);
+    void setPointerCapture(bool enabled);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -276,6 +277,9 @@
         // Show touches feature enable/disable.
         bool showTouches;
 
+        // Pointer capture feature enable/disable.
+        bool pointerCapture;
+
         // Sprite controller singleton, created on first use.
         sp<SpriteController> spriteController;
 
@@ -312,6 +316,7 @@
         mLocked.pointerSpeed = 0;
         mLocked.pointerGesturesEnabled = true;
         mLocked.showTouches = false;
+        mLocked.pointerCapture = false;
     }
     mInteractive = true;
 
@@ -339,6 +344,7 @@
         dump.appendFormat(INDENT "Pointer Gestures Enabled: %s\n",
                 toString(mLocked.pointerGesturesEnabled));
         dump.appendFormat(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
+        dump.appendFormat(INDENT "Pointer Capture Enabled: %s\n", toString(mLocked.pointerCapture));
     }
     dump.append("\n");
 
@@ -460,6 +466,8 @@
 
         outConfig->showTouches = mLocked.showTouches;
 
+        outConfig->pointerCapture = mLocked.pointerCapture;
+
         outConfig->setDisplayInfo(false /*external*/, mLocked.internalViewport);
         outConfig->setDisplayInfo(true /*external*/, mLocked.externalViewport);
     } // release lock
@@ -767,6 +775,22 @@
             InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
 }
 
+void NativeInputManager::setPointerCapture(bool enabled) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (mLocked.pointerCapture == enabled) {
+            return;
+        }
+
+        ALOGI("Setting pointer capture to %s.", enabled ? "enabled" : "disabled");
+        mLocked.pointerCapture = enabled;
+    } // release lock
+
+    mInputManager->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+}
+
 void NativeInputManager::setInteractive(bool interactive) {
     mInteractive = interactive;
 }
@@ -1323,6 +1347,12 @@
     im->setFocusedApplication(env, applicationHandleObj);
 }
 
+static void nativeSetPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr,
+        jboolean enabled) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    im->setPointerCapture(enabled);
+}
+
 static void nativeSetInputDispatchMode(JNIEnv* /* env */,
         jclass /* clazz */, jlong ptr, jboolean enabled, jboolean frozen) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1462,7 +1492,11 @@
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
     PointerIcon pointerIcon;
-    android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+    status_t result = android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+    if (result) {
+        jniThrowRuntimeException(env, "Failed to load custom pointer icon.");
+        return;
+    }
 
     SpriteIcon spriteIcon;
     pointerIcon.bitmap.copyTo(&spriteIcon.bitmap, kN32_SkColorType);
@@ -1505,6 +1539,8 @@
             (void*) nativeSetInputWindows },
     { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
             (void*) nativeSetFocusedApplication },
+    { "nativeSetPointerCapture", "(JZ)V",
+            (void*) nativeSetPointerCapture },
     { "nativeSetInputDispatchMode", "(JZZ)V",
             (void*) nativeSetInputDispatchMode },
     { "nativeSetSystemUiVisibility", "(JI)V",
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index 2fd0603..74af639 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -81,7 +81,7 @@
 
     // TODO(b/31632518)
     if (gLight == nullptr) {
-        gLight = ILight::getService("light");
+        gLight = ILight::getService();
     }
 
     if (gLight == nullptr) {
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
index 9106441..517fce0 100644
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ b/services/core/jni/com_android_server_location_ContextHubService.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #undef LOG_NDEBUG
 #undef LOG_TAG
 #define LOG_NDEBUG 0
@@ -26,11 +25,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/endian.h>
 
 #include <chrono>
 #include <mutex>
 #include <queue>
 #include <unordered_map>
+#include <utility>
 
 #include <android-base/macros.h>
 #include <android/hardware/contexthub/1.0/IContexthub.h>
@@ -39,20 +40,20 @@
 #include "core_jni_helpers.h"
 #include "JNIHelp.h"
 
-using IContexthub = android::hardware::contexthub::V1_0::IContexthub;
+using android::hardware::contexthub::V1_0::AsyncEventType;
+using android::hardware::contexthub::V1_0::ContextHub;
+using android::hardware::contexthub::V1_0::ContextHubMsg;
+using android::hardware::contexthub::V1_0::HubAppInfo;
+using android::hardware::contexthub::V1_0::IContexthub;
+using android::hardware::contexthub::V1_0::IContexthubCallback;
+using android::hardware::contexthub::V1_0::NanoAppBinary;
+using android::hardware::contexthub::V1_0::Result;
+using android::hardware::contexthub::V1_0::TransactionResult;
 
-using Result = android::hardware::contexthub::V1_0::Result;
-using ContextHubMsg = android::hardware::contexthub::V1_0::ContextHubMsg;
-using IContexthubCallback = android::hardware::contexthub::V1_0::IContexthubCallback;
-using AsyncEventType = android::hardware::contexthub::V1_0::AsyncEventType;
-using TransactionResult = android::hardware::contexthub::V1_0::TransactionResult;
-using ContextHub = android::hardware::contexthub::V1_0::ContextHub;
-using HubAppInfo = android::hardware::contexthub::V1_0::HubAppInfo;
-
-template<typename T>
-using Return = android::hardware::Return<T>;
+using android::hardware::Return;
 
 using std::chrono::steady_clock;
+
 // If a transaction takes longer than this, we'll allow it to be
 // canceled by a new transaction.  Note we do _not_ automatically
 // cancel a transaction after this much time.  We can have a
@@ -63,6 +64,22 @@
 
 namespace android {
 
+constexpr uint32_t kNanoAppBinaryHeaderVersion = 1;
+
+// Important: this header is explicitly defined as little endian byte order, and
+// therefore may not match host endianness
+struct NanoAppBinaryHeader {
+    uint32_t headerVersion;        // 0x1 for this version
+    uint32_t magic;                // "NANO" (see NANOAPP_MAGIC in context_hub.h)
+    uint64_t appId;                // App Id, contains vendor id
+    uint32_t appVersion;           // Version of the app
+    uint32_t flags;                // Signed, encrypted
+    uint64_t hwHubType;            // Which hub type is this compiled for
+    uint8_t targetChreApiMajorVersion; // Which CHRE API version this is compiled for
+    uint8_t targetChreApiMinorVersion;
+    uint8_t reserved[6];
+} __attribute__((packed));
+
 enum HubMessageType {
     CONTEXT_HUB_APPS_ENABLE  = 1, // Enables loaded nano-app(s)
     CONTEXT_HUB_APPS_DISABLE = 2, // Disables loaded nano-app(s)
@@ -361,11 +378,12 @@
 
 ContextHubServiceDb db;
 
-int getHubIdForHubHandle(int hubHandle) {
-    if (hubHandle < 0 || hubHandle >= db.hubInfo.numHubs) {
-      return -1;
+bool getHubIdForHubHandle(int hubHandle, uint32_t *hubId) {
+    if (hubHandle < 0 || hubHandle >= db.hubInfo.numHubs || hubId == nullptr) {
+        return false;
     } else {
-      return db.hubInfo.hubs[hubHandle].hubId;
+        *hubId = db.hubInfo.hubs[hubHandle].hubId;
+        return true;
     }
 }
 
@@ -380,17 +398,6 @@
     return db.appInstances[id].hubHandle;
 }
 
-int getHubIdForAppInstance(jint id) {
-    int hubHandle = getHubHandleForAppInstance(id);
-
-    if (hubHandle < 0) {
-        ALOGD("Cannot find hub instance for app instance %d", id);
-        return -1;
-    }
-
-    return db.hubInfo.hubs[hubHandle].hubId;
-}
-
 jint getAppInstanceForAppId(uint64_t app_id) {
     auto end = db.appInstances.end();
     for (auto current = db.appInstances.begin(); current != end; ++current) {
@@ -1001,6 +1008,45 @@
     return retArray;
 }
 
+Result sendLoadNanoAppRequest(uint32_t hubId,
+                              jbyte *data,
+                              size_t dataBufferLength) {
+    auto header = reinterpret_cast<const NanoAppBinaryHeader *>(data);
+    Result result;
+
+    if (dataBufferLength < sizeof(NanoAppBinaryHeader)) {
+        ALOGE("Got short NanoApp, length %zu", dataBufferLength);
+        result = Result::BAD_PARAMS;
+    } else if (header->headerVersion != htole32(kNanoAppBinaryHeaderVersion)) {
+        ALOGE("Got unexpected NanoApp header version %" PRIu32,
+              letoh32(header->headerVersion));
+        result = Result::BAD_PARAMS;
+    } else {
+        NanoAppBinary nanoapp;
+
+        // Data from the common nanoapp header goes into explicit fields
+        nanoapp.appId      = letoh64(header->appId);
+        nanoapp.appVersion = letoh32(header->appVersion);
+        nanoapp.flags      = letoh32(header->flags);
+        nanoapp.targetChreApiMajorVersion = header->targetChreApiMajorVersion;
+        nanoapp.targetChreApiMinorVersion = header->targetChreApiMinorVersion;
+
+        // Everything past the header goes in customBinary
+        auto dataBytes = reinterpret_cast<const uint8_t *>(data);
+        std::vector<uint8_t> customBinary(
+            dataBytes + sizeof(NanoAppBinaryHeader),
+            dataBytes + dataBufferLength);
+        nanoapp.customBinary = std::move(customBinary);
+
+        ALOGW("Calling Load NanoApp on hub %d", hubId);
+        result = db.hubInfo.contextHub->loadNanoApp(hubId,
+                                                    nanoapp,
+                                                    CONTEXT_HUB_LOAD_APP);
+    }
+
+    return result;
+}
+
 jint nativeSendMessage(JNIEnv *env,
                        jobject instance,
                        jintArray header_,
@@ -1012,19 +1058,18 @@
     jint retVal = -1; // Default to failure
 
     jint *header = env->GetIntArrayElements(header_, 0);
-    unsigned int numHeaderElements = env->GetArrayLength(header_);
+    size_t numHeaderElements = env->GetArrayLength(header_);
     jbyte *data = env->GetByteArrayElements(data_, 0);
-    int dataBufferLength = env->GetArrayLength(data_);
+    size_t dataBufferLength = env->GetArrayLength(data_);
 
     if (numHeaderElements < MSG_HEADER_SIZE) {
         ALOGW("Malformed header len");
         return -1;
     }
 
-    uint32_t appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE];
+    jint appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE];
     uint32_t msgType = header[HEADER_FIELD_MSG_TYPE];
     int hubHandle = -1;
-    int hubId;
     uint64_t appId;
 
     if (msgType == CONTEXT_HUB_UNLOAD_APP) {
@@ -1042,7 +1087,8 @@
         hubHandle = header[HEADER_FIELD_HUB_HANDLE];
     }
 
-    if (hubHandle < 0) {
+    uint32_t hubId = -1;
+    if (!getHubIdForHubHandle(hubHandle, &hubId)) {
         ALOGD("Invalid hub Handle %d", hubHandle);
         return -1;
     }
@@ -1072,46 +1118,41 @@
     Result result;
 
     if (msgType == CONTEXT_HUB_UNLOAD_APP) {
-        hubId = getHubIdForHubHandle(hubHandle);
-        ALOGW("Calling UnLoad NanoApp for app %" PRIx64 " on hub %d",
+        ALOGW("Calling UnLoad NanoApp for app %" PRIx64 " on hub %" PRIu32,
               db.appInstances[appInstanceHandle].appInfo.appId,
               hubId);
         result = db.hubInfo.contextHub->unloadNanoApp(
                 hubId, db.appInstances[appInstanceHandle].appInfo.appId, CONTEXT_HUB_UNLOAD_APP);
     } else {
-        if (header[HEADER_FIELD_APP_INSTANCE] == OS_APP_ID) {
+        if (appInstanceHandle == OS_APP_ID) {
             if (msgType == CONTEXT_HUB_LOAD_APP) {
-                std::vector<uint8_t> dataVector(reinterpret_cast<uint8_t *>(data),
-                                                reinterpret_cast<uint8_t *>(data + dataBufferLength));
-                hubId = getHubIdForHubHandle(hubHandle);
-                ALOGW("Calling Load NanoApp on hub %d", hubId);
-                result = db.hubInfo.contextHub->loadNanoApp(hubId,
-                                                            dataVector,
-                                                            CONTEXT_HUB_LOAD_APP);
+                result = sendLoadNanoAppRequest(hubId, data, dataBufferLength);
             } else {
                 ALOGD("Dropping OS addresses message of type - %" PRIu32, msgType);
                 result = Result::BAD_PARAMS;
             }
         } else {
-
-            appId = getAppIdForAppInstance(header[HEADER_FIELD_APP_INSTANCE]);
-            hubId = getHubIdForAppInstance(header[HEADER_FIELD_APP_INSTANCE]);
-
-            if (appId != static_cast<uint64_t>(INVALID_APP_ID) && hubId >= 0) {
+            appId = getAppIdForAppInstance(appInstanceHandle);
+            if (appId == static_cast<uint64_t>(INVALID_APP_ID)) {
+                ALOGD("Cannot find application instance %d", appInstanceHandle);
+                result = Result::BAD_PARAMS;
+            } else if (hubHandle != getHubHandleForAppInstance(appInstanceHandle)) {
+                ALOGE("Given hubHandle (%d) doesn't match expected for app instance (%d)",
+                      hubHandle,
+                      getHubHandleForAppInstance(appInstanceHandle));
+                result = Result::BAD_PARAMS;
+            } else {
                 ContextHubMsg msg;
                 msg.appName = appId;
                 msg.msgType = msgType;
                 msg.msg.setToExternal((unsigned char *)data, dataBufferLength);
 
-                ALOGW("Sending msg of type %" PRIu32 " len %u to app %" PRIx64 " on hub %d",
+                ALOGW("Sending msg of type %" PRIu32 " len %zu to app %" PRIx64 " on hub %" PRIu32,
                        msgType,
                        dataBufferLength,
                        appId,
                        hubId);
                 result = db.hubInfo.contextHub->sendMessageToHub(hubId, msg);
-            } else {
-                ALOGD("Cannot find application instance %u", header[HEADER_FIELD_APP_INSTANCE]);
-                result = Result::BAD_PARAMS;
             }
         }
     }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 5a20bed..01a1efc 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -673,7 +673,7 @@
     }
 
     if (flags & static_cast<uint32_t>(GnssMeasurementFlags::HAS_AUTOMATIC_GAIN_CONTROL)) {
-        SET(AgcLevelDb, measurement->agcLevelDb);
+        SET(AutomaticGainControlLevelInDb, measurement->agcLevelDb);
     }
 
     return object.get();
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index b2372a3..fab309b 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -110,7 +110,7 @@
 static void nativeInit(JNIEnv* env, jobject obj) {
     gPowerManagerServiceObj = env->NewGlobalRef(obj);
 
-    gPowerHal = IPower::getService("power");
+    gPowerHal = IPower::getService();
     if (gPowerHal == nullptr) {
         ALOGE("Couldn't load PowerHAL module");
     }
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 179fba0..9f528b1 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -327,7 +327,7 @@
 
 JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
     // TODO(b/31632518)
-    sp<ITvInput> tvInput = ITvInput::getService("tv.input");
+    sp<ITvInput> tvInput = ITvInput::getService();
     if (tvInput == nullptr) {
         ALOGE("Couldn't get tv.input service.");
         return nullptr;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 040188d..af78154 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -33,6 +33,13 @@
 import static android.app.admin.DevicePolicyManager.CODE_USER_HAS_PROFILE_OWNER;
 import static android.app.admin.DevicePolicyManager.CODE_USER_NOT_RUNNING;
 import static android.app.admin.DevicePolicyManager.CODE_USER_SETUP_COMPLETED;
+import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
+import static android.app.admin.DevicePolicyManager.DELEGATION_ENABLE_SYSTEM_APP;
+import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES;
+import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -69,6 +76,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.NetworkEvent;
 import android.app.admin.PasswordMetrics;
+import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SecurityLog;
 import android.app.admin.SecurityLog.SecurityEvent;
 import android.app.admin.SystemUpdatePolicy;
@@ -191,6 +199,7 @@
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -235,7 +244,7 @@
 
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
-    private static final long MS_PER_DAY = 86400 * 1000;
+    private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
 
     private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
 
@@ -257,6 +266,17 @@
     private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
             = "application-restrictions-manager";
 
+    // Comprehensive list of delegations.
+    private static final String DELEGATIONS[] = {
+        DELEGATION_CERT_INSTALL,
+        DELEGATION_APP_RESTRICTIONS,
+        DELEGATION_BLOCK_UNINSTALL,
+        DELEGATION_ENABLE_SYSTEM_APP,
+        DELEGATION_KEEP_UNINSTALLED_PACKAGES,
+        DELEGATION_PACKAGE_ACCESS,
+        DELEGATION_PERMISSION_GRANT
+    };
+
     /**
      *  System property whose value is either "true" or "false", indicating whether
      *  device owner is present.
@@ -330,7 +350,7 @@
      * Minimum timeout in milliseconds after which unlocking with weak auth times out,
      * i.e. the user has to use a strong authentication method like password, PIN or pattern.
      */
-    private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
+    private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
 
     /**
      * Strings logged with {@link
@@ -466,12 +486,11 @@
 
         ComponentName mRestrictionsProvider;
 
-        String mDelegatedCertInstallerPackage;
+        // Map of delegate package to delegation scopes
+        final ArrayMap<String, List<String>> mDelegationMap = new ArrayMap<>();
 
         boolean doNotAskCredentialsOnBoot = false;
 
-        String mApplicationRestrictionsManagingPackage;
-
         Set<String> mAffiliationIds = new ArraySet<>();
 
         long mLastSecurityLogRetrievalTime = -1;
@@ -1154,7 +1173,7 @@
                 } else if (TAG_KEEP_UNINSTALLED_PACKAGES.equals(tag)) {
                     keepUninstalledPackages = readPackageList(parser, tag);
                 } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
-                    UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions());
+                    userRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                 } else if (TAG_DEFAULT_ENABLED_USER_RESTRICTIONS.equals(tag)) {
                     readAttributeValues(
                             parser, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet);
@@ -1395,7 +1414,7 @@
     }
 
     private void handlePackagesChanged(String packageName, int userHandle) {
-        boolean removed = false;
+        boolean removedAdmin = false;
         if (VERBOSE_LOG) Slog.d(LOG_TAG, "Handling package changes for user " + userHandle);
         DevicePolicyData policy = getUserData(userHandle);
         synchronized (this) {
@@ -1411,32 +1430,36 @@
                                         PackageManager.MATCH_DIRECT_BOOT_AWARE
                                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                                         userHandle) == null) {
-                            removed = true;
+                            removedAdmin = true;
                             policy.mAdminList.remove(i);
                             policy.mAdminMap.remove(aa.info.getComponent());
                         }
                     }
                 } catch (RemoteException re) {
-                    // Shouldn't happen
+                    // Shouldn't happen.
                 }
             }
-            if (removed) {
+            if (removedAdmin) {
                 validatePasswordOwnerLocked(policy);
-                saveSettingsLocked(policy.mUserHandle);
             }
 
-            // Check if delegated cert installer or app restrictions managing packages are removed.
-            if (isRemovedPackage(packageName, policy.mDelegatedCertInstallerPackage, userHandle)) {
-                policy.mDelegatedCertInstallerPackage = null;
-                saveSettingsLocked(policy.mUserHandle);
+            boolean removedDelegate = false;
+
+            // Check if a delegate was removed.
+            for (int i = policy.mDelegationMap.size() - 1; i >= 0; i--) {
+                final String delegatePackage = policy.mDelegationMap.keyAt(i);
+                if (isRemovedPackage(packageName, delegatePackage, userHandle)) {
+                    policy.mDelegationMap.removeAt(i);
+                    removedDelegate = true;
+                }
             }
-            if (isRemovedPackage(
-                    packageName, policy.mApplicationRestrictionsManagingPackage, userHandle)) {
-                policy.mApplicationRestrictionsManagingPackage = null;
+
+            // Persist updates if the removed package was an admin or delegate.
+            if (removedAdmin || removedDelegate) {
                 saveSettingsLocked(policy.mUserHandle);
             }
         }
-        if (removed) {
+        if (removedAdmin) {
             // The removed admin might have disabled camera, so update user restrictions.
             pushUserRestrictions(userHandle);
         }
@@ -1508,6 +1531,10 @@
             return (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
         }
 
+        AlarmManager getAlarmManager() {
+            return (AlarmManager) mContext.getSystemService(AlarmManager.class);
+        }
+
         IWindowManager getIWindowManager() {
             return IWindowManager.Stub
                     .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
@@ -1607,6 +1634,11 @@
             mContext.getSystemService(PowerManager.class).reboot(reason);
         }
 
+        void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force)
+                throws IOException {
+            RecoverySystem.rebootWipeUserData(mContext, shutdown, reason, force);
+        }
+
         boolean systemPropertiesGetBoolean(String key, boolean def) {
             return SystemProperties.getBoolean(key, def);
         }
@@ -2063,7 +2095,7 @@
         long token = mInjector.binderClearCallingIdentity();
         try {
             int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle;
-            AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+            AlarmManager am = mInjector.getAlarmManager();
             PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD,
                     new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION),
                     PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT,
@@ -2370,13 +2402,19 @@
                 out.attribute(null, ATTR_PERMISSION_POLICY,
                         Integer.toString(policy.mPermissionPolicy));
             }
-            if (policy.mDelegatedCertInstallerPackage != null) {
-                out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER,
-                        policy.mDelegatedCertInstallerPackage);
-            }
-            if (policy.mApplicationRestrictionsManagingPackage != null) {
-                out.attribute(null, ATTR_APPLICATION_RESTRICTIONS_MANAGER,
-                        policy.mApplicationRestrictionsManagingPackage);
+
+            // Serialize delegations.
+            for (int i = 0; i < policy.mDelegationMap.size(); ++i) {
+                final String delegatePackage = policy.mDelegationMap.keyAt(i);
+                final List<String> scopes = policy.mDelegationMap.valueAt(i);
+
+                // Every "delegation" tag serializes the information of one delegate-scope pair.
+                for (String scope : scopes) {
+                    out.startTag(null, "delegation");
+                    out.attribute(null, "delegatePackage", delegatePackage);
+                    out.attribute(null, "scope", scope);
+                    out.endTag(null, "delegation");
+                }
             }
 
             final int N = policy.mAdminList.size();
@@ -2560,10 +2598,36 @@
             if (!TextUtils.isEmpty(permissionPolicy)) {
                 policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
             }
-            policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
-                    ATTR_DELEGATED_CERT_INSTALLER);
-            policy.mApplicationRestrictionsManagingPackage = parser.getAttributeValue(null,
-                    ATTR_APPLICATION_RESTRICTIONS_MANAGER);
+            // Check for delegation compatibility with pre-O.
+            // TODO(edmanp) remove in P.
+            {
+                final String certDelegate = parser.getAttributeValue(null,
+                        ATTR_DELEGATED_CERT_INSTALLER);
+                if (certDelegate != null) {
+                    List<String> scopes = policy.mDelegationMap.get(certDelegate);
+                    if (scopes == null) {
+                        scopes = new ArrayList<>();
+                        policy.mDelegationMap.put(certDelegate, scopes);
+                    }
+                    if (!scopes.contains(DELEGATION_CERT_INSTALL)) {
+                        scopes.add(DELEGATION_CERT_INSTALL);
+                        needsRewrite = true;
+                    }
+                }
+                final String appRestrictionsDelegate = parser.getAttributeValue(null,
+                        ATTR_APPLICATION_RESTRICTIONS_MANAGER);
+                if (appRestrictionsDelegate != null) {
+                    List<String> scopes = policy.mDelegationMap.get(appRestrictionsDelegate);
+                    if (scopes == null) {
+                        scopes = new ArrayList<>();
+                        policy.mDelegationMap.put(appRestrictionsDelegate, scopes);
+                    }
+                    if (!scopes.contains(DELEGATION_APP_RESTRICTIONS)) {
+                        scopes.add(DELEGATION_APP_RESTRICTIONS);
+                        needsRewrite = true;
+                    }
+                }
+            }
 
             type = parser.next();
             int outerDepth = parser.getDepth();
@@ -2598,6 +2662,23 @@
                     } catch (RuntimeException e) {
                         Slog.w(LOG_TAG, "Failed loading admin " + name, e);
                     }
+                } else if ("delegation".equals(tag)) {
+                    // Parse delegation info.
+                    final String delegatePackage = parser.getAttributeValue(null,
+                            "delegatePackage");
+                    final String scope = parser.getAttributeValue(null, "scope");
+
+                    // Get a reference to the scopes list for the delegatePackage.
+                    List<String> scopes = policy.mDelegationMap.get(delegatePackage);
+                    // Or make a new list if none was found.
+                    if (scopes == null) {
+                        scopes = new ArrayList<>();
+                        policy.mDelegationMap.put(delegatePackage, scopes);
+                    }
+                    // Add the new scope to the list of delegatePackage if it's not already there.
+                    if (!scopes.contains(scope)) {
+                        scopes.add(scope);
+                    }
                 } else if ("failed-password-attempts".equals(tag)) {
                     policy.mFailedPasswordAttempts = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
@@ -2663,14 +2744,14 @@
             // Ignore
         }
 
+        // Generate a list of admins from the admin map
+        policy.mAdminList.addAll(policy.mAdminMap.values());
+
         // Might need to upgrade the file by rewriting it
         if (needsRewrite) {
             saveSettingsLocked(userHandle);
         }
 
-        // Generate a list of admins from the admin map
-        policy.mAdminList.addAll(policy.mAdminMap.values());
-
         validatePasswordOwnerLocked(policy);
         updateMaximumTimeToLockLocked(userHandle);
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
@@ -4520,46 +4601,19 @@
     }
 
     @Override
-    public void enforceCanManageCaCerts(ComponentName who) {
+    public void enforceCanManageCaCerts(ComponentName who, String callerPackage) {
         if (who == null) {
-            if (!isCallerDelegatedCertInstaller()) {
+            if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
                 mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
             }
         } else {
-            synchronized (this) {
-                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            }
+            enforceProfileOrDeviceOwner(who);
         }
     }
 
-    private void enforceCanManageInstalledKeys(ComponentName who) {
-        if (who == null) {
-            if (!isCallerDelegatedCertInstaller()) {
-                throw new SecurityException("who == null, but caller is not cert installer");
-            }
-        } else {
-            synchronized (this) {
-                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            }
-        }
-    }
-
-    private boolean isCallerDelegatedCertInstaller() {
-        final int callingUid = mInjector.binderGetCallingUid();
-        final int userHandle = UserHandle.getUserId(callingUid);
+    private void enforceProfileOrDeviceOwner(ComponentName who) {
         synchronized (this) {
-            final DevicePolicyData policy = getUserData(userHandle);
-            if (policy.mDelegatedCertInstallerPackage == null) {
-                return false;
-            }
-
-            try {
-                int uid = mInjector.getPackageManager().getPackageUidAsUser(
-                        policy.mDelegatedCertInstallerPackage, userHandle);
-                return uid == callingUid;
-            } catch (NameNotFoundException e) {
-                return false;
-            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
         }
     }
 
@@ -4604,8 +4658,9 @@
     }
 
     @Override
-    public boolean installCaCert(ComponentName admin, byte[] certBuffer) throws RemoteException {
-        enforceCanManageCaCerts(admin);
+    public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer)
+            throws RemoteException {
+        enforceCanManageCaCerts(admin, callerPackage);
 
         byte[] pemCert;
         try {
@@ -4647,8 +4702,8 @@
     }
 
     @Override
-    public void uninstallCaCerts(ComponentName admin, String[] aliases) {
-        enforceCanManageCaCerts(admin);
+    public void uninstallCaCerts(ComponentName admin, String callerPackage, String[] aliases) {
+        enforceCanManageCaCerts(admin, callerPackage);
 
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
         final long id = mInjector.binderClearCallingIdentity();
@@ -4672,9 +4727,11 @@
     }
 
     @Override
-    public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, byte[] chain,
-            String alias, boolean requestAccess) {
-        enforceCanManageInstalledKeys(who);
+    public boolean installKeyPair(ComponentName who, String callerPackage, byte[] privKey,
+            byte[] cert, byte[] chain, String alias, boolean requestAccess) {
+        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                DELEGATION_CERT_INSTALL);
+
 
         final int callingUid = mInjector.binderGetCallingUid();
         final long id = mInjector.binderClearCallingIdentity();
@@ -4705,8 +4762,9 @@
     }
 
     @Override
-    public boolean removeKeyPair(ComponentName who, String alias) {
-        enforceCanManageInstalledKeys(who);
+    public boolean removeKeyPair(ComponentName who, String callerPackage, String alias) {
+        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                DELEGATION_CERT_INSTALL);
 
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
         final long id = Binder.clearCallingIdentity();
@@ -4791,33 +4849,267 @@
         }.execute();
     }
 
+    /**
+     * Set the scopes of a device owner or profile owner delegate.
+     *
+     * @param who the device owner or profile owner.
+     * @param delegatePackage the name of the delegate package.
+     * @param scopes the list of delegation scopes to be given to the delegate package.
+     */
     @Override
-    public void setCertInstallerPackage(ComponentName who, String installerPackage)
-            throws SecurityException {
-        int userHandle = UserHandle.getCallingUserId();
+    public void setDelegatedScopes(ComponentName who, String delegatePackage,
+            List<String> scopes) throws SecurityException {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkStringNotEmpty(delegatePackage, "Delegate package is null or empty");
+        Preconditions.checkCollectionElementsNotNull(scopes, "Scopes");
+        // Remove possible duplicates.
+        scopes = new ArrayList(new ArraySet(scopes));
+        // Ensure given scopes are valid.
+        if (scopes.retainAll(Arrays.asList(DELEGATIONS))) {
+            throw new IllegalArgumentException("Unexpected delegation scopes");
+        }
+
+        // Retrieve the user ID of the calling process.
+        final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
+            // Ensure calling process is device/profile owner.
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (getTargetSdk(who.getPackageName(), userHandle) >= Build.VERSION_CODES.N) {
-                if (installerPackage != null &&
-                        !isPackageInstalledForUser(installerPackage, userHandle)) {
-                    throw new IllegalArgumentException("Package " + installerPackage
+            // Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N).
+            if (scopes.size() == 1 && scopes.get(0).equals(DELEGATION_CERT_INSTALL) ||
+                    getTargetSdk(who.getPackageName(), userId) >= Build.VERSION_CODES.N) {
+                // Throw when the delegate package is not installed.
+                if (!isPackageInstalledForUser(delegatePackage, userId)) {
+                    throw new IllegalArgumentException("Package " + delegatePackage
                             + " is not installed on the current user");
                 }
             }
-            DevicePolicyData policy = getUserData(userHandle);
-            policy.mDelegatedCertInstallerPackage = installerPackage;
-            saveSettingsLocked(userHandle);
+
+            // Set the new delegate in user policies.
+            final DevicePolicyData policy = getUserData(userId);
+            if (!scopes.isEmpty()) {
+                policy.mDelegationMap.put(delegatePackage, new ArrayList<>(scopes));
+            } else {
+                // Remove any delegation info if the given scopes list is empty.
+                policy.mDelegationMap.remove(delegatePackage);
+            }
+
+            // Notify delegate package of updates.
+            final Intent intent = new Intent(
+                    DevicePolicyManager.ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED);
+            // Only call receivers registered in the manifest (don’t wake app if not running).
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            // Limit components this intent resolves to to the delegate package.
+            intent.setPackage(delegatePackage);
+            // Include the list of delegated scopes as an extra.
+            intent.putExtra(DevicePolicyManager.EXTRA_DELEGATION_SCOPES, scopes.toArray());
+            // Send the broadcast.
+            mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+
+            // Persist updates.
+            saveSettingsLocked(userId);
+        }
+    }
+
+    /**
+     * Get the delegation scopes given to a delegate package by a device owner or profile owner.
+     *
+     * A DO/PO can get the scopes of any package. A non DO/PO package can get its own scopes by
+     * passing in {@code null} as the {@code who} parameter and its own name as the
+     * {@code delegatepackage}.
+     *
+     * @param who the device owner or profile owner, or {@code null} if the caller is
+     *            {@code delegatePackage}.
+     * @param delegatePackage the name of the delegate package whose scopes are to be retrieved.
+     * @return a list of the delegation scopes currently given to {@code delegatePackage}.
+     */
+    @Override
+    @NonNull
+    public List<String> getDelegatedScopes(ComponentName who,
+            String delegatePackage) throws SecurityException {
+        Preconditions.checkNotNull(delegatePackage, "Delegate package is null");
+
+        // Retrieve the user ID of the calling process.
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int userId = UserHandle.getUserId(callingUid);
+        synchronized (this) {
+            // Ensure calling process is device/profile owner.
+            if (who != null) {
+                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Or ensure calling process is delegatePackage itself.
+            } else {
+                int uid = 0;
+                try {
+                  uid = mInjector.getPackageManager()
+                          .getPackageUidAsUser(delegatePackage, userId);
+                } catch(NameNotFoundException e) {
+                }
+                if (uid != callingUid) {
+                    throw new SecurityException("Caller with uid " + callingUid + " is not "
+                            + delegatePackage);
+                }
+            }
+            final DevicePolicyData policy = getUserData(userId);
+            // Retrieve the scopes assigned to delegatePackage, or null if no scope was given.
+            final List<String> scopes = policy.mDelegationMap.get(delegatePackage);
+            return scopes == null ? Collections.EMPTY_LIST : scopes;
+        }
+    }
+
+    /**
+     * Get a list of  packages that were given a specific delegation scopes by a device owner or
+     * profile owner.
+     *
+     * @param who the device owner or profile owner.
+     * @param scope the scope whose delegates are to be retrieved.
+     * @return a list of the delegate packages currently given the {@code scope} delegation.
+     */
+    @NonNull
+    public List<String> getDelegatePackages(ComponentName who, String scope)
+            throws SecurityException {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkNotNull(scope, "Scope is null");
+        if (!Arrays.asList(DELEGATIONS).contains(scope)) {
+            throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
+        }
+
+        // Retrieve the user ID of the calling process.
+        final int userId = mInjector.userHandleGetCallingUserId();
+        synchronized (this) {
+            // Ensure calling process is device/profile owner.
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final DevicePolicyData policy = getUserData(userId);
+
+            // Create a list to hold the resulting delegate packages.
+            final List<String> delegatePackagesWithScope = new ArrayList<>();
+            // Add all delegations containing scope to the result list.
+            for (int i = 0; i < policy.mDelegationMap.size(); i++) {
+                if (policy.mDelegationMap.valueAt(i).contains(scope)) {
+                    delegatePackagesWithScope.add(policy.mDelegationMap.keyAt(i));
+                }
+            }
+            return delegatePackagesWithScope;
+        }
+    }
+
+    /**
+     * Check whether a caller application has been delegated a given scope via
+     * {@link #setDelegatedScopes} to access privileged APIs on the behalf of a profile owner or
+     * device owner.
+     * <p>
+     * This is done by checking that {@code callerPackage} was granted {@code scope} delegation and
+     * then comparing the calling UID with the UID of {@code callerPackage} as reported by
+     * {@link PackageManager#getPackageUidAsUser}.
+     *
+     * @param callerPackage the name of the package that is trying to invoke a function in the DPMS.
+     * @param scope the delegation scope to be checked.
+     * @return {@code true} if the calling process is a delegate of {@code scope}.
+     */
+    private boolean isCallerDelegate(String callerPackage, String scope) {
+        Preconditions.checkNotNull(callerPackage, "callerPackage is null");
+        if (!Arrays.asList(DELEGATIONS).contains(scope)) {
+            throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
+        }
+
+        // Retrieve the UID and user ID of the calling process.
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int userId = UserHandle.getUserId(callingUid);
+        synchronized (this) {
+            // Retrieve user policy data.
+            final DevicePolicyData policy = getUserData(userId);
+            // Retrieve the list of delegation scopes granted to callerPackage.
+            final List<String> scopes = policy.mDelegationMap.get(callerPackage);
+            // Check callingUid only if callerPackage has the required scope delegation.
+            if (scopes != null && scopes.contains(scope)) {
+                try {
+                    // Retrieve the expected UID for callerPackage.
+                    final int uid = mInjector.getPackageManager()
+                            .getPackageUidAsUser(callerPackage, userId);
+                    // Return true if the caller is actually callerPackage.
+                    return uid == callingUid;
+                } catch (NameNotFoundException e) {
+                    // Ignore.
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Throw a security exception if a ComponentName is given and it is not a device/profile owner
+     * or if the calling process is not a delegate of the given scope.
+     *
+     * @param who the device owner of profile owner, or null if {@code callerPackage} is a
+     *            {@code scope} delegate.
+     * @param callerPackage the name of the calling package. Required if {@code who} is
+     *            {@code null}.
+     * @param reqPolicy the policy used in the API whose access permission is being checked.
+     * @param scoppe the delegation scope corresponding to the API being checked.
+     * @throws SecurityException if {@code who} is given and is not an owner for {@code reqPolicy};
+     *            or when {@code who} is {@code null} and {@code callerPackage} is not a delegate
+     *            of {@code scope}.
+     */
+    private void enforceCanManageScope(ComponentName who, String callerPackage, int reqPolicy,
+            String scope) {
+        // If a ComponentName is given ensure it is a device or profile owner according to policy.
+        if (who != null) {
+            synchronized (this) {
+                getActiveAdminForCallerLocked(who, reqPolicy);
+            }
+        // If no ComponentName is given ensure calling process has scope delegation.
+        } else if (!isCallerDelegate(callerPackage, scope)) {
+            throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid()
+                    + " is not a delegate of scope " + scope + ".");
+        }
+    }
+
+    /**
+     * Helper function to preserve delegation behavior pre-O when using the deprecated functions
+     * {@code #setCertInstallerPackage} and {@code #setApplicationRestrictionsManagingPackage}.
+     */
+    private void setDelegatedScopePreO(ComponentName who,
+            String delegatePackage, String scope) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        final int userId = mInjector.userHandleGetCallingUserId();
+        synchronized(this) {
+            // Ensure calling process is device/profile owner.
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final DevicePolicyData policy = getUserData(userId);
+
+            if (delegatePackage != null) {
+                // Set package as a delegate for scope if it is not already one.
+                List<String> scopes = policy.mDelegationMap.get(delegatePackage);
+                if (scopes == null) {
+                    scopes = new ArrayList<>();
+                }
+                if (!scopes.contains(scope)) {
+                    scopes.add(scope);
+                    setDelegatedScopes(who, delegatePackage, scopes);
+                }
+            }
+
+            // Clear any existing scope delegates.
+            for (int i = 0; i < policy.mDelegationMap.size(); i++) {
+                final String currentPackage = policy.mDelegationMap.keyAt(i);
+                final List<String> currentScopes = policy.mDelegationMap.valueAt(i);
+
+                if (!currentPackage.equals(delegatePackage) && currentScopes.remove(scope)) {
+                    setDelegatedScopes(who, currentPackage, currentScopes);
+                }
+            }
         }
     }
 
     @Override
+    public void setCertInstallerPackage(ComponentName who, String installerPackage)
+            throws SecurityException {
+        setDelegatedScopePreO(who, installerPackage, DELEGATION_CERT_INSTALL);
+    }
+
+    @Override
     public String getCertInstallerPackage(ComponentName who) throws SecurityException {
-        int userHandle = UserHandle.getCallingUserId();
-        synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            DevicePolicyData policy = getUserData(userHandle);
-            return policy.mDelegatedCertInstallerPackage;
-        }
+        final List<String> delegatePackages = getDelegatePackages(who, DELEGATION_CERT_INSTALL);
+        return delegatePackages.size() > 0 ? delegatePackages.get(0) : null;
     }
 
     /**
@@ -4830,9 +5122,7 @@
     @Override
     public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown)
             throws SecurityException {
-        synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        }
+        enforceProfileOrDeviceOwner(admin);
 
         final int userId = mInjector.userHandleGetCallingUserId();
         final long token = mInjector.binderClearCallingIdentity();
@@ -4854,9 +5144,7 @@
     @Override
     public String getAlwaysOnVpnPackage(ComponentName admin)
             throws SecurityException {
-        synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        }
+        enforceProfileOrDeviceOwner(admin);
 
         final int userId = mInjector.userHandleGetCallingUserId();
         final long token = mInjector.binderClearCallingIdentity();
@@ -4869,7 +5157,7 @@
         }
     }
 
-    private void wipeDataNoLock(boolean wipeExtRequested, String reason, boolean force) {
+    private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason) {
         wtfIfInLock();
 
         if (wipeExtRequested) {
@@ -4878,94 +5166,87 @@
             sm.wipeAdoptableDisks();
         }
         try {
-            RecoverySystem.rebootWipeUserData(mContext, false /* shutdown */, reason, force);
+            mInjector.recoverySystemRebootWipeUserData(
+                    /*shutdown=*/ false, reason, /*force=*/ true);
         } catch (IOException | SecurityException e) {
             Slog.w(LOG_TAG, "Failed requesting data wipe", e);
         }
     }
 
+    private void forceWipeUser(int userId) {
+        try {
+            IActivityManager am = mInjector.getIActivityManager();
+            if (am.getCurrentUser().id == userId) {
+                am.switchUser(UserHandle.USER_SYSTEM);
+            }
+
+            boolean userRemoved = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
+            if (!userRemoved) {
+                Slog.w(LOG_TAG, "Couldn't remove user " + userId);
+            } else if (isManagedProfile(userId)) {
+                sendWipeProfileNotification();
+            }
+        } catch (RemoteException re) {
+            // Shouldn't happen
+        }
+    }
+
     @Override
     public void wipeData(int flags) {
         if (!mHasFeature) {
             return;
         }
-        final int userHandle = mInjector.userHandleGetCallingUserId();
-        enforceFullCrossUsersPermission(userHandle);
+        enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
 
-        final String source;
+        final ActiveAdmin admin;
         synchronized (this) {
-            // This API can only be called by an active device admin,
-            // so try to retrieve it to check that the caller is one.
-            final ActiveAdmin admin = getActiveAdminForCallerLocked(null,
-                    DeviceAdminInfo.USES_POLICY_WIPE_DATA);
-            source = admin.info.getComponent().flattenToShortString();
-
-            long ident = mInjector.binderClearCallingIdentity();
-            try {
-                final String restriction;
-                if (userHandle == UserHandle.USER_SYSTEM) {
-                    restriction = UserManager.DISALLOW_FACTORY_RESET;
-                } else if (isManagedProfile(userHandle)) {
-                    restriction = UserManager.DISALLOW_REMOVE_MANAGED_PROFILE;
-                } else {
-                    restriction = UserManager.DISALLOW_REMOVE_USER;
-                }
-                if (isAdminAffectedByRestriction(
-                        admin.info.getComponent(), restriction, userHandle)) {
-                    throw new SecurityException("Cannot wipe data. " + restriction
-                            + " restriction is set for user " + userHandle);
-                }
-
-                if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
-                    if (!isDeviceOwner(admin.info.getComponent(), userHandle)) {
-                        throw new SecurityException(
-                               "Only device owner admins can set WIPE_RESET_PROTECTION_DATA");
-                    }
-                    PersistentDataBlockManager manager = (PersistentDataBlockManager)
-                            mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
-                    if (manager != null) {
-                        manager.wipe();
-                    }
-                }
-
-            } finally {
-                mInjector.binderRestoreCallingIdentity(ident);
-            }
+            admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA);
         }
-        final boolean wipeExtRequested = (flags & WIPE_EXTERNAL_STORAGE) != 0;
-        wipeDeviceNoLock(wipeExtRequested, userHandle,
-                "DevicePolicyManager.wipeData() from " + source, /*force=*/ true);
+        String reason = "DevicePolicyManager.wipeData() from "
+                + admin.info.getComponent().flattenToShortString();
+        wipeDataNoLock(
+                admin.info.getComponent(), flags, reason, admin.getUserHandle().getIdentifier());
     }
 
-    private void wipeDeviceNoLock(
-            boolean wipeExtRequested, final int userHandle, String reason, boolean force) {
+    private void wipeDataNoLock(ComponentName admin, int flags, String reason, int userId) {
         wtfIfInLock();
 
         long ident = mInjector.binderClearCallingIdentity();
         try {
-            // TODO If split user is enabled and the device owner is set in the primary user (rather
-            // than system), we should probably trigger factory reset. Current code just remove
-            // that user (but still clears FRP...)
-            if (userHandle == UserHandle.USER_SYSTEM) {
-                wipeDataNoLock(wipeExtRequested, reason, force);
+            // First check whether the admin is allowed to wipe the device/user/profile.
+            final String restriction;
+            if (userId == UserHandle.USER_SYSTEM) {
+                restriction = UserManager.DISALLOW_FACTORY_RESET;
+            } else if (isManagedProfile(userId)) {
+                restriction = UserManager.DISALLOW_REMOVE_MANAGED_PROFILE;
             } else {
-                try {
-                    IActivityManager am = mInjector.getIActivityManager();
-                    if (am.getCurrentUser().id == userHandle) {
-                        am.switchUser(UserHandle.USER_SYSTEM);
-                    }
+                restriction = UserManager.DISALLOW_REMOVE_USER;
+            }
+            if (isAdminAffectedByRestriction(admin, restriction, userId)) {
+                throw new SecurityException("Cannot wipe data. " + restriction
+                        + " restriction is set for user " + userId);
+            }
 
-                    boolean userRemoved = force
-                            ? mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle)
-                            : mUserManager.removeUser(userHandle);
-                    if (!userRemoved) {
-                        Slog.w(LOG_TAG, "Couldn't remove user " + userHandle);
-                    } else if (isManagedProfile(userHandle)) {
-                        sendWipeProfileNotification();
-                    }
-                } catch (RemoteException re) {
-                    // Shouldn't happen
+            if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
+                if (!isDeviceOwner(admin, userId)) {
+                    throw new SecurityException(
+                            "Only device owner admins can set WIPE_RESET_PROTECTION_DATA");
                 }
+                PersistentDataBlockManager manager = (PersistentDataBlockManager)
+                        mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+                if (manager != null) {
+                    manager.wipe();
+                }
+            }
+
+            // TODO If split user is enabled and the device owner is set in the primary user
+            // (rather than system), we should probably trigger factory reset. Current code just
+            // removes that user (but still clears FRP...)
+            if (userId == UserHandle.USER_SYSTEM) {
+                forceWipeDeviceNoLock(/*wipeExtRequested=*/ (flags & WIPE_EXTERNAL_STORAGE) != 0,
+                        reason);
+            } else {
+                forceWipeUser(userId);
             }
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
@@ -5105,25 +5386,21 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
+        boolean wipeData = false;
+        ActiveAdmin strictestAdmin = null;
         final long ident = mInjector.binderClearCallingIdentity();
         try {
-            boolean wipeData = false;
-            int identifier = 0;
             synchronized (this) {
                 DevicePolicyData policy = getUserData(userHandle);
                 policy.mFailedPasswordAttempts++;
                 saveSettingsLocked(userHandle);
                 if (mHasFeature) {
-                    ActiveAdmin strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked(
+                    strictestAdmin = getAdminWithMinimumFailedPasswordsForWipeLocked(
                             userHandle, /* parent */ false);
                     int max = strictestAdmin != null
                             ? strictestAdmin.maximumFailedPasswordsForWipe : 0;
                     if (max > 0 && policy.mFailedPasswordAttempts >= max) {
-                        // Wipe the user/profile associated with the policy that was violated. This
-                        // is not necessarily calling user: if the policy that fired was from a
-                        // managed profile rather than the main user profile, we wipe former only.
                         wipeData = true;
-                        identifier = strictestAdmin.getUserHandle().getIdentifier();
                     }
 
                     sendAdminCommandForLockscreenPoliciesLocked(
@@ -5131,14 +5408,33 @@
                             DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
                 }
             }
-            if (wipeData) {
-                // Call without holding lock.
-                wipeDeviceNoLock(false, identifier, "reportFailedPasswordAttempt()", false);
-            }
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
 
+        if (wipeData && strictestAdmin != null) {
+            final int userId = strictestAdmin.getUserHandle().getIdentifier();
+            Slog.i(LOG_TAG, "Max failed password attempts policy reached for admin: "
+                    + strictestAdmin.info.getComponent().flattenToShortString()
+                    + ". Calling wipeData for user " + userId);
+
+            // Attempt to wipe the device/user/profile associated with the admin, as if the
+            // admin had called wipeData(). That way we can check whether the admin is actually
+            // allowed to wipe the device (e.g. a regular device admin shouldn't be able to wipe the
+            // device if the device owner has set DISALLOW_FACTORY_RESET, but the DO should be
+            // able to do so).
+            // IMPORTANT: Call without holding the lock to prevent deadlock.
+            try {
+                wipeDataNoLock(strictestAdmin.info.getComponent(),
+                        /*flags=*/ 0,
+                        /*reason=*/ "reportFailedPasswordAttempt()",
+                        userId);
+            } catch (SecurityException e) {
+                Slog.w(LOG_TAG, "Failed to wipe user " + userId
+                        + " after max failed password attempts reached.", e);
+            }
+        }
+
         if (mInjector.securityLogIsLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
                     /*method strength*/ 1);
@@ -5574,7 +5870,7 @@
     }
 
     /**
-     * Set whether auto time is required by the specified admin (must be device owner).
+     * Set whether auto time is required by the specified admin (must be device or profile owner).
      */
     @Override
     public void setAutoTimeRequired(ComponentName who, boolean required) {
@@ -5585,7 +5881,7 @@
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             if (admin.requireAutoTime != required) {
                 admin.requireAutoTime = required;
                 saveSettingsLocked(userHandle);
@@ -5604,7 +5900,7 @@
     }
 
     /**
-     * Returns whether or not auto time is required by the device owner.
+     * Returns whether or not auto time is required by the device owner or any profile owner.
      */
     @Override
     public boolean getAutoTimeRequired() {
@@ -5613,7 +5909,20 @@
         }
         synchronized (this) {
             ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
-            return (deviceOwner != null) ? deviceOwner.requireAutoTime : false;
+            if (deviceOwner != null && deviceOwner.requireAutoTime) {
+                // If the device owner enforces auto time, we don't need to check the PO's
+                return true;
+            }
+
+            // Now check to see if any profile owner on any user enforces auto time
+            for (Integer userId : mOwners.getProfileOwnerKeys()) {
+                ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
+                if (profileOwner != null && profileOwner.requireAutoTime) {
+                    return true;
+                }
+            }
+
+            return false;
         }
     }
 
@@ -6003,32 +6312,38 @@
     }
 
     @Override
-    public void setKeepUninstalledPackages(ComponentName who, List<String> packageList) {
+    public void setKeepUninstalledPackages(ComponentName who, String callerPackage,
+            List<String> packageList) {
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
         Preconditions.checkNotNull(packageList, "packageList is null");
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
-            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-            admin.keepUninstalledPackages = packageList;
+            // Ensure the caller is a DO or a keep uninstalled packages delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+                    DELEGATION_KEEP_UNINSTALLED_PACKAGES);
+            // Get the device owner
+            ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+            // Set list of packages to be kept even if uninstalled.
+            deviceOwner.keepUninstalledPackages = packageList;
+            // Save settings.
             saveSettingsLocked(userHandle);
+            // Notify package manager.
             mInjector.getPackageManagerInternal().setKeepUninstalledPackages(packageList);
         }
     }
 
     @Override
-    public List<String> getKeepUninstalledPackages(ComponentName who) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+    public List<String> getKeepUninstalledPackages(ComponentName who, String callerPackage) {
         if (!mHasFeature) {
             return null;
         }
         // TODO In split system user mode, allow apps on user 0 to query the list
         synchronized (this) {
-            // Check if this is the device owner who is calling
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+            // Ensure the caller is a DO or a keep uninstalled packages delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+                    DELEGATION_KEEP_UNINSTALLED_PACKAGES);
             return getKeepUninstalledPackagesLocked();
         }
     }
@@ -6097,7 +6412,8 @@
             try {
                 // TODO Send to system too?
                 mContext.sendBroadcastAsUser(
-                        new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
+                        new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED)
+                                .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
                         UserHandle.of(userId));
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
@@ -6133,6 +6449,13 @@
         }
     }
 
+    private boolean isProfileOwnerPackage(String packageName, int userId) {
+        synchronized (this) {
+            return mOwners.hasProfileOwner(userId)
+                    && mOwners.getProfileOwnerPackage(userId).equals(packageName);
+        }
+    }
+
     public boolean isProfileOwner(ComponentName who, int userId) {
         final ComponentName profileOwner = getProfileOwner(userId);
         return who != null && who.equals(profileOwner);
@@ -6240,6 +6563,7 @@
                 clearDeviceOwnerLocked(admin, deviceOwnerUserId);
                 removeActiveAdminLocked(deviceOwnerComponent, deviceOwnerUserId);
                 Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
+                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 mContext.sendBroadcastAsUser(intent, UserHandle.of(deviceOwnerUserId));
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
@@ -6374,11 +6698,11 @@
     }
 
     private void clearUserPoliciesLocked(int userId) {
-        // Reset some of the user-specific policies
-        DevicePolicyData policy = getUserData(userId);
+        // Reset some of the user-specific policies.
+        final DevicePolicyData policy = getUserData(userId);
         policy.mPermissionPolicy = DevicePolicyManager.PERMISSION_POLICY_PROMPT;
-        policy.mDelegatedCertInstallerPackage = null;
-        policy.mApplicationRestrictionsManagingPackage = null;
+        // Clear delegations.
+        policy.mDelegationMap.clear();
         policy.mStatusBarDisabled = false;
         policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
         saveSettingsLocked(userId);
@@ -6783,6 +7107,18 @@
         enforceManageUsers();
     }
 
+    private void enforceProfileOwnerOrSystemUser(ComponentName admin) {
+        synchronized (this) {
+            if (getActiveAdminWithPolicyForUidLocked(admin,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
+                            != null) {
+                return;
+            }
+        }
+        Preconditions.checkState(isCallerWithSystemUid(),
+                "Only profile owner, device owner and system may call this method.");
+    }
+
     private void ensureCallerPackage(@Nullable String packageName) {
         if (packageName == null) {
             Preconditions.checkState(isCallerWithSystemUid(),
@@ -6950,68 +7286,31 @@
     @Override
     public boolean setApplicationRestrictionsManagingPackage(ComponentName admin,
             String packageName) {
-        Preconditions.checkNotNull(admin, "ComponentName is null");
-
-        final int userHandle = mInjector.userHandleGetCallingUserId();
-        synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (packageName != null && !isPackageInstalledForUser(packageName, userHandle)) {
-                return false;
-            }
-            DevicePolicyData policy = getUserData(userHandle);
-            policy.mApplicationRestrictionsManagingPackage = packageName;
-            saveSettingsLocked(userHandle);
-            return true;
+        try {
+            setDelegatedScopePreO(admin, packageName, DELEGATION_APP_RESTRICTIONS);
+        } catch (IllegalArgumentException e) {
+            return false;
         }
+        return true;
     }
 
     @Override
     public String getApplicationRestrictionsManagingPackage(ComponentName admin) {
-        Preconditions.checkNotNull(admin, "ComponentName is null");
-
-        final int userHandle = mInjector.userHandleGetCallingUserId();
-        synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            DevicePolicyData policy = getUserData(userHandle);
-            return policy.mApplicationRestrictionsManagingPackage;
-        }
+        final List<String> delegatePackages = getDelegatePackages(admin,
+                DELEGATION_APP_RESTRICTIONS);
+        return delegatePackages.size() > 0 ? delegatePackages.get(0) : null;
     }
 
     @Override
-    public boolean isCallerApplicationRestrictionsManagingPackage() {
-        final int callingUid = mInjector.binderGetCallingUid();
-        final int userHandle = UserHandle.getUserId(callingUid);
-        synchronized (this) {
-            final DevicePolicyData policy = getUserData(userHandle);
-            if (policy.mApplicationRestrictionsManagingPackage == null) {
-                return false;
-            }
-
-            try {
-                int uid = mInjector.getPackageManager().getPackageUidAsUser(
-                        policy.mApplicationRestrictionsManagingPackage, userHandle);
-                return uid == callingUid;
-            } catch (NameNotFoundException e) {
-                return false;
-            }
-        }
-    }
-
-    private void enforceCanManageApplicationRestrictions(ComponentName who) {
-        if (who != null) {
-            synchronized (this) {
-                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            }
-        } else if (!isCallerApplicationRestrictionsManagingPackage()) {
-            throw new SecurityException(
-                    "No admin component given, and caller cannot manage application restrictions "
-                    + "for other apps.");
-        }
+    public boolean isCallerApplicationRestrictionsManagingPackage(String callerPackage) {
+        return isCallerDelegate(callerPackage, DELEGATION_APP_RESTRICTIONS);
     }
 
     @Override
-    public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
-        enforceCanManageApplicationRestrictions(who);
+    public void setApplicationRestrictions(ComponentName who, String callerPackage,
+            String packageName, Bundle settings) {
+        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                DELEGATION_APP_RESTRICTIONS);
 
         final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
         final long id = mInjector.binderClearCallingIdentity();
@@ -7701,8 +8000,10 @@
     }
 
     @Override
-    public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
-        enforceCanManageApplicationRestrictions(who);
+    public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
+            String packageName) {
+        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                DELEGATION_APP_RESTRICTIONS);
 
         final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
         final long id = mInjector.binderClearCallingIdentity();
@@ -7717,12 +8018,13 @@
     }
 
     @Override
-    public String[] setPackagesSuspended(ComponentName who, String[] packageNames,
-            boolean suspended) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+    public String[] setPackagesSuspended(ComponentName who, String callerPackage,
+            String[] packageNames, boolean suspended) {
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or a package access delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_PACKAGE_ACCESS);
 
             long id = mInjector.binderClearCallingIdentity();
             try {
@@ -7739,10 +8041,12 @@
     }
 
     @Override
-    public boolean isPackageSuspended(ComponentName who, String packageName) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+    public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
+            // Ensure the caller is a DO/PO or a package access delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_PACKAGE_ACCESS);
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             long id = mInjector.binderClearCallingIdentity();
@@ -7767,7 +8071,7 @@
 
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
-            ActiveAdmin activeAdmin =
+            final ActiveAdmin activeAdmin =
                     getActiveAdminForCallerLocked(who,
                             DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             final boolean isDeviceOwner = isDeviceOwner(who, userHandle);
@@ -7782,7 +8086,12 @@
             }
 
             // Save the restriction to ActiveAdmin.
-            activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
+            final Bundle restrictions = activeAdmin.ensureUserRestrictions();
+            if (enabledFromThisOwner) {
+                restrictions.putBoolean(key, true);
+            } else {
+                restrictions.remove(key);
+            }
             saveUserRestrictionsLocked(userHandle);
         }
     }
@@ -7795,39 +8104,46 @@
 
     private void pushUserRestrictions(int userId) {
         synchronized (this) {
-            final Bundle global;
-            final Bundle local = new Bundle();
-            if (mOwners.isDeviceOwnerUserId(userId)) {
-                global = new Bundle();
+            final boolean isDeviceOwner = mOwners.isDeviceOwnerUserId(userId);
+            final Bundle userRestrictions;
+            // Whether device owner enforces camera restriction.
+            boolean disallowCameraGlobally = false;
 
+            if (isDeviceOwner) {
                 final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
                 if (deviceOwner == null) {
                     return; // Shouldn't happen.
                 }
-
-                UserRestrictionsUtils.sortToGlobalAndLocal(deviceOwner.userRestrictions,
-                        global, local);
+                userRestrictions = deviceOwner.userRestrictions;
                 // DO can disable camera globally.
-                if (deviceOwner.disableCamera) {
-                    global.putBoolean(UserManager.DISALLOW_CAMERA, true);
-                }
+                disallowCameraGlobally = deviceOwner.disableCamera;
             } else {
-                global = null;
+                final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
+                userRestrictions = profileOwner != null ? profileOwner.userRestrictions : null;
+            }
 
-                ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
-                if (profileOwner != null) {
-                    UserRestrictionsUtils.merge(local, profileOwner.userRestrictions);
-                }
-            }
-            // Also merge in *local* camera restriction.
-            if (getCameraDisabled(/* who= */ null,
-                    userId, /* mergeDeviceOwnerRestriction= */ false)) {
-                local.putBoolean(UserManager.DISALLOW_CAMERA, true);
-            }
-            mUserManagerInternal.setDevicePolicyUserRestrictions(userId, local, global);
+            // Whether any admin enforces camera restriction.
+            final int cameraRestrictionScope =
+                    getCameraRestrictionScopeLocked(userId, disallowCameraGlobally);
+
+            mUserManagerInternal.setDevicePolicyUserRestrictions(userId, userRestrictions,
+                    isDeviceOwner, cameraRestrictionScope);
         }
     }
 
+    /**
+     * Get the scope of camera restriction for a given user if any.
+     */
+    private int getCameraRestrictionScopeLocked(int userId, boolean disallowCameraGlobally) {
+        if (disallowCameraGlobally) {
+            return UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
+        } else if (getCameraDisabled(
+                /* who= */ null, userId, /* mergeDeviceOwnerRestriction= */ false)) {
+            return UserManagerInternal.CAMERA_DISABLED_LOCALLY;
+        }
+        return UserManagerInternal.CAMERA_NOT_DISABLED;
+    }
+
     @Override
     public Bundle getUserRestrictions(ComponentName who) {
         if (!mHasFeature) {
@@ -7842,12 +8158,13 @@
     }
 
     @Override
-    public boolean setApplicationHidden(ComponentName who, String packageName,
+    public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
             boolean hidden) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or a package access delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_PACKAGE_ACCESS);
 
             long id = mInjector.binderClearCallingIdentity();
             try {
@@ -7864,11 +8181,13 @@
     }
 
     @Override
-    public boolean isApplicationHidden(ComponentName who, String packageName) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+    public boolean isApplicationHidden(ComponentName who, String callerPackage,
+            String packageName) {
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or a package access delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_PACKAGE_ACCESS);
 
             long id = mInjector.binderClearCallingIdentity();
             try {
@@ -7885,12 +8204,11 @@
     }
 
     @Override
-    public void enableSystemApp(ComponentName who, String packageName) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+    public void enableSystemApp(ComponentName who, String callerPackage, String packageName) {
         synchronized (this) {
-            // This API can only be called by an active device admin,
-            // so try to retrieve it to check that the caller is one.
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or an enable system app delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_ENABLE_SYSTEM_APP);
 
             int userId = UserHandle.getCallingUserId();
             long id = mInjector.binderClearCallingIdentity();
@@ -7920,12 +8238,11 @@
     }
 
     @Override
-    public int enableSystemAppWithIntent(ComponentName who, Intent intent) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+    public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) {
         synchronized (this) {
-            // This API can only be called by an active device admin,
-            // so try to retrieve it to check that the caller is one.
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or an enable system app delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_ENABLE_SYSTEM_APP);
 
             int userId = UserHandle.getCallingUserId();
             long id = mInjector.binderClearCallingIdentity();
@@ -8024,12 +8341,13 @@
     }
 
     @Override
-    public void setUninstallBlocked(ComponentName who, String packageName,
+    public void setUninstallBlocked(ComponentName who, String callerPackage, String packageName,
             boolean uninstallBlocked) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
         final int userId = UserHandle.getCallingUserId();
         synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or a block uninstall delegate
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_BLOCK_UNINSTALL);
 
             long id = mInjector.binderClearCallingIdentity();
             try {
@@ -8643,18 +8961,20 @@
 
             ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
             if (profileOwner != null) {
-                return createShowAdminSupportIntent(profileOwner, userId);
+                return DevicePolicyManagerService.this
+                        .createShowAdminSupportIntent(profileOwner, userId);
             }
 
             final Pair<Integer, ComponentName> deviceOwner =
                     mOwners.getDeviceOwnerUserIdAndComponent();
             if (deviceOwner != null && deviceOwner.first == userId) {
-                return createShowAdminSupportIntent(deviceOwner.second, userId);
+                return DevicePolicyManagerService.this
+                        .createShowAdminSupportIntent(deviceOwner.second, userId);
             }
 
             // We're not specifying the device admin because there isn't one.
             if (useDefaultIfNoAdmin) {
-                return createShowAdminSupportIntent(null, userId);
+                return DevicePolicyManagerService.this.createShowAdminSupportIntent(null, userId);
             }
             return null;
         }
@@ -8682,11 +9002,12 @@
             if (enforcedByDo && enforcedByPo) {
                 // In this case, we'll show an admin support dialog that does not
                 // specify the admin.
-                return createShowAdminSupportIntent(null, userId);
+                return DevicePolicyManagerService.this.createShowAdminSupportIntent(null, userId);
             } else if (enforcedByPo) {
                 final ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
                 if (profileOwner != null) {
-                    return createShowAdminSupportIntent(profileOwner, userId);
+                    return DevicePolicyManagerService.this
+                            .createShowAdminSupportIntent(profileOwner, userId);
                 }
                 // This could happen if another thread has changed the profile owner since we called
                 // getUserRestrictionSource
@@ -8695,7 +9016,8 @@
                 final Pair<Integer, ComponentName> deviceOwner
                         = mOwners.getDeviceOwnerUserIdAndComponent();
                 if (deviceOwner != null) {
-                    return createShowAdminSupportIntent(deviceOwner.second, deviceOwner.first);
+                    return DevicePolicyManagerService.this
+                            .createShowAdminSupportIntent(deviceOwner.second, deviceOwner.first);
                 }
                 // This could happen if another thread has changed the device owner since we called
                 // getUserRestrictionSource
@@ -8703,15 +9025,57 @@
             }
             return null;
         }
+    }
 
-        private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
-            // This method is called with AMS lock held, so don't take DPMS lock
-            final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
-            intent.putExtra(Intent.EXTRA_USER_ID, userId);
-            intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            return intent;
+    private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
+        // This method is called with AMS lock held, so don't take DPMS lock
+        final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+        intent.putExtra(Intent.EXTRA_USER_ID, userId);
+        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
+
+    @Override
+    public Intent createAdminSupportIntent(String restriction) {
+        Preconditions.checkNotNull(restriction);
+        final int uid = mInjector.binderGetCallingUid();
+        final int userId = UserHandle.getUserId(uid);
+        Intent intent = null;
+        if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
+                DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+            synchronized(this) {
+                final DevicePolicyData policy = getUserData(userId);
+                final int N = policy.mAdminList.size();
+                for (int i = 0; i < N; i++) {
+                    final ActiveAdmin admin = policy.mAdminList.get(i);
+                    if ((admin.disableCamera &&
+                                DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
+                        (admin.disableScreenCapture && DevicePolicyManager
+                                .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
+                        intent = createShowAdminSupportIntent(admin.info.getComponent(), userId);
+                        break;
+                    }
+                }
+                // For the camera, a device owner on a different user can disable it globally,
+                // so we need an additional check.
+                if (intent == null
+                        && DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
+                    final ActiveAdmin admin = getDeviceOwnerAdminLocked();
+                    if (admin != null && admin.disableCamera) {
+                        intent = createShowAdminSupportIntent(admin.info.getComponent(),
+                                mOwners.getDeviceOwnerUserId());
+                    }
+                }
+            }
+        } else {
+            // if valid, |restriction| can only be a user restriction
+            intent = mLocalService.createUserRestrictionSupportIntent(userId, restriction);
         }
+        if (intent != null) {
+            intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, restriction);
+        }
+        return intent;
     }
 
     /**
@@ -8792,7 +9156,7 @@
     }
 
     @Override
-    public void notifyPendingSystemUpdate(long updateReceivedTime) {
+    public void notifyPendingSystemUpdate(@Nullable SystemUpdateInfo info) {
         mContext.enforceCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE,
                 "Only the system update service can broadcast update information");
 
@@ -8801,9 +9165,15 @@
                     "can broadcast update information.");
             return;
         }
-        final Intent intent = new Intent(DeviceAdminReceiver.ACTION_NOTIFY_PENDING_SYSTEM_UPDATE);
-        intent.putExtra(DeviceAdminReceiver.EXTRA_SYSTEM_UPDATE_RECEIVED_TIME,
-                updateReceivedTime);
+
+        if (!mOwners.saveSystemUpdateInfo(info)) {
+            // Pending system update hasn't changed, don't send duplicate notification.
+            return;
+        }
+
+        final Intent intent = new Intent(DeviceAdminReceiver.ACTION_NOTIFY_PENDING_SYSTEM_UPDATE)
+                .putExtra(DeviceAdminReceiver.EXTRA_SYSTEM_UPDATE_RECEIVED_TIME,
+                        info == null ? -1 : info.getReceivedTime());
 
         final long ident = mInjector.binderClearCallingIdentity();
         try {
@@ -8842,10 +9212,21 @@
     }
 
     @Override
-    public void setPermissionPolicy(ComponentName admin, int policy) throws RemoteException {
+    public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin) {
+        Preconditions.checkNotNull(admin, "ComponentName is null");
+        enforceProfileOrDeviceOwner(admin);
+
+        return mOwners.getSystemUpdateInfo();
+    }
+
+    @Override
+    public void setPermissionPolicy(ComponentName admin, String callerPackage, int policy)
+            throws RemoteException {
         int userId = UserHandle.getCallingUserId();
         synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or a permission grant state delegate.
+            enforceCanManageScope(admin, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_PERMISSION_GRANT);
             DevicePolicyData userPolicy = getUserData(userId);
             if (userPolicy.mPermissionPolicy != policy) {
                 userPolicy.mPermissionPolicy = policy;
@@ -8864,11 +9245,13 @@
     }
 
     @Override
-    public boolean setPermissionGrantState(ComponentName admin, String packageName,
-            String permission, int grantState) throws RemoteException {
+    public boolean setPermissionGrantState(ComponentName admin, String callerPackage,
+            String packageName, String permission, int grantState) throws RemoteException {
         UserHandle user = mInjector.binderGetCallingUserHandle();
         synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or a permission grant state delegate.
+            enforceCanManageScope(admin, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_PERMISSION_GRANT);
             long ident = mInjector.binderClearCallingIdentity();
             try {
                 if (getTargetSdk(packageName, user.getIdentifier())
@@ -8908,13 +9291,16 @@
     }
 
     @Override
-    public int getPermissionGrantState(ComponentName admin, String packageName,
-            String permission) throws RemoteException {
+    public int getPermissionGrantState(ComponentName admin, String callerPackage,
+            String packageName, String permission) throws RemoteException {
         PackageManager packageManager = mInjector.getPackageManager();
 
         UserHandle user = mInjector.binderGetCallingUserHandle();
+        enforceProfileOwnerOrSystemUser(admin);
         synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Ensure the caller is a DO/PO or a permission grant state delegate.
+            enforceCanManageScope(admin, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_PERMISSION_GRANT);
             long ident = mInjector.binderClearCallingIdentity();
             try {
                 int granted = mIPackageManager.checkPermission(permission,
@@ -9070,21 +9456,25 @@
         final long ident = mInjector.binderClearCallingIdentity();
         try {
             final UserHandle callingUserHandle = UserHandle.of(callingUserId);
-            if (mUserManager.hasUserRestriction(
-                    UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle)) {
-                // The DO can initiate provisioning if the restriction was set by the DO.
-                if (!isDeviceOwnerPackage(packageName, callingUserId)
-                        || isAdminAffectedByRestriction(mOwners.getDeviceOwnerComponent(),
-                                UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserId)) {
-                    // Caller is not DO or the restriction was set by the system.
+            final ComponentName ownerAdmin = getOwnerComponent(packageName, callingUserId);
+            if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+                    callingUserHandle)) {
+                // An admin can initiate provisioning if it has set the restriction.
+                if (ownerAdmin == null || isAdminAffectedByRestriction(ownerAdmin,
+                        UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserId)) {
                     return CODE_ADD_MANAGED_PROFILE_DISALLOWED;
                 }
             }
-
-            // TODO: Allow it if the caller is the DO? DO could just call removeUser() before
-            // provisioning, so not strictly required...
-            boolean canRemoveProfile = !mUserManager.hasUserRestriction(
-                        UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, callingUserHandle);
+            boolean canRemoveProfile = true;
+            if (mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                    callingUserHandle)) {
+                // We can remove a profile if the admin itself has set the restriction.
+                if (ownerAdmin == null || isAdminAffectedByRestriction(ownerAdmin,
+                        UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                        callingUserId)) {
+                    canRemoveProfile = false;
+                }
+            }
             if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
@@ -9094,6 +9484,16 @@
         return CODE_OK;
     }
 
+    private ComponentName getOwnerComponent(String packageName, int userId) {
+        if (isDeviceOwnerPackage(packageName, userId)) {
+            return mOwners.getDeviceOwnerComponent();
+        }
+        if (isProfileOwnerPackage(packageName, userId)) {
+            return mOwners.getProfileOwnerComponent(userId);
+        }
+        return null;
+    }
+
     private int checkManagedUserProvisioningPreCondition(int callingUserId) {
         if (!hasFeatureManagedUsers()) {
             return CODE_MANAGED_USERS_NOT_SUPPORTED;
@@ -9168,9 +9568,7 @@
 
     @Override
     public boolean isManagedProfile(ComponentName admin) {
-        synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        }
+        enforceProfileOrDeviceOwner(admin);
         return isManagedProfile(mInjector.userHandleGetCallingUserId());
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index 7d68412..9b4de043 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -16,12 +16,15 @@
 
 package com.android.server.devicepolicy;
 
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.NetworkEvent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.SystemClock;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -42,11 +45,26 @@
     // If this value changes, update DevicePolicyManager#retrieveNetworkLogs() javadoc
     private static final int MAX_EVENTS_PER_BATCH = 1200;
     private static final long BATCH_FINALIZATION_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(90);
+    private static final long BATCH_FINALIZATION_TIMEOUT_ALARM_INTERVAL_MS =
+            TimeUnit.MINUTES.toMillis(30);
 
-    static final int LOG_NETWORK_EVENT_MSG = 1;
-    static final int FINALIZE_BATCH_MSG = 2;
+    private static final String NETWORK_LOGGING_TIMEOUT_ALARM_TAG = "NetworkLogging.batchTimeout";
 
     private final DevicePolicyManagerService mDpm;
+    private final AlarmManager mAlarmManager;
+
+    private final OnAlarmListener mBatchTimeoutAlarmListener = new OnAlarmListener() {
+        @Override
+        public void onAlarm() {
+            Log.d(TAG, "Received a batch finalization timeout alarm, finalizing "
+                    + mNetworkEvents.size() + " pending events.");
+            synchronized (NetworkLoggingHandler.this) {
+                finalizeBatchAndNotifyDeviceOwnerLocked();
+            }
+        }
+    };
+
+    static final int LOG_NETWORK_EVENT_MSG = 1;
 
     // threadsafe as it's Handler's thread confined
     @GuardedBy("this")
@@ -68,6 +86,7 @@
     NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm) {
         super(looper);
         mDpm = dpm;
+        mAlarmManager = mDpm.mInjector.getAlarmManager();
     }
 
     @Override
@@ -85,19 +104,19 @@
                 }
                 break;
             }
-            case FINALIZE_BATCH_MSG: {
-                synchronized (NetworkLoggingHandler.this) {
-                    finalizeBatchAndNotifyDeviceOwnerLocked();
-                }
+            default: {
+                Log.d(TAG, "NetworkLoggingHandler received an unknown of message.");
                 break;
             }
         }
     }
 
     void scheduleBatchFinalization() {
-        removeMessages(FINALIZE_BATCH_MSG);
-        sendMessageDelayed(obtainMessage(FINALIZE_BATCH_MSG), BATCH_FINALIZATION_TIMEOUT_MS);
-        Log.d(TAG, "Scheduled new batch finalization " + BATCH_FINALIZATION_TIMEOUT_MS
+        final long when = SystemClock.elapsedRealtime() + BATCH_FINALIZATION_TIMEOUT_MS;
+        mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
+                BATCH_FINALIZATION_TIMEOUT_ALARM_INTERVAL_MS, NETWORK_LOGGING_TIMEOUT_ALARM_TAG,
+                mBatchTimeoutAlarmListener, this);
+        Log.d(TAG, "Scheduled a new batch finalization alarm " + BATCH_FINALIZATION_TIMEOUT_MS
                 + "ms from now.");
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index b53933e..a5500dd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -17,6 +17,7 @@
 package com.android.server.devicepolicy;
 
 import android.annotation.Nullable;
+import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.content.ComponentName;
 import android.content.pm.PackageManagerInternal;
@@ -47,13 +48,14 @@
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import libcore.io.IoUtils;
 
 /**
- * Stores and restores state for the Device and Profile owners. By definition there can be
- * only one device owner, but there may be a profile owner for each user.
+ * Stores and restores state for the Device and Profile owners and related device-wide information.
+ * By definition there can be only one device owner, but there may be a profile owner for each user.
  *
  * <p>This class is thread safe, so individual methods can safely be called without locking.
  * However, caller must still synchronize on their side to ensure integrity between multiple calls.
@@ -65,6 +67,7 @@
 
     private static final String DEVICE_OWNER_XML_LEGACY = "device_owner.xml";
 
+    // XML storing device owner info, system update policy and pending OTA update information.
     private static final String DEVICE_OWNER_XML = "device_owner_2.xml";
 
     private static final String PROFILE_OWNER_XML = "profile_owner.xml";
@@ -73,6 +76,8 @@
 
     private static final String TAG_DEVICE_OWNER = "device-owner";
     private static final String TAG_DEVICE_INITIALIZER = "device-initializer";
+    private static final String TAG_SYSTEM_UPDATE_POLICY = "system-update-policy";
+    private static final String TAG_PENDING_OTA_INFO = "pending-ota-info";
     private static final String TAG_PROFILE_OWNER = "profile-owner";
     // Holds "context" for device-owner, this must not be show up before device-owner.
     private static final String TAG_DEVICE_OWNER_CONTEXT = "device-owner-context";
@@ -85,8 +90,6 @@
     private static final String ATTR_USERID = "userId";
     private static final String ATTR_USER_RESTRICTIONS_MIGRATED = "userRestrictionsMigrated";
 
-    private static final String TAG_SYSTEM_UPDATE_POLICY = "system-update-policy";
-
     private final UserManager mUserManager;
     private final UserManagerInternal mUserManagerInternal;
     private final PackageManagerInternal mPackageManagerInternal;
@@ -102,6 +105,10 @@
     // Local system update policy controllable by device owner.
     private SystemUpdatePolicy mSystemUpdatePolicy;
 
+    // Pending OTA info if there is one.
+    @Nullable
+    private SystemUpdateInfo mSystemUpdateInfo;
+
     private final Object mLock = new Object();
 
     public Owners(UserManager userManager,
@@ -468,6 +475,32 @@
         }
     }
 
+    /**
+     * Saves the given {@link SystemUpdateInfo} if it is different from the existing one, or if
+     * none exists.
+     *
+     * @return Whether the saved system update information has changed.
+     */
+    boolean saveSystemUpdateInfo(@Nullable SystemUpdateInfo newInfo) {
+        synchronized (mLock) {
+            // Check if we already have the same update information.
+            if (Objects.equals(newInfo, mSystemUpdateInfo)) {
+                return false;
+            }
+
+            mSystemUpdateInfo = newInfo;
+            new DeviceOwnerReadWriter().writeToFileLocked();
+            return true;
+        }
+    }
+
+    @Nullable
+    public SystemUpdateInfo getSystemUpdateInfo() {
+        synchronized (mLock) {
+            return mSystemUpdateInfo;
+        }
+    }
+
     private abstract static class FileReadWriter {
         private final File mFile;
 
@@ -573,7 +606,7 @@
                     }
                 }
             } catch (XmlPullParserException | IOException e) {
-                Slog.e(TAG, "Error parsing device-owner file", e);
+                Slog.e(TAG, "Error parsing owners information file", e);
             } finally {
                 IoUtils.closeQuietly(input);
             }
@@ -592,7 +625,8 @@
 
         @Override
         boolean shouldWrite() {
-            return (mDeviceOwner != null) || (mSystemUpdatePolicy != null);
+            return (mDeviceOwner != null) || (mSystemUpdatePolicy != null)
+                    || (mSystemUpdateInfo != null);
         }
 
         @Override
@@ -609,6 +643,10 @@
                 mSystemUpdatePolicy.saveToXml(out);
                 out.endTag(null, TAG_SYSTEM_UPDATE_POLICY);
             }
+
+            if (mSystemUpdateInfo != null) {
+                mSystemUpdateInfo.writeToXml(out, TAG_PENDING_OTA_INFO);
+            }
         }
 
         @Override
@@ -637,6 +675,9 @@
                 case TAG_SYSTEM_UPDATE_POLICY:
                     mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
                     break;
+                case TAG_PENDING_OTA_INFO:
+                    mSystemUpdateInfo = SystemUpdateInfo.readFromXml(parser);
+                    break;
                 default:
                     Slog.e(TAG, "Unexpected tag: " + tag);
                     return false;
@@ -783,7 +824,6 @@
         }
         if (mSystemUpdatePolicy != null) {
             if (needBlank) {
-                needBlank = false;
                 pw.println();
             }
             pw.println(prefix + "System Update Policy: " + mSystemUpdatePolicy);
@@ -792,7 +832,6 @@
         if (mProfileOwners != null) {
             for (Map.Entry<Integer, OwnerInfo> entry : mProfileOwners.entrySet()) {
                 if (needBlank) {
-                    needBlank = false;
                     pw.println();
                 }
                 pw.println(prefix + "Profile Owner (User " + entry.getKey() + "): ");
@@ -800,6 +839,13 @@
                 needBlank = true;
             }
         }
+        if (mSystemUpdateInfo != null) {
+            if (needBlank) {
+                pw.println();
+            }
+            pw.println(prefix + "Pending System Update: " + mSystemUpdateInfo);
+            needBlank = true;
+        }
     }
 
     File getLegacyConfigFileWithTestOverride() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 08fb591..c3ef23b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -280,14 +280,8 @@
             Slog.i(TAG, "Entered the Android system server!");
             int uptimeMillis = (int) SystemClock.elapsedRealtime();
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis);
-            if (!mRuntimeRestart && !mFirstBoot) {
+            if (!mRuntimeRestart) {
                 MetricsLogger.histogram(null, "boot_system_server_init", uptimeMillis);
-                // Also report when first stage of init has started
-                long initStartNs = SystemProperties.getLong("ro.boottime.init", -1);
-                if (initStartNs >= 0) {
-                    MetricsLogger.histogram(null, "boot_android_init",
-                            (int)(initStartNs / 1000000));
-                }
             }
 
             // In case the runtime switched since last boot (such as when
@@ -355,7 +349,6 @@
             // Create the system service manager.
             mSystemServiceManager = new SystemServiceManager(mSystemContext);
             mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
-            mSystemServiceManager.setFirstBoot(mFirstBoot);
             LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
             // Prepare the thread pool for init tasks that can be parallelized
             SystemServerInitThreadPool.get();
@@ -382,9 +375,14 @@
         if (StrictMode.conditionallyEnableDebugLogging()) {
             Slog.i(TAG, "Enabled StrictMode for system server main thread.");
         }
-        if (!mRuntimeRestart && !mFirstBoot) {
-            MetricsLogger.histogram(null, "boot_system_server_ready",
-                    (int) SystemClock.elapsedRealtime());
+        if (!mRuntimeRestart && !isFirstBootOrUpgrade()) {
+            int uptimeMillis = (int) SystemClock.elapsedRealtime();
+            MetricsLogger.histogram(null, "boot_system_server_ready", uptimeMillis);
+            final int MAX_UPTIME_MILLIS = 60 * 1000;
+            if (uptimeMillis > MAX_UPTIME_MILLIS) {
+                Slog.wtf("SystemServerTiming",
+                        "SystemServer init took too long. uptimeMillis=" + uptimeMillis);
+            }
         }
 
         // Loop forever.
@@ -392,6 +390,10 @@
         throw new RuntimeException("Main thread loop unexpectedly exited");
     }
 
+    private boolean isFirstBootOrUpgrade() {
+        return mPackageManagerService.isFirstBoot() || mPackageManagerService.isUpgrade();
+    }
+
     private void reportWtf(String msg, Throwable e) {
         Slog.w(TAG, "***********************************************");
         Slog.wtf(TAG, "BOOT FAILURE " + msg, e);
@@ -487,6 +489,18 @@
         mActivityManagerService.initPowerManagement();
         traceEnd();
 
+        // Bring up recovery system in case a rescue party needs a reboot
+        if (!SystemProperties.getBoolean("config.disable_noncore", false)) {
+            traceBeginAndSlog("StartRecoverySystemService");
+            mSystemServiceManager.startService(RecoverySystemService.class);
+            traceEnd();
+        }
+
+        // Now that we have the bare essentials of the OS up and running, take
+        // note that we just booted, which might send out a rescue party if
+        // we're stuck in a runtime restart loop.
+        RescueParty.noteBoot(mSystemContext);
+
         // Manages LEDs and display backlight so we need it to bring up the display.
         traceBeginAndSlog("StartLightsService");
         mSystemServiceManager.startService(LightsService.class);
@@ -524,7 +538,7 @@
         mFirstBoot = mPackageManagerService.isFirstBoot();
         mPackageManager = mSystemContext.getPackageManager();
         traceEnd();
-        if (!mRuntimeRestart && !mFirstBoot) {
+        if (!mRuntimeRestart && !isFirstBootOrUpgrade()) {
             MetricsLogger.histogram(null, "boot_package_manager_init_ready",
                     (int) SystemClock.elapsedRealtime());
         }
@@ -644,6 +658,11 @@
 
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
 
+        // For debugging RescueParty
+        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_system", false)) {
+            throw new RuntimeException();
+        }
+
         try {
             Slog.i(TAG, "Reading configuration...");
             traceBeginAndSlog("ReadingSystemConfig");
@@ -920,6 +939,12 @@
                 traceEnd();
             }
 
+            if (!disableNonCoreServices) {
+                traceBeginAndSlog("StartFontServiceManager");
+                mSystemServiceManager.startService(FontManagerService.Lifecycle.class);
+                traceEnd();
+            }
+
             if (!disableNonCoreServices && !disableTextServices) {
                 traceBeginAndSlog("StartTextServicesManager");
                 mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
@@ -955,6 +980,21 @@
                 }
                 traceEnd();
 
+                // Wifi Service must be started first for wifi-related services.
+                traceBeginAndSlog("StartWifi");
+                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+                traceEnd();
+                traceBeginAndSlog("StartWifiScanning");
+                mSystemServiceManager.startService(
+                        "com.android.server.wifi.scanner.WifiScanningService");
+                traceEnd();
+
+                if (!disableRtt) {
+                    traceBeginAndSlog("StartWifiRtt");
+                    mSystemServiceManager.startService("com.android.server.wifi.RttService");
+                    traceEnd();
+                }
+
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_WIFI_AWARE)) {
                     traceBeginAndSlog("StartWifiAware");
@@ -970,19 +1010,6 @@
                     mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
                     traceEnd();
                 }
-                traceBeginAndSlog("StartWifi");
-                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-                traceEnd();
-                traceBeginAndSlog("StartWifiScanning");
-                mSystemServiceManager.startService(
-                            "com.android.server.wifi.scanner.WifiScanningService");
-                traceEnd();
-
-                if (!disableRtt) {
-                    traceBeginAndSlog("StartWifiRtt");
-                    mSystemServiceManager.startService("com.android.server.wifi.RttService");
-                    traceEnd();
-                }
 
                 if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
                     mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
@@ -1025,12 +1052,6 @@
                 traceEnd();
             }
 
-            if (!disableNonCoreServices) {
-                traceBeginAndSlog("StartRecoverSystemService");
-                mSystemServiceManager.startService(RecoverySystemService.class);
-                traceEnd();
-            }
-
             /*
              * StorageManagerService has a few dependencies: Notification Manager and
              * AppWidget Provider. Make sure StorageManagerService is completely started
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 8dd05b1..2624f0b 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -30,6 +30,7 @@
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
+import android.net.TrafficStats;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.DhcpClientEvent;
 import android.net.metrics.DhcpErrorEvent;
@@ -303,6 +304,7 @@
     }
 
     private boolean initUdpSocket() {
+        final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
         try {
             mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
@@ -314,6 +316,8 @@
         } catch(SocketException|ErrnoException e) {
             Log.e(TAG, "Error creating UDP socket", e);
             return false;
+        } finally {
+            TrafficStats.setThreadStatsTag(oldTag);
         }
         return true;
     }
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 87018ec..abdf683 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -865,13 +865,7 @@
         for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
             newLp.addRoute(route);
         }
-        for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
-            // Only add likely reachable DNS servers.
-            // TODO: investigate deleting this.
-            if (newLp.isReachable(dns)) {
-                newLp.addDnsServer(dns);
-            }
-        }
+        addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
 
         // [3] Add in data from DHCPv4, if available.
         //
@@ -881,13 +875,7 @@
             for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
                 newLp.addRoute(route);
             }
-            for (InetAddress dns : mDhcpResults.dnsServers) {
-                // Only add likely reachable DNS servers.
-                // TODO: investigate deleting this.
-                if (newLp.isReachable(dns)) {
-                    newLp.addDnsServer(dns);
-                }
-            }
+            addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
             newLp.setDomains(mDhcpResults.domains);
 
             if (mDhcpResults.mtu != 0) {
@@ -909,6 +897,18 @@
         return newLp;
     }
 
+    private static void addAllReachableDnsServers(
+            LinkProperties lp, Iterable<InetAddress> dnses) {
+        // TODO: Investigate deleting this reachability check.  We should be
+        // able to pass everything down to netd and let netd do evaluation
+        // and RFC6724-style sorting.
+        for (InetAddress dns : dnses) {
+            if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
+                lp.addDnsServer(dns);
+            }
+        }
+    }
+
     // Returns false if we have lost provisioning, true otherwise.
     private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
         final LinkProperties newLp = assembleLinkProperties();
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 6802cff..ba1621d 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -22,6 +22,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
+import android.net.TrafficStats;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructGroupReq;
@@ -563,6 +564,7 @@
     private boolean createSocket() {
         final int SEND_TIMEOUT_MS = 300;
 
+        final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NEIGHBOR);
         try {
             mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
             // Setting SNDTIMEO is purely for defensive purposes.
@@ -574,6 +576,8 @@
         } catch (ErrnoException | IOException e) {
             Log.e(TAG, "Failed to create RA daemon socket: " + e);
             return false;
+        } finally {
+            TrafficStats.setThreadStatsTag(oldTag);
         }
 
         return true;
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index 785c3fa..f89fd47 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -58,6 +58,7 @@
 import android.provider.CallLog;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.KeyValueListParser;
 import android.util.Slog;
 import com.android.internal.os.BackgroundThread;
@@ -105,7 +106,8 @@
     private static final String DEMO_SESSION_COUNT = "retail_demo_session_count";
     private static final String DEMO_SESSION_DURATION = "retail_demo_session_duration";
 
-    boolean mDeviceInDemoMode = false;
+    boolean mDeviceInDemoMode;
+    boolean mIsCarrierDemoMode;
     int mCurrentUserId = UserHandle.USER_SYSTEM;
     long mUserInactivityTimeout;
     long mWarningDialogTimeout;
@@ -135,7 +137,8 @@
             if (!mDeviceInDemoMode) {
                 return;
             }
-            switch (intent.getAction()) {
+            final String action = intent.getAction();
+            switch (action) {
                 case Intent.ACTION_SCREEN_OFF:
                     mHandler.removeMessages(MSG_TURN_SCREEN_ON);
                     mHandler.sendEmptyMessageDelayed(MSG_TURN_SCREEN_ON, SCREEN_WAKEUP_DELAY);
@@ -166,7 +169,7 @@
                     mInjector.acquireWakeLock();
                     break;
                 case MSG_INACTIVITY_TIME_OUT:
-                    if (isDemoLauncherDisabled()) {
+                    if (!mIsCarrierDemoMode && isDemoLauncherDisabled()) {
                         Slog.i(TAG, "User inactivity timeout reached");
                         showInactivityCountdownDialog();
                     }
@@ -177,12 +180,30 @@
                     }
                     removeMessages(MSG_START_NEW_SESSION);
                     removeMessages(MSG_INACTIVITY_TIME_OUT);
-                    if (mCurrentUserId != UserHandle.USER_SYSTEM) {
+                    if (!mIsCarrierDemoMode && mCurrentUserId != UserHandle.USER_SYSTEM) {
                         logSessionDuration();
                     }
-                    final UserInfo demoUser = mInjector.getUserManager().createUser(DEMO_USER_NAME,
-                            UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL);
-                    if (demoUser != null) {
+
+                    final UserManager um = mInjector.getUserManager();
+                    UserInfo demoUser = null;
+                    if (mIsCarrierDemoMode) {
+                        // Re-use the existing demo user in carrier demo mode.
+                        for (UserInfo user : um.getUsers()) {
+                            if (user.isDemo()) {
+                                demoUser = user;
+                                break;
+                            }
+                        }
+                    }
+
+                    if (demoUser == null) {
+                        // User in carrier demo mode should survive reboots.
+                        final int flags = UserInfo.FLAG_DEMO
+                                | (mIsCarrierDemoMode ? 0 : UserInfo.FLAG_EPHEMERAL);
+                        demoUser = um.createUser(DEMO_USER_NAME, flags);
+                    }
+
+                    if (demoUser != null && mCurrentUserId != demoUser.id) {
                         setupDemoUser(demoUser);
                         mInjector.switchUser(demoUser.id);
                     }
@@ -211,7 +232,7 @@
         }
 
         public void register() {
-            ContentResolver cr = mInjector.getContentResolver();
+            final ContentResolver cr = mInjector.getContentResolver();
             cr.registerContentObserver(mDeviceDemoModeUri, false, this, UserHandle.USER_SYSTEM);
             cr.registerContentObserver(mDeviceProvisionedUri, false, this, UserHandle.USER_SYSTEM);
             cr.registerContentObserver(mRetailDemoConstantsUri, false, this,
@@ -224,30 +245,28 @@
                 refreshTimeoutConstants();
                 return;
             }
-            if (mDeviceDemoModeUri.equals(uri)) {
-                mDeviceInDemoMode = UserManager.isDeviceInDemoMode(getContext());
-                if (mDeviceInDemoMode) {
+
+            // If device is provisioned and left demo mode - run the cleanup in demo folder
+            if (isDeviceProvisioned()) {
+                if (UserManager.isDeviceInDemoMode(getContext())) {
                     startDemoMode();
                 } else {
                     mInjector.systemPropertiesSet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "0");
+
+                    // Run on the bg thread to not block the fg thread
+                    BackgroundThread.getHandler().post(() -> {
+                        if (!deletePreloadsFolderContents()) {
+                            Slog.w(TAG, "Failed to delete preloads folder contents");
+                        }
+                    });
+
+                    stopDemoMode();
+
                     if (mInjector.isWakeLockHeld()) {
                         mInjector.releaseWakeLock();
                     }
                 }
             }
-            // If device is provisioned and left demo mode - run the cleanup in demo folder
-            if (!mDeviceInDemoMode && isDeviceProvisioned()) {
-                // Run on the bg thread to not block the fg thread
-                BackgroundThread.getHandler().post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!deletePreloadsFolderContents()) {
-                            Slog.w(TAG, "Failed to delete preloads folder contents");
-                        }
-                    }
-                });
-                stopDemoMode();
-            }
         }
 
         private void refreshTimeoutConstants() {
@@ -300,23 +319,22 @@
     }
 
     boolean isDemoLauncherDisabled() {
-        IPackageManager pm = mInjector.getIPackageManager();
         int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-        String demoLauncherComponent = getContext().getResources()
-                .getString(R.string.config_demoModeLauncherComponent);
         try {
-            enabledState = pm.getComponentEnabledSetting(
-                    ComponentName.unflattenFromString(demoLauncherComponent),
-                    mCurrentUserId);
-        } catch (RemoteException exc) {
-            Slog.e(TAG, "Unable to talk to Package Manager", exc);
+            final IPackageManager iPm = mInjector.getIPackageManager();
+            final String demoLauncherComponent =
+                    getContext().getString(R.string.config_demoModeLauncherComponent);
+            enabledState = iPm.getComponentEnabledSetting(
+                    ComponentName.unflattenFromString(demoLauncherComponent), mCurrentUserId);
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Error retrieving demo launcher enabled setting", re);
         }
         return enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
     }
 
     private void setupDemoUser(UserInfo userInfo) {
-        UserManager um = mInjector.getUserManager();
-        UserHandle user = UserHandle.of(userInfo.id);
+        final UserManager um = mInjector.getUserManager();
+        final UserHandle user = UserHandle.of(userInfo.id);
         um.setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, user);
         um.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true, user);
         um.setUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, true, user);
@@ -327,6 +345,7 @@
         um.setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, false, user);
         // Disallow rebooting in safe mode - controlled by user 0
         um.setUserRestriction(UserManager.DISALLOW_SAFE_BOOT, true, UserHandle.SYSTEM);
+
         Settings.Secure.putIntForUser(mInjector.getContentResolver(),
                 Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id);
         Settings.Global.putInt(mInjector.getContentResolver(),
@@ -334,6 +353,47 @@
 
         grantRuntimePermissionToCamera(user);
         clearPrimaryCallLog();
+
+        if (!mIsCarrierDemoMode) {
+            // Enable demo launcher.
+            final String demoLauncher = getContext().getString(
+                    R.string.config_demoModeLauncherComponent);
+            if (!TextUtils.isEmpty(demoLauncher)) {
+                final ComponentName componentToEnable =
+                        ComponentName.unflattenFromString(demoLauncher);
+                final String packageName = componentToEnable.getPackageName();
+                try {
+                    final IPackageManager iPm = AppGlobals.getPackageManager();
+                    iPm.setComponentEnabledSetting(componentToEnable,
+                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0, userInfo.id);
+                    iPm.setApplicationEnabledSetting(packageName,
+                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0, userInfo.id, null);
+                } catch (RemoteException re) {
+                    // Internal, shouldn't happen
+                }
+            }
+        } else {
+            // Set the carrier demo mode setting for the demo user.
+            final String carrierDemoModeSetting = getContext().getString(
+                    R.string.config_carrierDemoModeSetting);
+            Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                    carrierDemoModeSetting, 1, userInfo.id);
+
+            // Enable packages for carrier demo mode.
+            final String packageList = getContext().getString(
+                    R.string.config_carrierDemoModePackages);
+            final String[] packageNames = packageList == null ? new String[0]
+                    : TextUtils.split(packageList, ",");
+            final IPackageManager iPm = AppGlobals.getPackageManager();
+            for (String packageName : packageNames) {
+                try {
+                    iPm.setApplicationEnabledSetting(packageName,
+                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0, userInfo.id, null);
+                } catch (RemoteException re) {
+                    Slog.e(TAG, "Error enabling application: " + packageName, re);
+                }
+            }
+        }
     }
 
     private void grantRuntimePermissionToCamera(UserHandle user) {
@@ -380,18 +440,43 @@
 
     private boolean deletePreloadsFolderContents() {
         final File dir = mInjector.getDataPreloadsDirectory();
+        final File[] files = FileUtils.listFilesOrEmpty(dir);
+        final File fileCacheDirectory = mInjector.getDataPreloadsFileCacheDirectory();
         Slog.i(TAG, "Deleting contents of " + dir);
-        return FileUtils.deleteContents(dir);
+        boolean success = true;
+        for (File file : files) {
+            if (file.isFile()) {
+                if (!file.delete()) {
+                    success = false;
+                    Slog.w(TAG, "Cannot delete file " + file);
+                }
+            } else {
+                // Do not remove file_cache dir
+                if (!file.equals(fileCacheDirectory)) {
+                    if (!FileUtils.deleteContentsAndDir(file)) {
+                        success = false;
+                        Slog.w(TAG, "Cannot delete dir and its content " + file);
+                    }
+                } else {
+                    Slog.i(TAG, "Skipping directory with file cache " + file);
+                }
+            }
+        }
+        return success;
     }
 
     private void registerBroadcastReceiver() {
-        if (mBroadcastReceiver == null) {
-            final IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_SCREEN_OFF);
-            filter.addAction(ACTION_RESET_DEMO);
-            mBroadcastReceiver = new IntentReceiver();
-            getContext().registerReceiver(mBroadcastReceiver, filter);
+        if (mBroadcastReceiver != null) {
+            return;
         }
+
+        final IntentFilter filter = new IntentFilter();
+        if (!mIsCarrierDemoMode) {
+            filter.addAction(Intent.ACTION_SCREEN_OFF);
+        }
+        filter.addAction(ACTION_RESET_DEMO);
+        mBroadcastReceiver = new IntentReceiver();
+        getContext().registerReceiver(mBroadcastReceiver, filter);
     }
 
     private void unregisterBroadcastReceiver() {
@@ -427,6 +512,8 @@
     }
 
     private void startDemoMode() {
+        mDeviceInDemoMode = true;
+
         mPreloadAppsInstaller = mInjector.getPreloadAppsInstaller();
         mInjector.initializeWakeLock();
         if (mCameraIdsWithFlash == null) {
@@ -434,6 +521,12 @@
         }
         registerBroadcastReceiver();
 
+        final String carrierDemoModeSetting =
+                getContext().getString(R.string.config_carrierDemoModeSetting);
+        mIsCarrierDemoMode = !TextUtils.isEmpty(carrierDemoModeSetting)
+                && (Settings.Secure.getInt(getContext().getContentResolver(),
+                        carrierDemoModeSetting, 0) == 1);
+
         mInjector.systemPropertiesSet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "1");
         mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
 
@@ -471,13 +564,12 @@
     public void onBootPhase(int bootPhase) {
         switch (bootPhase) {
             case PHASE_THIRD_PARTY_APPS_CAN_START:
-                SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+                final SettingsObserver settingsObserver = new SettingsObserver(mHandler);
                 settingsObserver.register();
                 settingsObserver.refreshTimeoutConstants();
                 break;
             case PHASE_BOOT_COMPLETED:
                 if (UserManager.isDeviceInDemoMode(getContext())) {
-                    mDeviceInDemoMode = true;
                     startDemoMode();
                 }
                 break;
@@ -497,33 +589,39 @@
             Slog.wtf(TAG, "Should not allow switch to non-demo user in demo mode");
             return;
         }
-        if (!mInjector.isWakeLockHeld()) {
+        if (!mIsCarrierDemoMode && !mInjector.isWakeLockHeld()) {
             mInjector.acquireWakeLock();
         }
         mCurrentUserId = userId;
         mInjector.getActivityManagerInternal().updatePersistentConfigurationForUser(
                 mInjector.getSystemUsersConfiguration(), userId);
+
         mInjector.turnOffAllFlashLights(mCameraIdsWithFlash);
         muteVolumeStreams();
         if (!mInjector.getWifiManager().isWifiEnabled()) {
             mInjector.getWifiManager().setWifiEnabled(true);
         }
+
         // Disable lock screen for demo users.
         mInjector.getLockPatternUtils().setLockScreenDisabled(true, userId);
-        mInjector.getNotificationManager().notifyAsUser(TAG,
-                1, mInjector.createResetNotification(), UserHandle.of(userId));
 
-        synchronized (mActivityLock) {
-            mUserUntouched = true;
-        }
-        mInjector.logSessionCount(1);
-        mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT);
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mPreloadAppsInstaller.installApps(userId);
+        if (!mIsCarrierDemoMode) {
+            // Show reset notification (except in carrier demo mode).
+            mInjector.getNotificationManager().notifyAsUser(TAG,
+                    1, mInjector.createResetNotification(), UserHandle.of(userId));
+
+            synchronized (mActivityLock) {
+                mUserUntouched = true;
             }
-        });
+            mInjector.logSessionCount(1);
+            mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mPreloadAppsInstaller.installApps(userId);
+                }
+            });
+        }
     }
 
     private RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() {
@@ -531,7 +629,7 @@
 
         @Override
         public void onUserActivity() {
-            if (!mDeviceInDemoMode) {
+            if (!mDeviceInDemoMode || mIsCarrierDemoMode) {
                 return;
             }
             long timeOfActivity = SystemClock.uptimeMillis();
@@ -682,7 +780,7 @@
         }
 
         boolean isWakeLockHeld() {
-            return mWakeLock.isHeld();
+            return mWakeLock != null && mWakeLock.isHeld();
         }
 
         void acquireWakeLock() {
@@ -738,6 +836,10 @@
             return Environment.getDataPreloadsDirectory();
         }
 
+        File getDataPreloadsFileCacheDirectory() {
+            return Environment.getDataPreloadsFileCacheDirectory();
+        }
+
         void publishLocalService(RetailDemoModeService service,
                 RetailDemoModeServiceInternal localService) {
             service.publishLocalService(RetailDemoModeServiceInternal.class, localService);
diff --git a/services/tests/notification/AndroidManifest.xml b/services/tests/notification/AndroidManifest.xml
index 1ed8ed0..92f155f 100644
--- a/services/tests/notification/AndroidManifest.xml
+++ b/services/tests/notification/AndroidManifest.xml
@@ -22,6 +22,7 @@
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
new file mode 100644
index 0000000..b26bac3
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BadgeExtractorTest {
+
+    @Mock RankingConfig mConfig;
+
+    private String mPkg = "com.android.server.notification";
+    private int mId = 1001;
+    private String mTag = null;
+    private int mUid = 1000;
+    private int mPid = 2000;
+    private UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    private NotificationRecord getNotificationRecord(NotificationChannel channel) {
+        final Builder builder = new Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setPriority(Notification.PRIORITY_HIGH)
+                .setDefaults(Notification.DEFAULT_SOUND);
+
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
+                mPid, n, mUser, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+        return r;
+    }
+
+    private Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    //
+    // Tests
+    //
+
+    @Test
+    public void testAppYesChannelNo() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
+        NotificationChannel channel =
+                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
+        channel.setShowBadge(false);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertFalse(r.canShowBadge());
+    }
+
+    @Test
+    public void testAppNoChannelYes() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
+        NotificationChannel channel =
+                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);
+        channel.setShowBadge(true);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertFalse(r.canShowBadge());
+    }
+
+    @Test
+    public void testAppYesChannelYes() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
+        NotificationChannel channel =
+                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
+        channel.setShowBadge(true);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertTrue(r.canShowBadge());
+    }
+
+    @Test
+    public void testAppNoChannelNo() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
+        NotificationChannel channel =
+                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
+        channel.setShowBadge(false);
+
+        NotificationRecord r = getNotificationRecord(channel);
+
+        extractor.process(r);
+
+        assertFalse(r.canShowBadge());
+    }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ad436724a..468a26b 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -230,9 +230,9 @@
             n.flags |= Notification.FLAG_INSISTENT;
         }
 
-        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, channel, id, mTag, mUid,
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
                 mPid, n, mUser, null, System.currentTimeMillis());
-        NotificationRecord r = new NotificationRecord(getContext(), sbn);
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
         mService.addNotification(r);
         return r;
     }
diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
index f48d785..936531b 100644
--- a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
@@ -70,9 +70,7 @@
         if (groupKey != null) {
             nb.setGroup(groupKey);
         }
-        NotificationChannel channel =
-                new NotificationChannel("test", "test", NotificationManager.IMPORTANCE_LOW);
-        return new StatusBarNotification(pkg, pkg, channel, id, tag, 0, 0, nb.build(), user, null,
+        return new StatusBarNotification(pkg, pkg, id, tag, 0, 0, nb.build(), user, null,
                 System.currentTimeMillis());
     }
 
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
index eee9cf1..f8a32bb 100644
--- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -69,9 +69,9 @@
                 .setDefaults(Notification.DEFAULT_SOUND);
 
         Notification n = builder.build();
-        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, channel, mId, mTag, mUid,
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
                 mPid, n, mUser, null, System.currentTimeMillis());
-        NotificationRecord r = new NotificationRecord(getContext(), sbn);
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
         return r;
     }
 
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
index 403b65c..aa08b41 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
@@ -105,8 +105,8 @@
                 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build();
         mRecordMinCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
-                callPkg, getDefaultChannel(), 1, "minCall", callUid, callUid, n1,
-                new UserHandle(userId), "", 2000));
+                callPkg, 1, "minCall", callUid, callUid, n1,
+                new UserHandle(userId), "", 2000), getDefaultChannel());
         mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN);
 
         Notification n2 = new Notification.Builder(mContext)
@@ -114,8 +114,8 @@
                 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build();
         mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
-                callPkg, getDefaultChannel(), 1, "highcall", callUid, callUid, n2,
-                new UserHandle(userId), "", 1999));
+                callPkg, 1, "highcall", callUid, callUid, n2,
+                new UserHandle(userId), "", 1999), getDefaultChannel());
         mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
 
         Notification n3 = new Notification.Builder(mContext)
@@ -124,43 +124,43 @@
                 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build();
         mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "media", uid2, uid2, n3, new UserHandle(userId),
-                "", 1499));
+                pkg2, 1, "media", uid2, uid2, n3, new UserHandle(userId),
+                "", 1499), getDefaultChannel());
         mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
 
         Notification n4 = new Notification.Builder(mContext)
                 .setStyle(new Notification.MessagingStyle("sender!")).build();
         mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId),
-                "", 1599));
+                pkg2, 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId),
+                "", 1599), getDefaultChannel());
         mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
         mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX);
 
         Notification n5 = new Notification.Builder(mContext)
                 .setCategory(Notification.CATEGORY_MESSAGE).build();
         mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg,
-                smsPkg, getDefaultChannel(), 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
-                "", 1299));
+                smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
+                "", 1299), getDefaultChannel());
         mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
 
         Notification n6 = new Notification.Builder(mContext).build();
         mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "starred", uid2, uid2, n6, new UserHandle(userId),
-                "", 1259));
+                pkg2, 1, "starred", uid2, uid2, n6, new UserHandle(userId),
+                "", 1259), getDefaultChannel());
         mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT);
         mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
 
         Notification n7 = new Notification.Builder(mContext).build();
         mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "contact", uid2, uid2, n7, new UserHandle(userId),
-                "", 1259));
+                pkg2, 1, "contact", uid2, uid2, n7, new UserHandle(userId),
+                "", 1259), getDefaultChannel());
         mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT);
         mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
 
         Notification n8 = new Notification.Builder(mContext).build();
         mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
-                "", 1258));
+                pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
+                "", 1258), getDefaultChannel());
         mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
 
         Notification n9 = new Notification.Builder(mContext)
@@ -169,15 +169,15 @@
                         |Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build();
         mRecordCheater = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "cheater", uid2, uid2, n9, new UserHandle(userId),
-                "", 9258));
+                pkg2, 1, "cheater", uid2, uid2, n9, new UserHandle(userId),
+                "", 9258), getDefaultChannel());
         mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW);
 
         Notification n10 = new Notification.Builder(mContext)
                 .setStyle(new Notification.InboxStyle().setSummaryText("message!")).build();
         mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
-                pkg2, getDefaultChannel(), 1, "email", uid2, uid2, n10, new UserHandle(userId),
-                "", 1599));
+                pkg2, 1, "email", uid2, uid2, n10, new UserHandle(userId),
+                "", 1599), getDefaultChannel());
         mRecordEmail.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
     }
 
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
index b6166f6..f0f4c4d 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -59,6 +59,7 @@
             assertEquals(getChannel(key, i), ranking.getChannel());
             assertEquals(getPeople(key, i), ranking.getAdditionalPeople());
             assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria());
+            assertEquals(getShowBadge(i), ranking.canShowBadge());
         }
     }
 
@@ -68,9 +69,10 @@
         Bundle overrideGroupKeys = new Bundle();
         Bundle suppressedVisualEffects = new Bundle();
         Bundle explanation = new Bundle();
-        Bundle overrideChannels = new Bundle();
+        Bundle channels = new Bundle();
         Bundle overridePeople = new Bundle();
         Bundle snoozeCriteria = new Bundle();
+        Bundle showBadge = new Bundle();
         int[] importance = new int[mKeys.length];
 
         for (int i = 0; i < mKeys.length; i++) {
@@ -83,14 +85,15 @@
             suppressedVisualEffects.putInt(key, getSuppressedVisualEffects(i));
             importance[i] = getImportance(i);
             explanation.putString(key, getExplanation(key));
-            overrideChannels.putParcelable(key, getChannel(key, i));
+            channels.putParcelable(key, getChannel(key, i));
             overridePeople.putStringArrayList(key, getPeople(key, i));
             snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
+            showBadge.putBoolean(key, getShowBadge(i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
-                overrideChannels, overridePeople, snoozeCriteria);
+                channels, overridePeople, snoozeCriteria, showBadge);
         return update;
     }
 
@@ -122,6 +125,10 @@
         return new NotificationChannel(key, key, getImportance(index));
     }
 
+    private boolean getShowBadge(int index) {
+        return index % 3 == 0;
+    }
+
     private ArrayList<String> getPeople(String key, int index) {
         ArrayList<String> people = new ArrayList<>();
         for (int i = 0; i < index; i++) {
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 40938fd..0ec5f16 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -24,7 +24,9 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -33,18 +35,25 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.MessageQueue;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -54,35 +63,79 @@
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+// THESE TESTS ARE DISABLED FOR NOW BECAUSE THEY DO NOT FINISH 1/2 THE TIME.
 public class NotificationManagerServiceTest {
     private final String pkg = "com.android.server.notification";
-    private final int uid = 0;
+    private final int uid = Binder.getCallingUid();
     private NotificationManagerService mNotificationManagerService;
     private INotificationManager mBinderService;
     private IPackageManager mPackageManager = mock(IPackageManager.class);
+    private Context mContext;
+    private HandlerThread mThread;
 
     @Before
     @UiThreadTest
     public void setUp() throws Exception {
-        final Context context = InstrumentationRegistry.getTargetContext();
-        mNotificationManagerService = new NotificationManagerService(context);
+        mContext = InstrumentationRegistry.getTargetContext();
+        mNotificationManagerService = new NotificationManagerService(mContext);
 
         // MockPackageManager - default returns ApplicationInfo with matching calling UID
         final ApplicationInfo applicationInfo = new ApplicationInfo();
-        applicationInfo.uid = Binder.getCallingUid();
+        applicationInfo.uid = uid;
         when(mPackageManager.getApplicationInfo(any(), anyInt(), anyInt()))
                 .thenReturn(applicationInfo);
+        final PackageManager mockPackageManagerClient = mock(PackageManager.class);
+        when(mockPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(applicationInfo);
         final LightsManager mockLightsManager = mock(LightsManager.class);
         when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
-        mNotificationManagerService.init(mPackageManager, mockLightsManager);
+        // Use a separate thread for service looper.
+        mThread = new HandlerThread("TestThread");
+        mThread.start();
+        // Mock NotificationListeners to bypass security checks.
+        final NotificationManagerService.NotificationListeners mockNotificationListeners =
+                mock(NotificationManagerService.NotificationListeners.class);
+        when(mockNotificationListeners.checkServiceTokenLocked(any())).thenReturn(
+                mockNotificationListeners.new ManagedServiceInfo(null,
+                        new ComponentName(pkg, "test_class"), uid, true, null, 0));
+
+        mNotificationManagerService.init(mThread.getLooper(), mPackageManager,
+                mockPackageManagerClient, mockLightsManager, mockNotificationListeners);
 
         // Tests call directly into the Binder.
         mBinderService = mNotificationManagerService.getBinderService();
     }
 
-    @Test
+    public void waitForIdle() throws Exception {
+        MessageQueue queue = mThread.getLooper().getQueue();
+        CountDownLatch latch = new CountDownLatch(1);
+        queue.addIdleHandler(new MessageQueue.IdleHandler() {
+                @Override public boolean queueIdle() {
+                    latch.countDown();
+                    return false;
+                }
+        });
+        latch.await();
+        if (!queue.isIdle()) {
+            waitForIdle();
+        }
+    }
+
+    private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
+        if (channel == null) {
+            channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
+        }
+        Notification n = new Notification.Builder(mContext)
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setPriority(Notification.PRIORITY_HIGH)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(mContext.getPackageName(),
+                mContext.getPackageName(), 1, "tag", uid, 0,
+                n, new UserHandle(uid), null, 0);
+        return new NotificationRecord(mContext, sbn, channel);
+    }
+
     @UiThreadTest
     public void testCreateNotificationChannels_SingleChannel() throws Exception {
         final NotificationChannel channel =
@@ -94,7 +147,6 @@
         assertTrue(createdChannel != null);
     }
 
-    @Test
     @UiThreadTest
     public void testCreateNotificationChannels_NullChannelThrowsException() throws Exception {
         try {
@@ -106,7 +158,6 @@
         }
     }
 
-    @Test
     @UiThreadTest
     public void testCreateNotificationChannels_TwoChannels() throws Exception {
         final NotificationChannel channel1 =
@@ -119,7 +170,6 @@
         assertTrue(mBinderService.getNotificationChannel("test_pkg", "id2") != null);
     }
 
-    @Test
     @UiThreadTest
     public void testCreateNotificationChannels_SecondCreateDoesNotChangeImportance()
             throws Exception {
@@ -138,7 +188,6 @@
         assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
     }
 
-    @Test
     @UiThreadTest
     public void testCreateNotificationChannels_IdenticalChannelsInListIgnoresSecond()
             throws Exception {
@@ -153,7 +202,6 @@
         assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
     }
 
-    @Test
     @UiThreadTest
     public void testBlockedNotifications_suspended() throws Exception {
         NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
@@ -169,7 +217,6 @@
         verify(usageStats, times(1)).registerSuspendedByAdmin(eq(r));
     }
 
-    @Test
     @UiThreadTest
     public void testBlockedNotifications_blockedChannel() throws Exception {
         NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
@@ -186,7 +233,6 @@
         verify(usageStats, times(1)).registerBlocked(eq(r));
     }
 
-    @Test
     @UiThreadTest
     public void testBlockedNotifications_blockedApp() throws Exception {
         NotificationUsageStats usageStats = mock(NotificationUsageStats.class);
@@ -203,15 +249,99 @@
         verify(usageStats, times(1)).registerBlocked(eq(r));
     }
 
-    private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
-        final Context context = InstrumentationRegistry.getTargetContext();
-        Notification n = new Notification.Builder(context)
-                .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setPriority(Notification.PRIORITY_HIGH)
-                .build();
-        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, channel, 1, "tag", uid, uid,
-                n, UserHandle.SYSTEM, null, uid);
-        return new NotificationRecord(context, sbn);
+    @UiThreadTest
+    public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
+        mBinderService.enqueueNotificationWithTag(mContext.getPackageName(), "opPkg", "tag", 0,
+                generateNotificationRecord(null).getNotification(), new int[1], 0);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(mContext.getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @UiThreadTest
+    public void testCancelNotificationImmediatelyAfterEnqueue() throws Exception {
+        mBinderService.enqueueNotificationWithTag(mContext.getPackageName(), "opPkg", "tag", 0,
+                generateNotificationRecord(null).getNotification(), new int[1], 0);
+        mBinderService.cancelNotificationWithTag(mContext.getPackageName(), "tag", 0, 0);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(mContext.getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @UiThreadTest
+    public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), "opPkg", "tag",
+                sbn.getId(), sbn.getNotification(), new int[1], sbn.getUserId());
+        mBinderService.cancelNotificationsFromListener(null, null);
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(sbn.getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @UiThreadTest
+    public void testCancelAllNotificationsImmediatelyAfterEnqueue() throws Exception {
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), "opPkg", "tag",
+                sbn.getId(), sbn.getNotification(), new int[1], sbn.getUserId());
+        mBinderService.cancelAllNotifications(sbn.getPackageName(), sbn.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(sbn.getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @UiThreadTest
+    public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), "opPkg", "tag",
+                sbn.getId(), sbn.getNotification(), new int[1], sbn.getUserId());
+        mBinderService.cancelAllNotifications(sbn.getPackageName(), sbn.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(sbn.getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @UiThreadTest
+    public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), "opPkg", "tag",
+                sbn.getId(), sbn.getNotification(), new int[1], sbn.getUserId());
+        mBinderService.cancelAllNotifications("other_pkg_name", sbn.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(sbn.getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @UiThreadTest
+    public void testCancelAllNotifications_NullPkgRemovesAll() throws Exception {
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), "opPkg", "tag",
+                sbn.getId(), sbn.getNotification(), new int[1], sbn.getUserId());
+        mBinderService.cancelAllNotifications(null, sbn.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(sbn.getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @UiThreadTest
+    public void testCancelAllNotifications_NullPkgIgnoresUserAllNotifications() throws Exception {
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), "opPkg", "tag",
+                sbn.getId(), sbn.getNotification(), new int[1], UserHandle.USER_ALL);
+        // Null pkg is how we signal a user switch.
+        mBinderService.cancelAllNotifications(null, sbn.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(sbn.getPackageName());
+        assertEquals(1, notifs.length);
     }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index fc94271f..15dcc26 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -136,10 +136,10 @@
 
         Notification n = builder.build();
         if (preO) {
-            return new StatusBarNotification(pkg, pkg, defaultChannel, id1, tag1, uid, uid, n,
+            return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n,
                     mUser, null, uid);
         } else {
-            return new StatusBarNotification(pkg2, pkg2, channel, id2, tag2, uid2, uid2, n,
+            return new StatusBarNotification(pkg2, pkg2, id2, tag2, uid2, uid2, n,
                     mUser, null, uid2);
         }
     }
@@ -155,7 +155,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound());
     }
 
@@ -166,7 +166,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_SOUND, record.getSound());
     }
 
@@ -178,7 +178,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_SOUND, record.getSound());
     }
 
@@ -189,7 +189,7 @@
         StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(CUSTOM_SOUND, record.getSound());
     }
 
@@ -200,7 +200,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, true /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertNotNull(record.getVibration());
     }
 
@@ -211,7 +211,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_VIBRATION, record.getVibration());
     }
 
@@ -223,7 +223,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertTrue(!Objects.equals(CUSTOM_VIBRATION, record.getVibration()));
     }
 
@@ -234,7 +234,7 @@
         StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
                 false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(CUSTOM_CHANNEL_VIBRATION, record.getVibration());
     }
 
@@ -245,7 +245,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
     }
 
@@ -256,7 +256,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
     }
 
@@ -264,7 +264,7 @@
     public void testImportance_preUpgrade() throws Exception {
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance());
     }
 
@@ -275,7 +275,7 @@
         StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
 
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(NotificationManager.IMPORTANCE_LOW, record.getImportance());
     }
 
@@ -283,7 +283,7 @@
     public void testImportance_upgrade() throws Exception {
         StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */);
-        NotificationRecord record = new NotificationRecord(mMockContext, sbn);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(NotificationManager.IMPORTANCE_DEFAULT, record.getImportance());
     }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index f8061f6..5be6e91 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.notification;
 
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.fail;
 
 import org.junit.Before;
@@ -29,6 +30,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.app.Notification;
+import android.app.NotificationChannelGroup;
 import android.content.Context;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -49,6 +51,9 @@
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -100,8 +105,8 @@
                 .setWhen(1205)
                 .build();
         mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortA, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         mNotiGroupGSortB = new Notification.Builder(getContext())
                 .setContentTitle("B")
@@ -110,24 +115,24 @@
                 .setWhen(1200)
                 .build();
         mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortB, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         mNotiNoGroup = new Notification.Builder(getContext())
                 .setContentTitle("C")
                 .setWhen(1201)
                 .build();
         mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiNoGroup, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         mNotiNoGroup2 = new Notification.Builder(getContext())
                 .setContentTitle("D")
                 .setWhen(1202)
                 .build();
         mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup2, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiNoGroup2, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         mNotiNoGroupSortA = new Notification.Builder(getContext())
                 .setContentTitle("E")
@@ -135,8 +140,8 @@
                 .setSortKey("A")
                 .build();
         mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroupSortA, user,
-                null, System.currentTimeMillis()));
+                "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
 
         final ApplicationInfo legacy = new ApplicationInfo();
         legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -166,7 +171,7 @@
         serializer.flush();
 
         for (String channelId : channelIds) {
-            mHelper.deleteNotificationChannel(pkg, uid, channelId);
+            mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId);
         }
         return baos;
     }
@@ -181,6 +186,7 @@
         assertEquals(expected.getSound(), actual.getSound());
         assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
         assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
+        assertEquals(expected.getGroup(), actual.getGroup());
     }
 
     @Test
@@ -235,6 +241,7 @@
 
     @Test
     public void testChannelXml() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "2");
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         NotificationChannel channel2 =
@@ -244,17 +251,19 @@
         channel2.setBypassDnd(true);
         channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel2.enableVibration(true);
+        channel2.setGroup(ncg.getId());
         channel2.setVibrationPattern(new long[] {100, 67, 145, 156});
 
-        mHelper.createNotificationChannel(pkg, uid, channel1, false);
+        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
+        mHelper.createNotificationChannel(pkg, uid, channel1, true);
         mHelper.createNotificationChannel(pkg, uid, channel2, false);
 
+        mHelper.setShowBadge(pkg, uid, true);
+        mHelper.setShowBadge(pkg2, uid2, false);
+
         ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(),
                 NotificationChannel.DEFAULT_CHANNEL_ID);
-
-        mHelper.deleteNotificationChannel(pkg, uid, channel1.getId());
-        mHelper.deleteNotificationChannel(pkg, uid, channel2.getId());
-        mHelper.deleteNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{pkg}, new int[]{uid});
 
         XmlPullParser parser = Xml.newPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
@@ -262,10 +271,17 @@
         parser.nextTag();
         mHelper.readXml(parser, false);
 
-        assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId()));
-        compareChannels(channel2, mHelper.getNotificationChannel(pkg, uid, channel2.getId()));
-        assertNotNull(
-                mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID));
+        assertFalse(mHelper.canShowBadge(pkg2, uid2));
+        assertTrue(mHelper.canShowBadge(pkg, uid));
+        assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId(), false));
+        compareChannels(channel2,
+                mHelper.getNotificationChannel(pkg, uid, channel2.getId(), false));
+        assertNotNull(mHelper.getNotificationChannel(
+                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false));
+        assertEquals(ncg.getId(),
+                mHelper.getNotificationChannelGroups(pkg, uid, false).getList().get(0).getId());
+        assertEquals(channel2.getGroup(), mHelper.getNotificationChannelGroups(
+                pkg, uid, false).getList().get(0).getChannels().get(0).getGroup());
     }
 
     @Test
@@ -284,8 +300,8 @@
         parser.nextTag();
         mHelper.readXml(parser, false);
 
-        final NotificationChannel updated =
-                mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+        final NotificationChannel updated = mHelper.getNotificationChannel(
+                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false);
         assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
         assertFalse(updated.canBypassDnd());
         assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,updated.getLockscreenVisibility());
@@ -298,8 +314,8 @@
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MIN);
         mHelper.createNotificationChannel(pkg, uid, channel1, true);
 
-        final NotificationChannel defaultChannel =
-                mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(
+                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false);
         defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
         mHelper.updateNotificationChannel(pkg, uid, defaultChannel);
 
@@ -313,7 +329,7 @@
         mHelper.readXml(parser, false);
 
         assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
-                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID).getImportance());
+                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
     }
 
     @Test
@@ -331,8 +347,8 @@
         parser.nextTag();
         mHelper.readXml(parser, false);
 
-        final NotificationChannel updated1 =
-            mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+        final NotificationChannel updated1 = mHelper.getNotificationChannel(
+                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false);
         assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
         assertTrue(updated1.canBypassDnd());
         assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
@@ -340,8 +356,8 @@
             | NotificationChannel.USER_LOCKED_PRIORITY
             | NotificationChannel.USER_LOCKED_VISIBILITY, updated1.getUserLockedFields());
 
-        final NotificationChannel updated2 =
-            mHelper.getNotificationChannel(pkg2, uid2, NotificationChannel.DEFAULT_CHANNEL_ID);
+        final NotificationChannel updated2 = mHelper.getNotificationChannel(
+                pkg2, uid2, NotificationChannel.DEFAULT_CHANNEL_ID, false);
         // clamped
         assertEquals(NotificationManager.IMPORTANCE_LOW, updated2.getImportance());
         assertFalse(updated2.canBypassDnd());
@@ -378,7 +394,7 @@
         mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
 
         // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
     }
 
     @Test
@@ -399,7 +415,7 @@
         mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
 
         // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
     }
 
     @Test
@@ -421,7 +437,7 @@
         mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
 
         // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
     }
 
     @Test
@@ -442,7 +458,7 @@
         mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
 
         // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
     }
 
     @Test
@@ -463,7 +479,7 @@
         mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
 
         // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
     }
 
     @Test
@@ -484,7 +500,7 @@
         mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
 
         // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
     }
 
     @Test
@@ -503,7 +519,7 @@
         mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
 
         // no fields should be changed
-        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
     }
 
     @Test
@@ -529,13 +545,13 @@
         mHelper.updateNotificationChannel(pkg, uid, channel2);
 
         // all fields should be changed
-        assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+        assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
     }
 
     @Test
     public void testGetChannelWithFallback() throws Exception {
         NotificationChannel channel =
-                mHelper.getNotificationChannelWithFallback(pkg, uid, "garbage");
+                mHelper.getNotificationChannelWithFallback(pkg, uid, "garbage", false);
         assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID, channel.getId());
     }
 
@@ -557,7 +573,7 @@
         mHelper.createNotificationChannel(pkg, uid, channel, true);
 
         NotificationChannel savedChannel =
-                mHelper.getNotificationChannel(pkg, uid, channel.getId());
+                mHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
 
         assertEquals(channel.getName(), savedChannel.getName());
         assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
@@ -584,7 +600,7 @@
         mHelper.createNotificationChannel(pkg, uid, channel, true);
 
         NotificationChannel savedChannel =
-                mHelper.getNotificationChannel(pkg, uid, channel.getId());
+                mHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
 
         assertEquals(channel.getName(), savedChannel.getName());
         assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
@@ -592,4 +608,276 @@
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
     }
+
+    @Test
+    public void testGetDeletedChannel() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        channel.setSound(new Uri.Builder().scheme("test").build());
+        channel.setLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        channel.enableVibration(true);
+        channel.setVibrationPattern(new long[] {100, 67, 145, 156});
+
+        mHelper.createNotificationChannel(pkg, uid, channel, true);
+        mHelper.deleteNotificationChannel(pkg, uid, channel.getId());
+
+        // Does not return deleted channel
+        NotificationChannel response =
+                mHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
+        assertNull(response);
+
+        // Returns deleted channel
+        response = mHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
+        compareChannels(channel, response);
+        assertTrue(response.isDeleted());
+    }
+
+    @Test
+    public void testGetDeletedChannels() throws Exception {
+        Map<String, NotificationChannel> channelMap = new HashMap<>();
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        channel.setSound(new Uri.Builder().scheme("test").build());
+        channel.setLights(true);
+        channel.setBypassDnd(true);
+        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+        channel.enableVibration(true);
+        channel.setVibrationPattern(new long[] {100, 67, 145, 156});
+        channelMap.put(channel.getId(), channel);
+        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.deleteNotificationChannel(pkg, uid, channel.getId());
+
+        // Returns only non-deleted channels
+        List<NotificationChannel> channels =
+                mHelper.getNotificationChannels(pkg, uid, false).getList();
+        assertEquals(2, channels.size());   // Default channel + non-deleted channel
+        for (NotificationChannel nc : channels) {
+            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+                compareChannels(channel2, nc);
+            }
+        }
+
+        // Returns deleted channels too
+        channels =  mHelper.getNotificationChannels(pkg, uid, true).getList();
+        assertEquals(3, channels.size());               // Includes default channel
+        for (NotificationChannel nc : channels) {
+            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+                compareChannels(channelMap.get(nc.getId()), nc);
+            }
+        }
+    }
+
+    @Test
+    public void testUpdateDeletedChannels() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(pkg, uid, channel, true);
+
+        mHelper.deleteNotificationChannel(pkg, uid, channel.getId());
+
+        channel.setSound(new Uri.Builder().scheme("test").build());
+        try {
+            mHelper.updateNotificationChannel(pkg, uid, channel);
+            fail("Updated deleted channel");
+        } catch (IllegalArgumentException e) {
+            // :)
+        }
+
+        try {
+            mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
+            fail("Updated deleted channel");
+        } catch (IllegalArgumentException e) {
+            // :)
+        }
+    }
+
+    @Test
+    public void testCreateDeletedChannel() throws Exception {
+        long[] vibration = new long[] {100, 67, 145, 156};
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        channel.setVibrationPattern(vibration);
+
+        mHelper.createNotificationChannel(pkg, uid, channel, true);
+        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);
+
+        // No long deleted, using old settings
+        compareChannels(channel,
+                mHelper.getNotificationChannel(pkg, uid, newChannel.getId(), false));
+    }
+
+    @Test
+    public void testCreateChannel_alreadyExists() throws Exception {
+        long[] vibration = new long[] {100, 67, 145, 156};
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+        channel.setVibrationPattern(vibration);
+
+        mHelper.createNotificationChannel(pkg, uid, channel, true);
+
+        NotificationChannel newChannel = new NotificationChannel(
+                channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
+        newChannel.setVibrationPattern(new long[] {100});
+
+        mHelper.createNotificationChannel(pkg, uid, newChannel, true);
+
+        // Old settings not overridden
+        compareChannels(channel,
+                mHelper.getNotificationChannel(pkg, uid, newChannel.getId(), false));
+    }
+
+    @Test
+    public void testPermanentlyDeleteChannels() throws Exception {
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+
+        mHelper.createNotificationChannel(pkg, uid, channel1, true);
+        mHelper.createNotificationChannel(pkg, uid, channel2, false);
+
+        mHelper.permanentlyDeleteNotificationChannels(pkg, uid);
+
+        // Only default channel remains
+        assertEquals(1, mHelper.getNotificationChannels(pkg, uid, true).getList().size());
+    }
+
+    @Test
+    public void testOnPackageChanged_packageRemoval() throws Exception {
+        // Deleted
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(pkg, uid, channel1, true);
+
+        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.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
+        assertEquals(2, mHelper.getNotificationChannels(pkg, uid, false).getList().size());
+    }
+
+    @Test
+    public void testOnPackageChanged_packageRemoval_importance() throws Exception {
+        mHelper.setImportance(pkg, uid, NotificationManager.IMPORTANCE_HIGH);
+
+        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
+
+        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(pkg, uid));
+    }
+
+    @Test
+    public void testOnPackageChanged_packageRemoval_groups() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
+        mHelper.createNotificationChannelGroup(pkg, uid, ncg2, true);
+
+        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
+
+        assertEquals(0, mHelper.getNotificationChannelGroups(pkg, uid, true).getList().size());
+    }
+
+    @Test
+    public void testRecordDefaults() throws Exception {
+        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(pkg, uid));
+        assertEquals(true, mHelper.canShowBadge(pkg, uid));
+        assertEquals(1, mHelper.getNotificationChannels(pkg, uid, false).getList().size());
+    }
+
+    @Test
+    public void testCreateGroup() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
+        assertEquals(ncg, mHelper.getNotificationChannelGroups(pkg, uid, false).getList().get(0));
+    }
+
+    @Test
+    public void testCannotCreateChannel_badGroup() throws Exception {
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1.setGroup("garbage");
+        try {
+            mHelper.createNotificationChannel(pkg, uid, channel1, true);
+            fail("Created a channel with a bad group");
+        } catch (IllegalArgumentException e) {}
+    }
+
+    @Test
+    public void testCannotCreateChannel_goodGroup() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1.setGroup(ncg.getId());
+        mHelper.createNotificationChannel(pkg, uid, channel1, true);
+
+        assertEquals(ncg.getId(),
+                mHelper.getNotificationChannel(pkg, uid, channel1.getId(), false).getGroup());
+    }
+
+    @Test
+    public void testGetChannelGroups() throws Exception {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
+        mHelper.createNotificationChannelGroup(pkg, uid, ncg2, true);
+
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1.setGroup(ncg.getId());
+        mHelper.createNotificationChannel(pkg, uid, channel1, true);
+        NotificationChannel channel1a =
+                new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel1a.setGroup(ncg.getId());
+        mHelper.createNotificationChannel(pkg, uid, channel1a, true);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+        channel2.setGroup(ncg2.getId());
+        mHelper.createNotificationChannel(pkg, uid, channel2, true);
+
+        NotificationChannel channel3 =
+                new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(pkg, uid, channel3, true);
+
+        List<NotificationChannelGroup> actual =
+                mHelper.getNotificationChannelGroups(pkg, uid, true).getList();
+        assertEquals(3, actual.size());
+        for (NotificationChannelGroup group: actual) {
+            if (group.getId() == null) {
+                assertEquals(2, group.getChannels().size()); // misc channel too
+                assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
+                || channel3.getId().equals(group.getChannels().get(1).getId()));
+            } else if (group.getId().equals(ncg.getId())) {
+                assertEquals(2, group.getChannels().size());
+                if (group.getChannels().get(0).getId().equals(channel1.getId())) {
+                    assertTrue(group.getChannels().get(1).getId().equals(channel1a.getId()));
+                } else if (group.getChannels().get(0).getId().equals(channel1a.getId())) {
+                    assertTrue(group.getChannels().get(1).getId().equals(channel1.getId()));
+                } else {
+                    fail("expected channel not found");
+                }
+            } else if (group.getId().equals(ncg2.getId())) {
+                assertEquals(1, group.getChannels().size());
+                assertEquals(channel2.getId(), group.getChannels().get(0).getId());
+            }
+        }
+    }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index 460fcdf..69724f4 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -33,6 +33,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 import static org.mockito.Matchers.any;
@@ -42,6 +43,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 
 @SmallTest
@@ -190,6 +192,39 @@
         verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
     }
 
+    @Test
+    public void testGetSnoozedByUser() throws Exception {
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
+        NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
+        NotificationRecord r4 = getNotificationRecord("pkg2", 3, "three", UserHandle.CURRENT);
+        mSnoozeHelper.snooze(r, 1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        mSnoozeHelper.snooze(r3, 1000);
+        mSnoozeHelper.snooze(r4, 1000);
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
+                new int[] {UserHandle.USER_SYSTEM});
+        assertEquals(3, mSnoozeHelper.getSnoozed().size());
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
+                new int[] {UserHandle.USER_CURRENT});
+        assertEquals(1, mSnoozeHelper.getSnoozed().size());
+    }
+
+    @Test
+    public void testGetSnoozedByUser_managedProfiles() throws Exception {
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(
+                new int[] {UserHandle.USER_SYSTEM, UserHandle.USER_CURRENT});
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
+        NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
+        NotificationRecord r4 = getNotificationRecord("pkg2", 3, "three", UserHandle.CURRENT);
+        mSnoozeHelper.snooze(r, 1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        mSnoozeHelper.snooze(r3, 1000);
+        mSnoozeHelper.snooze(r4, 1000);
+        assertEquals(4, mSnoozeHelper.getSnoozed().size());
+    }
+
     private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
             UserHandle user) {
         Notification n = new Notification.Builder(getContext())
@@ -199,8 +234,8 @@
                 .setWhen(1205)
                 .build();
         return new NotificationRecord(getContext(), new StatusBarNotification(
-                pkg, pkg, getDefaultChannel(), id, tag, 0, 0, n, user, null,
-                System.currentTimeMillis()));
+                pkg, pkg, id, tag, 0, 0, n, user, null,
+                System.currentTimeMillis()), getDefaultChannel());
     }
 
     private NotificationChannel getDefaultChannel() {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1393615..6c7f146 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -18,6 +18,7 @@
         package="com.android.frameworks.servicestests">
 
     <uses-permission android:name="android.permission.READ_LOGS" />
+    <uses-permission android:name="android.permission.ACCOUNT_MANAGER" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
@@ -65,6 +66,24 @@
           </intent-filter>
         </service>
 
+        <service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService"
+            android:exported="false">
+          <intent-filter>
+            <action android:name="android.accounts.AccountAuthenticator" />
+          </intent-filter>
+          <meta-data android:name="android.accounts.AccountAuthenticator"
+              android:resource="@xml/test_account_type1_authenticator" />
+        </service>
+
+        <service android:name="com.android.server.accounts.TestAccountType2AuthenticatorService"
+            android:exported="false">
+          <intent-filter>
+            <action android:name="android.accounts.AccountAuthenticator" />
+          </intent-filter>
+          <meta-data android:name="android.accounts.AccountAuthenticator"
+              android:resource="@xml/test_account_type2_authenticator" />
+        </service>
+
         <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
                 android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
@@ -117,7 +136,9 @@
         <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
 
         <activity android:name="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="true" />
+                 android:enabled="true" android:exported="true" />
+
+        <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" />
 
         <activity-alias android:name="a.ShortcutEnabled"
             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
@@ -138,6 +159,12 @@
             android:enabled="true" android:exported="true">
             <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/>
         </activity-alias>
+        <activity-alias android:name="a.ShortcutConfigActivity"
+                        android:targetActivity="com.android.server.pm.ShortcutTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+            </intent-filter>
+        </activity-alias>
 
         <activity-alias android:name="a.DisabledMain"
             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
diff --git a/services/tests/servicestests/res/values/strings.xml b/services/tests/servicestests/res/values/strings.xml
index 2f9d06c..121c1de 100644
--- a/services/tests/servicestests/res/values/strings.xml
+++ b/services/tests/servicestests/res/values/strings.xml
@@ -21,4 +21,8 @@
     <string name="shortcut_title2"></string>
     <string name="shortcut_text2"></string>
     <string name="shortcut_disabled_message2"></string>
+    <string name="test_account_type1_authenticator_label">AccountManagerService Test Account Type1</string>
+    <string name="test_account_type2_authenticator_label">AccountManagerService Test Account Type2</string>
+    <string name="test_account_type1">com.android.server.accounts.account_manager_service_test.account.type1</string>
+    <string name="test_account_type2">com.android.server.accounts.account_manager_service_test.account.type2</string>
 </resources>
diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml b/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml
similarity index 62%
copy from packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
copy to services/tests/servicestests/res/xml/test_account_type1_authenticator.xml
index 85573e0..0c531de 100644
--- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
+++ b/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,11 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accountType="@string/test_account_type1"
+    android:icon="@drawable/icon1"
+    android:smallIcon="@drawable/icon1"
+    android:label="@string/test_account_type1_authenticator_label" />
diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml b/services/tests/servicestests/res/xml/test_account_type2_authenticator.xml
similarity index 62%
copy from packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
copy to services/tests/servicestests/res/xml/test_account_type2_authenticator.xml
index 85573e0..f88eeb9 100644
--- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
+++ b/services/tests/servicestests/res/xml/test_account_type2_authenticator.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,11 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accountType="@string/test_account_type2"
+    android:icon="@drawable/icon1"
+    android:smallIcon="@drawable/icon1"
+    android:label="@string/test_account_type2_authenticator_label" />
diff --git a/services/tests/servicestests/src/com/android/server/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/BaseLockSettingsServiceTests.java
new file mode 100644
index 0000000..c89d35c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BaseLockSettingsServiceTests.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.IActivityManager;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IProgressListener;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.os.storage.IStorageManager;
+import android.security.KeyStore;
+import android.service.gatekeeper.GateKeeperResponse;
+import android.service.gatekeeper.IGateKeeperService;
+import android.test.AndroidTestCase;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.LockSettingsService.SynchronizedStrongAuthTracker;
+import com.android.server.LockSettingsStorage.CredentialHash;
+import com.android.server.MockGateKeeperService.AuthToken;
+import com.android.server.MockGateKeeperService.VerifyHandle;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.File;
+import java.util.Arrays;
+
+
+public class BaseLockSettingsServiceTests extends AndroidTestCase {
+    protected static final int PRIMARY_USER_ID = 0;
+    protected static final int MANAGED_PROFILE_USER_ID = 12;
+    protected static final int SECONDARY_USER_ID = 20;
+
+    private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER_ID, null, null,
+            UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+    private static final UserInfo MANAGED_PROFILE_INFO = new UserInfo(MANAGED_PROFILE_USER_ID, null,
+            null, UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE);
+    private static final UserInfo SECONDARY_USER_INFO = new UserInfo(SECONDARY_USER_ID, null, null,
+            UserInfo.FLAG_INITIALIZED);
+
+    LockSettingsService mService;
+
+    MockLockSettingsContext mContext;
+    LockSettingsStorageTestable mStorage;
+
+    LockPatternUtils mLockPatternUtils;
+    MockGateKeeperService mGateKeeperService;
+    NotificationManager mNotificationManager;
+    UserManager mUserManager;
+    MockStorageManager mStorageManager;
+    IActivityManager mActivityManager;
+
+    KeyStore mKeyStore;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mLockPatternUtils = mock(LockPatternUtils.class);
+        mGateKeeperService = new MockGateKeeperService();
+        mNotificationManager = mock(NotificationManager.class);
+        mUserManager = mock(UserManager.class);
+        mStorageManager = new MockStorageManager();
+        mActivityManager = mock(IActivityManager.class);
+        mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager);
+        mStorage = new LockSettingsStorageTestable(mContext,
+                new File(getContext().getFilesDir(), "locksettings"));
+        File storageDir = mStorage.mStorageDir;
+        if (storageDir.exists()) {
+            FileUtils.deleteContents(storageDir);
+        } else {
+            storageDir.mkdirs();
+        }
+
+        mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils,
+                mStorage, mGateKeeperService, mKeyStore, mStorageManager, mActivityManager);
+        when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
+        when(mUserManager.getProfiles(eq(PRIMARY_USER_ID))).thenReturn(Arrays.asList(
+                new UserInfo[] {PRIMARY_USER_INFO, MANAGED_PROFILE_INFO}));
+        when(mUserManager.getUserInfo(eq(MANAGED_PROFILE_USER_ID))).thenReturn(
+                MANAGED_PROFILE_INFO);
+        when(mUserManager.getProfileParent(eq(MANAGED_PROFILE_USER_ID))).thenReturn(
+                PRIMARY_USER_INFO);
+        when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO);
+
+        when(mActivityManager.unlockUser(anyInt(), any(), any(), any())).thenAnswer(
+                new Answer<Boolean>() {
+            @Override
+            public Boolean answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                mStorageManager.unlockUser((int)args[0], (byte[])args[2],
+                        (IProgressListener) args[3]);
+                return true;
+            }
+        });
+
+        when(mLockPatternUtils.getLockSettings()).thenReturn(mService);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mStorage.closeDatabase();
+        File db = getContext().getDatabasePath("locksettings.db");
+        assertTrue(!db.exists() || db.delete());
+
+        File storageDir = mStorage.mStorageDir;
+        assertTrue(FileUtils.deleteContents(storageDir));
+    }
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/LockSettingsServiceTestable.java
new file mode 100644
index 0000000..613ec0b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsServiceTestable.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import static org.mockito.Mockito.mock;
+
+import android.app.IActivityManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.storage.IStorageManager;
+import android.security.KeyStore;
+import android.service.gatekeeper.IGateKeeperService;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import java.io.FileNotFoundException;
+
+public class LockSettingsServiceTestable extends LockSettingsService {
+
+    private static class MockInjector extends LockSettingsService.Injector {
+
+        private LockSettingsStorage mLockSettingsStorage;
+        private KeyStore mKeyStore;
+        private IActivityManager mActivityManager;
+        private LockPatternUtils mLockPatternUtils;
+        private IStorageManager mStorageManager;
+
+        public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
+                IActivityManager activityManager, LockPatternUtils lockPatternUtils,
+                IStorageManager storageManager) {
+            super(context);
+            mLockSettingsStorage = storage;
+            mKeyStore = keyStore;
+            mActivityManager = activityManager;
+            mLockPatternUtils = lockPatternUtils;
+            mStorageManager = storageManager;
+        }
+
+        @Override
+        public Handler getHandler() {
+            return mock(Handler.class);
+        }
+
+        @Override
+        public LockSettingsStorage getStorage() {
+            return mLockSettingsStorage;
+        }
+
+        @Override
+        public LockSettingsStrongAuth getStrongAuth() {
+            return mock(LockSettingsStrongAuth.class);
+        }
+
+        @Override
+        public SynchronizedStrongAuthTracker getStrongAuthTracker() {
+            return mock(SynchronizedStrongAuthTracker.class);
+        }
+
+        @Override
+        public IActivityManager getActivityManager() {
+            return mActivityManager;
+        }
+
+        @Override
+        public LockPatternUtils getLockPatternUtils() {
+            return mLockPatternUtils;
+        }
+
+        @Override
+        public KeyStore getKeyStore() {
+            return mKeyStore;
+        }
+
+        @Override
+        public IStorageManager getStorageManager() {
+            return mStorageManager;
+        }
+    }
+
+    protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
+            LockSettingsStorage storage, IGateKeeperService gatekeeper, KeyStore keystore,
+            IStorageManager storageManager, IActivityManager mActivityManager) {
+        super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
+                storageManager));
+        mGateKeeperService = gatekeeper;
+    }
+
+    @Override
+    protected void tieProfileLockToParent(int userId, String password) {
+        mStorage.writeChildProfileLock(userId, password.getBytes());
+    }
+
+    @Override
+    protected String getDecryptedPasswordForTiedProfile(int userId) throws FileNotFoundException {
+        byte[] storedData = mStorage.readChildProfileLock(userId);
+        if (storedData == null) {
+            throw new FileNotFoundException("Child profile lock file not found");
+        }
+        return new String(storedData);
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsServiceTests.java
new file mode 100644
index 0000000..4c2e171
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsServiceTests.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
+import android.os.RemoteException;
+import android.service.gatekeeper.GateKeeperResponse;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.LockSettingsStorage.CredentialHash;
+import com.android.server.MockGateKeeperService.VerifyHandle;
+
+/**
+ * runtest frameworks-services -c com.android.server.LockSettingsServiceTests
+ */
+public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testCreatePasswordPrimaryUser() throws RemoteException {
+        testCreateCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD);
+    }
+
+    public void testCreatePatternPrimaryUser() throws RemoteException {
+        testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN);
+    }
+
+    public void testChangePasswordPrimaryUser() throws RemoteException {
+        testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN,
+                "asdfghjk", CREDENTIAL_TYPE_PASSWORD);
+    }
+
+    public void testChangePatternPrimaryUser() throws RemoteException {
+        testChangeCredentials(PRIMARY_USER_ID, "!£$%^&*(())", CREDENTIAL_TYPE_PASSWORD,
+                "1596321", CREDENTIAL_TYPE_PATTERN);
+    }
+
+    public void testChangePasswordFailPrimaryUser() throws RemoteException {
+        final long sid = 1234;
+        final String FAILED_MESSAGE = "Failed to enroll password";
+        initializeStorageWithCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
+
+        try {
+            mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, "badpwd",
+                    PRIMARY_USER_ID);
+            fail("Did not fail when enrolling using incorrect credential");
+        } catch (RemoteException expected) {
+            assertTrue(expected.getMessage().equals(FAILED_MESSAGE));
+        }
+        try {
+            mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
+            fail("Did not fail when enrolling using incorrect credential");
+        } catch (RemoteException expected) {
+            assertTrue(expected.getMessage().equals(FAILED_MESSAGE));
+        }
+        assertVerifyCredentials(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
+    }
+
+    public void testClearPasswordPrimaryUser() throws RemoteException {
+        final String PASSWORD = "password";
+        initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234);
+        mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD, PRIMARY_USER_ID);
+        assertFalse(mService.havePassword(PRIMARY_USER_ID));
+        assertFalse(mService.havePattern(PRIMARY_USER_ID));
+        assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
+    }
+
+    public void testManagedProfileUnifiedChallenge() throws RemoteException {
+        final String UnifiedPassword = "testManagedProfileUnifiedChallenge-pwd";
+        mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PRIMARY_USER_ID);
+        mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
+        final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+        final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
+        assertTrue(primarySid != 0);
+        assertTrue(profileSid != 0);
+        assertTrue(profileSid != primarySid);
+
+        // clear auth token and wait for verify challenge from primary user to re-generate it.
+        mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID);
+        // verify credential
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+                .getResponseCode());
+
+        // Verify that we have a new auth token for the profile
+        assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
+        assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
+
+        /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new
+         * credential as part of verifyCredential() before the new credential is committed in
+         * StorageManager. So we relax the check in our mock StorageManager to allow that.
+         */
+        mStorageManager.setIgnoreBadUnlock(true);
+        // Change primary password and verify that profile SID remains
+        mService.setLockCredential("pwd", LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                UnifiedPassword, PRIMARY_USER_ID);
+        mStorageManager.setIgnoreBadUnlock(false);
+        assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
+    }
+
+    public void testManagedProfileSeparateChallenge() throws RemoteException {
+        final String primaryPassword = "testManagedProfileSeparateChallenge-primary";
+        final String profilePassword = "testManagedProfileSeparateChallenge-profile";
+        mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PRIMARY_USER_ID);
+        /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new
+         * credential as part of verifyCredential() before the new credential is committed in
+         * StorageManager. So we relax the check in our mock StorageManager to allow that.
+         */
+        mStorageManager.setIgnoreBadUnlock(true);
+        mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                MANAGED_PROFILE_USER_ID);
+        mStorageManager.setIgnoreBadUnlock(false);
+
+        final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
+        final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
+        assertTrue(primarySid != 0);
+        assertTrue(profileSid != 0);
+        assertTrue(profileSid != primarySid);
+
+        // clear auth token and make sure verify challenge from primary user does not regenerate it.
+        mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID);
+        // verify primary credential
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+                .getResponseCode());
+        assertNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
+
+        // verify profile credential
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
+                MANAGED_PROFILE_USER_ID).getResponseCode());
+        assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
+        assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
+
+        // Change primary credential and make sure we don't affect profile
+        mStorageManager.setIgnoreBadUnlock(true);
+        mService.setLockCredential("pwd", LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                primaryPassword, PRIMARY_USER_ID);
+        mStorageManager.setIgnoreBadUnlock(false);
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
+                MANAGED_PROFILE_USER_ID).getResponseCode());
+        assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
+    }
+
+    private void testCreateCredential(int userId, String credential, int type)
+            throws RemoteException {
+        mService.setLockCredential(credential, type, null, userId);
+        assertVerifyCredentials(userId, credential, type, -1);
+    }
+
+    private void testChangeCredentials(int userId, String newCredential, int newType,
+            String oldCredential, int oldType) throws RemoteException {
+        final long sid = 1234;
+        initializeStorageWithCredential(userId, oldCredential, oldType, sid);
+        mService.setLockCredential(newCredential, newType, oldCredential, userId);
+        assertVerifyCredentials(userId, newCredential, newType, sid);
+    }
+
+    private void assertVerifyCredentials(int userId, String credential, int type, long sid)
+            throws RemoteException{
+        final long challenge = 54321;
+        VerifyCredentialResponse response = mService.verifyCredential(credential, type, challenge,
+                userId);
+
+        assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode());
+        if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId));
+        final int incorrectType;
+        if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+            assertTrue(mService.havePassword(userId));
+            assertFalse(mService.havePattern(userId));
+            incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+        } else if (type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN){
+            assertFalse(mService.havePassword(userId));
+            assertTrue(mService.havePattern(userId));
+            incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+        } else {
+            assertFalse(mService.havePassword(userId));
+            assertFalse(mService.havePassword(userId));
+            incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+        }
+        // check for bad type
+        assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(credential,
+                incorrectType, challenge, userId).getResponseCode());
+        // check for bad credential
+        assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential("0" + credential,
+                type, challenge, userId).getResponseCode());
+    }
+
+    private void initializeStorageWithCredential(int userId, String credential, int type, long sid) {
+        byte[] oldHash = new VerifyHandle(credential.getBytes(), sid).toBytes();
+        if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+            mStorage.writeCredentialHash(CredentialHash.create(oldHash,
+                    LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
+        } else {
+            mStorage.writeCredentialHash(CredentialHash.create(oldHash,
+                    LockPatternUtils.CREDENTIAL_TYPE_PATTERN), userId);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTestable.java
new file mode 100644
index 0000000..e81b02f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTestable.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.content.Context;
+
+import java.io.File;
+
+public class LockSettingsStorageTestable extends LockSettingsStorage {
+
+    public File mStorageDir;
+
+    public LockSettingsStorageTestable(Context context, File storageDir) {
+        super(context);
+        mStorageDir = storageDir;
+    }
+
+    @Override
+    String getLockPatternFilename(int userId) {
+        return new File(mStorageDir,
+                super.getLockPatternFilename(userId).replace('/', '-')).getAbsolutePath();
+    }
+
+    @Override
+    String getLockPasswordFilename(int userId) {
+        return new File(mStorageDir,
+                super.getLockPasswordFilename(userId).replace('/', '-')).getAbsolutePath();
+    }
+
+    @Override
+    String getChildProfileLockFile(int userId) {
+        return new File(mStorageDir,
+                super.getChildProfileLockFile(userId).replace('/', '-')).getAbsolutePath();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
index 9d52153..d110fea 100644
--- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
@@ -20,6 +20,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.NotificationManager;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.UserInfo;
@@ -60,46 +61,22 @@
         assertTrue(FileUtils.deleteContents(mStorageDir));
         assertTrue(!mDb.exists() || mDb.delete());
 
-        final Context ctx = getContext();
         final UserManager mockUserManager = mock(UserManager.class);
         // User 2 is a profile of user 1.
         when(mockUserManager.getProfileParent(eq(2))).thenReturn(new UserInfo(1, "name", 0));
         // User 3 is a profile of user 0.
         when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
-        setContext(new ContextWrapper(ctx) {
-            @Override
-            public Object getSystemService(String name) {
-                if (USER_SERVICE.equals(name)) {
-                    return mockUserManager;
-                }
-                return super.getSystemService(name);
-            }
-        });
 
-        mStorage = new LockSettingsStorage(getContext(), new LockSettingsStorage.Callback() {
-            @Override
-            public void initialize(SQLiteDatabase db) {
-                mStorage.writeKeyValue(db, "initializedKey", "initialValue", 0);
-            }
-        }) {
-            @Override
-            String getLockPatternFilename(int userId) {
-                return new File(mStorageDir,
-                        super.getLockPatternFilename(userId).replace('/', '-')).getAbsolutePath();
-            }
-
-            @Override
-            String getLockPasswordFilename(int userId) {
-                return new File(mStorageDir,
-                        super.getLockPasswordFilename(userId).replace('/', '-')).getAbsolutePath();
-            }
-
-            @Override
-            String getChildProfileLockFile(int userId) {
-                return new File(mStorageDir,
-                        super.getChildProfileLockFile(userId).replace('/', '-')).getAbsolutePath();
-            }
-        };
+        MockLockSettingsContext context = new MockLockSettingsContext(getContext(), mockUserManager,
+                mock(NotificationManager.class));
+        mStorage = new LockSettingsStorageTestable(context,
+                new File(getContext().getFilesDir(), "locksettings"));
+        mStorage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() {
+                    @Override
+                    public void initialize(SQLiteDatabase db) {
+                        mStorage.writeKeyValue(db, "initializedKey", "initialValue", 0);
+                    }
+                });
     }
 
     @Override
@@ -323,7 +300,7 @@
     }
 
     public void testFileLocation_Owner() {
-        LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
+        LockSettingsStorage storage = new LockSettingsStorage(getContext());
 
         assertEquals("/data/system/gesture.key", storage.getLegacyLockPatternFilename(0));
         assertEquals("/data/system/password.key", storage.getLegacyLockPasswordFilename(0));
@@ -332,21 +309,21 @@
     }
 
     public void testFileLocation_SecondaryUser() {
-        LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
+        LockSettingsStorage storage = new LockSettingsStorage(getContext());
 
         assertEquals("/data/system/users/1/gatekeeper.pattern.key", storage.getLockPatternFilename(1));
         assertEquals("/data/system/users/1/gatekeeper.password.key", storage.getLockPasswordFilename(1));
     }
 
     public void testFileLocation_ProfileToSecondary() {
-        LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
+        LockSettingsStorage storage = new LockSettingsStorage(getContext());
 
         assertEquals("/data/system/users/2/gatekeeper.pattern.key", storage.getLockPatternFilename(2));
         assertEquals("/data/system/users/2/gatekeeper.password.key", storage.getLockPasswordFilename(2));
     }
 
     public void testFileLocation_ProfileToOwner() {
-        LockSettingsStorage storage = new LockSettingsStorage(getContext(), null);
+        LockSettingsStorage storage = new LockSettingsStorage(getContext());
 
         assertEquals("/data/system/users/3/gatekeeper.pattern.key", storage.getLockPatternFilename(3));
         assertEquals("/data/system/users/3/gatekeeper.password.key", storage.getLockPasswordFilename(3));
diff --git a/services/tests/servicestests/src/com/android/server/MockGateKeeperService.java b/services/tests/servicestests/src/com/android/server/MockGateKeeperService.java
new file mode 100644
index 0000000..15983ca
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/MockGateKeeperService.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.gatekeeper.GateKeeperResponse;
+import android.service.gatekeeper.IGateKeeperService;
+import android.util.ArrayMap;
+
+import junit.framework.AssertionFailedError;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+
+public class MockGateKeeperService implements IGateKeeperService {
+    static class VerifyHandle {
+        public byte[] password;
+        public long sid;
+
+        public VerifyHandle(byte[] password, long sid) {
+            this.password = password;
+            this.sid = sid;
+        }
+
+        public VerifyHandle(byte[] handle) {
+            ByteBuffer buffer = ByteBuffer.allocate(handle.length);
+            buffer.put(handle, 0, handle.length);
+            buffer.flip();
+            int version = buffer.get();
+            sid = buffer.getLong();
+            password = new byte[buffer.remaining()];
+            buffer.get(password);
+        }
+
+        public byte[] toBytes() {
+            ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + password.length);
+            buffer.put((byte)0);
+            buffer.putLong(sid);
+            buffer.put(password);
+            return buffer.array();
+        }
+    }
+
+    static class AuthToken {
+        public long challenge;
+        public long sid;
+
+        public AuthToken(long challenge, long sid) {
+            this.challenge = challenge;
+            this.sid = sid;
+        }
+
+        public AuthToken(byte[] handle) {
+            ByteBuffer buffer = ByteBuffer.allocate(handle.length);
+            buffer.put(handle, 0, handle.length);
+            buffer.flip();
+            int version = buffer.get();
+            challenge = buffer.getLong();
+            sid = buffer.getLong();
+        }
+
+        public byte[] toBytes() {
+            ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + Long.BYTES);
+            buffer.put((byte)0);
+            buffer.putLong(challenge);
+            buffer.putLong(sid);
+            return buffer.array();
+        }
+    }
+
+    private ArrayMap<Integer, Long> sidMap = new ArrayMap<>();
+    private ArrayMap<Integer, AuthToken> authTokenMap = new ArrayMap<>();
+
+    private ArrayMap<Integer, byte[]> handleMap = new ArrayMap<>();
+
+    @Override
+    public GateKeeperResponse enroll(int uid, byte[] currentPasswordHandle, byte[] currentPassword,
+            byte[] desiredPassword) throws android.os.RemoteException {
+
+        if (currentPasswordHandle != null) {
+            VerifyHandle handle = new VerifyHandle(currentPasswordHandle);
+            if (Arrays.equals(currentPassword, handle.password)) {
+                // Trusted enroll
+                VerifyHandle newHandle = new VerifyHandle(desiredPassword, handle.sid);
+                refreshSid(uid, handle.sid, false);
+                handleMap.put(uid, newHandle.toBytes());
+                return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
+            } else {
+                return null;
+            }
+        } else {
+            // Untrusted enroll
+            long newSid = new Random().nextLong();
+            VerifyHandle newHandle = new VerifyHandle(desiredPassword, newSid);
+            refreshSid(uid, newSid, true);
+            handleMap.put(uid, newHandle.toBytes());
+            return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
+        }
+    }
+
+    @Override
+    public GateKeeperResponse verify(int uid, byte[] enrolledPasswordHandle,
+            byte[] providedPassword) throws android.os.RemoteException {
+        return verifyChallenge(uid, 0, enrolledPasswordHandle, providedPassword);
+    }
+
+    @Override
+    public GateKeeperResponse verifyChallenge(int uid, long challenge,
+            byte[] enrolledPasswordHandle, byte[] providedPassword) throws RemoteException {
+
+        VerifyHandle handle = new VerifyHandle(enrolledPasswordHandle);
+        if (Arrays.equals(handle.password, providedPassword)) {
+            byte[] knownHandle = handleMap.get(uid);
+            if (knownHandle != null) {
+                if (!Arrays.equals(knownHandle, enrolledPasswordHandle)) {
+                    throw new AssertionFailedError("Got correct but obsolete handle");
+                }
+            }
+            refreshSid(uid, handle.sid, false);
+            AuthToken token = new AuthToken(challenge, handle.sid);
+            refreshAuthToken(uid, token);
+            return GateKeeperResponse.createOkResponse(token.toBytes(), false);
+        } else {
+            return GateKeeperResponse.createGenericResponse(GateKeeperResponse.RESPONSE_ERROR);
+        }
+    }
+
+    private void refreshAuthToken(int uid, AuthToken token) {
+        authTokenMap.put(uid, token);
+    }
+
+    public AuthToken getAuthToken(int uid) {
+        return authTokenMap.get(uid);
+    }
+
+    public void clearAuthToken(int uid) {
+        authTokenMap.remove(uid);
+    }
+
+    @Override
+    public IBinder asBinder() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearSecureUserId(int userId) throws RemoteException {
+        sidMap.remove(userId);
+    }
+
+    @Override
+    public long getSecureUserId(int userId) throws RemoteException {
+        if (sidMap.containsKey(userId)) {
+            return sidMap.get(userId);
+        } else {
+            return 0L;
+        }
+    }
+
+    private void refreshSid(int uid, long sid, boolean force) {
+        if (!sidMap.containsKey(uid) || force) {
+            sidMap.put(uid, sid);
+        } else{
+            if (sidMap.get(uid) != sid) {
+                throw new AssertionFailedError("Inconsistent SID");
+            }
+        }
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/MockLockSettingsContext.java
new file mode 100644
index 0000000..b63936f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/MockLockSettingsContext.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.os.UserManager;
+
+public class MockLockSettingsContext extends ContextWrapper {
+
+    private UserManager mUserManager;
+    private NotificationManager mNotificationManager;
+
+    public MockLockSettingsContext(Context base, UserManager userManager,
+            NotificationManager notificationManager) {
+        super(base);
+        mUserManager = userManager;
+        mNotificationManager = notificationManager;
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        if (USER_SERVICE.equals(name)) {
+            return mUserManager;
+        } else if (NOTIFICATION_SERVICE.equals(name)) {
+            return mNotificationManager;
+        } else {
+            throw new RuntimeException("System service not mocked: " + name);
+        }
+    }
+
+    @Override
+    public void enforceCallingOrSelfPermission(String permission, String message) {
+        // Skip permission checks for unit tests.
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/MockStorageManager.java b/services/tests/servicestests/src/com/android/server/MockStorageManager.java
new file mode 100644
index 0000000..031a3b3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/MockStorageManager.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.content.pm.IPackageMoveObserver;
+import android.os.IBinder;
+import android.os.IProgressListener;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.storage.DiskInfo;
+import android.os.storage.IObbActionListener;
+import android.os.storage.IStorageEventListener;
+import android.os.storage.IStorageManager;
+import android.os.storage.IStorageShutdownObserver;
+import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+import android.util.ArrayMap;
+import android.util.Pair;
+
+import com.android.internal.os.AppFuseMount;
+
+import junit.framework.AssertionFailedError;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class MockStorageManager implements IStorageManager {
+
+    private ArrayMap<Integer, ArrayList<Pair<byte[], byte[]>>> mAuth = new ArrayMap<>();
+    private boolean mIgnoreBadUnlock;
+
+    @Override
+    public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret)
+            throws RemoteException {
+        getUserAuth(userId).add(new Pair<>(token, secret));
+    }
+
+    @Override
+    public void fixateNewestUserKeyAuth(int userId) throws RemoteException {
+        ArrayList<Pair<byte[], byte[]>> auths = mAuth.get(userId);
+        Pair<byte[], byte[]> latest = auths.get(auths.size() - 1);
+        auths.clear();
+        auths.add(latest);
+    }
+
+    private ArrayList<Pair<byte[], byte[]>> getUserAuth(int userId) {
+        if (!mAuth.containsKey(userId)) {
+            ArrayList<Pair<byte[], byte[]>> auths = new ArrayList<Pair<byte[], byte[]>>();
+            auths.add(new Pair(null, null));
+            mAuth.put(userId,  auths);
+        }
+        return mAuth.get(userId);
+    }
+
+    public byte[] getUserUnlockToken(int userId) {
+        ArrayList<Pair<byte[], byte[]>> auths = getUserAuth(userId);
+        if (auths.size() != 1) {
+            throw new AssertionFailedError("More than one secret exists");
+        }
+        return auths.get(0).second;
+    }
+
+    public void unlockUser(int userId, byte[] secret, IProgressListener listener)
+            throws RemoteException {
+        listener.onStarted(userId, null);
+        listener.onFinished(userId, null);
+        ArrayList<Pair<byte[], byte[]>> auths = getUserAuth(userId);
+        if (secret != null) {
+            if (auths.size() > 1) {
+                throw new AssertionFailedError("More than one secret exists");
+            }
+            Pair<byte[], byte[]> auth = auths.get(0);
+            if ((!mIgnoreBadUnlock) && auth.second != null && !Arrays.equals(secret, auth.second)) {
+                throw new AssertionFailedError("Invalid secret to unlock user");
+            }
+        } else {
+            if (auths != null && auths.size() > 0) {
+                throw new AssertionFailedError("Cannot unlock encrypted user with empty token");
+            }
+        }
+    }
+
+    public void setIgnoreBadUnlock(boolean ignore) {
+        mIgnoreBadUnlock = ignore;
+    }
+
+    @Override
+    public IBinder asBinder() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void registerListener(IStorageEventListener listener) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unregisterListener(IStorageEventListener listener) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isUsbMassStorageConnected() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setUsbMassStorageEnabled(boolean enable) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isUsbMassStorageEnabled() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int mountVolume(String mountPoint) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unmountVolume(String mountPoint, boolean force, boolean removeEncryption)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+
+    }
+
+    @Override
+    public int formatVolume(String mountPoint) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int[] getStorageUsers(String path) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getVolumeState(String mountPoint) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid,
+            boolean external) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int finalizeSecureContainer(String id) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int destroySecureContainer(String id, boolean force) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int unmountSecureContainer(String id, boolean force) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isSecureContainerMounted(String id) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int renameSecureContainer(String oldId, String newId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getSecureContainerPath(String id) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] getSecureContainerList() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void shutdown(IStorageShutdownObserver observer) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void finishMediaUpdate() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void mountObb(String rawPath, String canonicalPath, String key, IObbActionListener token,
+            int nonce) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isObbMounted(String rawPath) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getMountedObbPath(String rawPath) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isExternalStorageEmulated() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int decryptStorage(String password) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int encryptStorage(int type, String password) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int changeEncryptionPassword(int type, String password) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public StorageVolume[] getVolumeList(int uid, String packageName, int flags)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getSecureContainerFilesystemPath(String cid) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getEncryptionState() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int verifyEncryptionPassword(String password) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int fixPermissionsSecureContainer(String id, int gid, String filename)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int mkdirs(String callingPkg, String path) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPasswordType() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getPassword() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clearPassword() throws RemoteException {
+        throw new UnsupportedOperationException();
+
+    }
+
+    @Override
+    public void setField(String field, String contents) throws RemoteException {
+        throw new UnsupportedOperationException();
+
+    }
+
+    @Override
+    public String getField(String field) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int resizeSecureContainer(String id, int sizeMb, String key) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long lastMaintenance() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void runMaintenance() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void waitForAsecScan() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DiskInfo[] getDisks() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public VolumeInfo[] getVolumes(int flags) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public VolumeRecord[] getVolumeRecords(int flags) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void mount(String volId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unmount(String volId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void format(String volId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void partitionPublic(String diskId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void partitionPrivate(String diskId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void partitionMixed(String diskId, int ratio) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setVolumeNickname(String fsUuid, String nickname) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void forgetVolume(String fsUuid) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void forgetAllVolumes() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getPrimaryStorageUuid() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long benchmark(String volId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void setDebugFlags(int flags, int mask) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void createUserKey(int userId, int serialNumber, boolean ephemeral)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void destroyUserKey(int userId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void lockUserKey(int userId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isUserKeyUnlocked(int userId) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void destroyUserStorage(String volumeUuid, int userId, int flags)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isConvertibleToFBE() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void fstrim(int flags) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public AppFuseMount mountProxyFileDescriptorBridge() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long getCacheQuotaBytes(String volumeUuid, int uid) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long getCacheSizeBytes(String volumeUuid, int uid) throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 1189dae..152b9c9 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -24,6 +24,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
@@ -60,21 +61,33 @@
 import android.net.RecommendationRequest;
 import android.net.RecommendationResult;
 import android.net.ScoredNetwork;
+import android.net.Uri;
 import android.net.WifiKey;
+import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiSsid;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.server.devicepolicy.MockUtils;
 
+import com.google.android.collect.Lists;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -88,7 +101,12 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.UnaryOperator;
 
 /**
  * Tests for {@link NetworkScoreService}.
@@ -96,24 +114,40 @@
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class NetworkScoreServiceTest {
+    private static final String SSID = "ssid";
+    private static final String SSID_2 = "ssid_2";
+    private static final String SSID_3 = "ssid_3";
     private static final ScoredNetwork SCORED_NETWORK =
-            new ScoredNetwork(new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00")),
+            new ScoredNetwork(new NetworkKey(new WifiKey(quote(SSID), "00:00:00:00:00:00")),
+                    null /* rssiCurve*/);
+    private static final ScoredNetwork SCORED_NETWORK_2 =
+            new ScoredNetwork(new NetworkKey(new WifiKey(quote(SSID_2), "00:00:00:00:00:00")),
                     null /* rssiCurve*/);
     private static final NetworkScorerAppData NEW_SCORER =
         new NetworkScorerAppData("newPackageName", 1, "newScoringServiceClass");
 
-    @Mock private PackageManager mPackageManager;
     @Mock private NetworkScorerAppManager mNetworkScorerAppManager;
     @Mock private Context mContext;
     @Mock private Resources mResources;
     @Mock private INetworkScoreCache.Stub mNetworkScoreCache, mNetworkScoreCache2;
     @Mock private IBinder mIBinder, mIBinder2;
     @Mock private INetworkRecommendationProvider mRecommendationProvider;
+    @Mock private UnaryOperator<List<ScoredNetwork>> mCurrentNetworkFilter;
+    @Mock private UnaryOperator<List<ScoredNetwork>> mScanResultsFilter;
+    @Mock private WifiInfo mWifiInfo;
     @Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
 
     private ContentResolver mContentResolver;
     private NetworkScoreService mNetworkScoreService;
     private RecommendationRequest mRecommendationRequest;
+    private RemoteCallback mRemoteCallback;
+    private OnResultListener mOnResultListener;
+    private HandlerThread mHandlerThread;
+    private List<ScanResult> mScanResults;
+
+    private static String quote(String str) {
+        return String.format("\"%s\"", str);
+    }
 
     @Before
     public void setUp() throws Exception {
@@ -123,12 +157,42 @@
         mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
         when(mContext.getResources()).thenReturn(mResources);
-        mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager);
+        when(mWifiInfo.getSSID()).thenReturn(SCORED_NETWORK.networkKey.wifiKey.ssid);
+        when(mWifiInfo.getBSSID()).thenReturn(SCORED_NETWORK.networkKey.wifiKey.bssid);
+        mHandlerThread = new HandlerThread("NetworkScoreServiceTest");
+        mHandlerThread.start();
+        mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager,
+                mHandlerThread.getLooper());
         WifiConfiguration configuration = new WifiConfiguration();
         configuration.SSID = "NetworkScoreServiceTest_SSID";
         configuration.BSSID = "NetworkScoreServiceTest_BSSID";
         mRecommendationRequest = new RecommendationRequest.Builder()
-            .setCurrentRecommendedWifiConfig(configuration).build();
+            .setDefaultWifiConfig(configuration).build();
+        mOnResultListener = new OnResultListener();
+        mRemoteCallback = new RemoteCallback(mOnResultListener);
+        Settings.Global.putLong(mContentResolver,
+                Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, -1L);
+        mNetworkScoreService.refreshRecommendationRequestTimeoutMs();
+        populateScanResults();
+    }
+
+    private void populateScanResults() {
+        mScanResults = new ArrayList<>();
+        mScanResults.add(createScanResult(SSID, SCORED_NETWORK.networkKey.wifiKey.bssid));
+        mScanResults.add(createScanResult(SSID_2, SCORED_NETWORK_2.networkKey.wifiKey.bssid));
+        mScanResults.add(createScanResult(SSID_3, "10:10:00:00:10:10"));
+    }
+
+    private ScanResult createScanResult(String ssid, String bssid) {
+        ScanResult result = new ScanResult();
+        result.wifiSsid = WifiSsid.createFromAsciiEncoded(ssid);
+        result.BSSID = bssid;
+        return result;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
     }
 
     @Test
@@ -214,7 +278,7 @@
         final RecommendationResult result =
                 mNetworkScoreService.requestRecommendation(mRecommendationRequest);
         assertNotNull(result);
-        assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+        assertEquals(mRecommendationRequest.getDefaultWifiConfig(),
                 result.getWifiConfiguration());
     }
 
@@ -229,7 +293,7 @@
         final RecommendationResult result =
                 mNetworkScoreService.requestRecommendation(mRecommendationRequest);
         assertNotNull(result);
-        assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+        assertEquals(mRecommendationRequest.getDefaultWifiConfig(),
                 result.getWifiConfiguration());
     }
 
@@ -262,6 +326,116 @@
     }
 
     @Test
+    public void testRequestRecommendationAsync_noPermission() throws Exception {
+        doThrow(new SecurityException()).when(mContext)
+                .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES),
+                        anyString());
+        try {
+            mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+                    mRemoteCallback);
+            fail("REQUEST_NETWORK_SCORES not enforced.");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testRequestRecommendationAsync_providerNotConnected() throws Exception {
+        mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+                mRemoteCallback);
+        boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS);
+        assertTrue(callbackRan);
+        verifyZeroInteractions(mRecommendationProvider);
+    }
+
+    @Test
+    public void testRequestRecommendationAsync_requestTimesOut() throws Exception {
+        injectProvider();
+        Settings.Global.putLong(mContentResolver,
+                Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, 1L);
+        mNetworkScoreService.refreshRecommendationRequestTimeoutMs();
+        mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+                mRemoteCallback);
+        boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS);
+        assertTrue(callbackRan);
+        verify(mRecommendationProvider).requestRecommendation(eq(mRecommendationRequest),
+                isA(IRemoteCallback.Stub.class), anyInt());
+
+        assertTrue(mOnResultListener.receivedBundle.containsKey(EXTRA_RECOMMENDATION_RESULT));
+        RecommendationResult result =
+                mOnResultListener.receivedBundle.getParcelable(EXTRA_RECOMMENDATION_RESULT);
+        assertTrue(result.hasRecommendation());
+        assertEquals(mRecommendationRequest.getDefaultWifiConfig().SSID,
+                result.getWifiConfiguration().SSID);
+    }
+
+    @Test
+    public void testRequestRecommendationAsync_requestSucceeds() throws Exception {
+        injectProvider();
+        final Bundle bundle = new Bundle();
+        doAnswer(invocation -> {
+            invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
+            return null;
+        }).when(mRecommendationProvider)
+                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
+                        anyInt());
+
+        mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+                mRemoteCallback);
+        boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS);
+        assertTrue(callbackRan);
+        // If it's not the same instance then something else ran the callback.
+        assertSame(bundle, mOnResultListener.receivedBundle);
+    }
+
+    @Test
+    public void testRequestRecommendationAsync_requestThrowsRemoteException() throws Exception {
+        injectProvider();
+        doThrow(new RemoteException()).when(mRecommendationProvider)
+                .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
+                        anyInt());
+
+        mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+                mRemoteCallback);
+        boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS);
+        assertTrue(callbackRan);
+    }
+
+    @Test
+    public void dispatchingContentObserver_nullUri() throws Exception {
+        NetworkScoreService.DispatchingContentObserver observer =
+                new NetworkScoreService.DispatchingContentObserver(mContext, null /*handler*/);
+
+        observer.onChange(false, null);
+        // nothing to assert or verify but since we passed in a null handler we'd see a NPE
+        // if it were interacted with.
+    }
+
+    @Test
+    public void dispatchingContentObserver_dispatchUri() throws Exception {
+        final CountDownHandler handler = new CountDownHandler(mHandlerThread.getLooper());
+        NetworkScoreService.DispatchingContentObserver observer =
+                new NetworkScoreService.DispatchingContentObserver(mContext, handler);
+        Uri uri = Uri.parse("content://settings/global/network_score_service_test");
+        int expectedWhat = 24;
+        observer.observe(uri, expectedWhat);
+
+        observer.onChange(false, uri);
+        final boolean msgHandled = handler.latch.await(3, TimeUnit.SECONDS);
+        assertTrue(msgHandled);
+        assertEquals(expectedWhat, handler.receivedWhat);
+    }
+
+    @Test
+    public void oneTimeCallback_multipleCallbacks() throws Exception {
+        NetworkScoreService.OneTimeCallback callback =
+                new NetworkScoreService.OneTimeCallback(mRemoteCallback);
+        callback.sendResult(null);
+        callback.sendResult(null);
+        assertEquals(1, mOnResultListener.resultCount);
+    }
+
+    @Test
     public void testUpdateScores_notActiveScorer() {
         bindToScorer(false /*callerIsScorer*/);
 
@@ -486,6 +660,173 @@
         assertEquals(NEW_SCORER.packageName, mNetworkScoreService.getActiveScorerPackage());
     }
 
+    @Test
+    public void testCacheUpdatingConsumer_nullFilter() throws Exception {
+        List<ScoredNetwork> scoredNetworkList = Lists.newArrayList(SCORED_NETWORK);
+        NetworkScoreService.FilteringCacheUpdatingConsumer consumer =
+                new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext,
+                        new ArrayList<>(scoredNetworkList), NetworkKey.TYPE_WIFI,
+                        mCurrentNetworkFilter, mScanResultsFilter);
+
+        consumer.accept(mNetworkScoreCache, null /*cookie*/);
+
+        verify(mNetworkScoreCache).updateScores(scoredNetworkList);
+        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+    }
+
+    @Test
+    public void testCacheUpdatingConsumer_noneFilter() throws Exception {
+        List<ScoredNetwork> scoredNetworkList = Lists.newArrayList(SCORED_NETWORK);
+        NetworkScoreService.FilteringCacheUpdatingConsumer
+                consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext,
+                new ArrayList<>(scoredNetworkList),
+                NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter);
+
+        consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE);
+
+        verify(mNetworkScoreCache).updateScores(scoredNetworkList);
+        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+    }
+
+    @Test
+    public void testCacheUpdatingConsumer_unknownFilter() throws Exception {
+        List<ScoredNetwork> scoredNetworkList = Lists.newArrayList(SCORED_NETWORK);
+        NetworkScoreService.FilteringCacheUpdatingConsumer
+                consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext,
+                new ArrayList<>(scoredNetworkList),
+                NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter);
+
+        consumer.accept(mNetworkScoreCache, -1 /*cookie*/);
+
+        verify(mNetworkScoreCache).updateScores(scoredNetworkList);
+        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+    }
+
+    @Test
+    public void testCacheUpdatingConsumer_nonIntFilter() throws Exception {
+        List<ScoredNetwork> scoredNetworkList = Lists.newArrayList(SCORED_NETWORK);
+        NetworkScoreService.FilteringCacheUpdatingConsumer
+                consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext,
+                new ArrayList<>(scoredNetworkList),
+                NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter);
+
+        consumer.accept(mNetworkScoreCache, "not an int" /*cookie*/);
+
+        verify(mNetworkScoreCache).updateScores(scoredNetworkList);
+        verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter);
+    }
+
+    @Test
+    public void testCacheUpdatingConsumer_emptyScoreList() throws Exception {
+        NetworkScoreService.FilteringCacheUpdatingConsumer
+                consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext,
+                Collections.emptyList(),
+                NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter);
+
+        consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE);
+
+        verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter);
+    }
+
+    @Test
+    public void testCacheUpdatingConsumer_currentNetworkFilter() throws Exception {
+        List<ScoredNetwork> scoredNetworkList =
+                Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2);
+        NetworkScoreService.FilteringCacheUpdatingConsumer
+                consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext,
+                new ArrayList<>(scoredNetworkList),
+                NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter);
+
+        List<ScoredNetwork> filteredList = new ArrayList<>(scoredNetworkList);
+        filteredList.remove(SCORED_NETWORK);
+        when(mCurrentNetworkFilter.apply(scoredNetworkList)).thenReturn(filteredList);
+        consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK);
+
+        verify(mNetworkScoreCache).updateScores(filteredList);
+        verifyZeroInteractions(mScanResultsFilter);
+    }
+
+    @Test
+    public void testCacheUpdatingConsumer_scanResultsFilter() throws Exception {
+        List<ScoredNetwork> scoredNetworkList =
+                Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2);
+        NetworkScoreService.FilteringCacheUpdatingConsumer
+                consumer = new NetworkScoreService.FilteringCacheUpdatingConsumer(mContext,
+                new ArrayList<>(scoredNetworkList),
+                NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter);
+
+        List<ScoredNetwork> filteredList = new ArrayList<>(scoredNetworkList);
+        filteredList.remove(SCORED_NETWORK);
+        when(mScanResultsFilter.apply(scoredNetworkList)).thenReturn(filteredList);
+        consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS);
+
+        verify(mNetworkScoreCache).updateScores(filteredList);
+        verifyZeroInteractions(mCurrentNetworkFilter);
+    }
+
+    @Test
+    public void testCurrentNetworkScoreCacheFilter_nullWifiInfo() throws Exception {
+        NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter =
+                new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> null /*WifiInfo*/);
+
+        List<ScoredNetwork> actualList =
+                cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2));
+
+        assertTrue(actualList.isEmpty());
+    }
+
+    @Test
+    public void testCurrentNetworkScoreCacheFilter_scoreFiltered() throws Exception {
+        NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter =
+                new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> mWifiInfo);
+
+        List<ScoredNetwork> actualList =
+                cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2));
+
+        List<ScoredNetwork> expectedList = Collections.singletonList(SCORED_NETWORK);
+        assertEquals(expectedList, actualList);
+    }
+
+    @Test
+    public void testCurrentNetworkScoreCacheFilter_currentNetworkNotInList() throws Exception {
+        when(mWifiInfo.getSSID()).thenReturn("\"notInList\"");
+        NetworkScoreService.CurrentNetworkScoreCacheFilter cacheFilter =
+                new NetworkScoreService.CurrentNetworkScoreCacheFilter(() -> mWifiInfo);
+
+        List<ScoredNetwork> actualList =
+                cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2));
+
+        assertTrue(actualList.isEmpty());
+    }
+
+    @Test
+    public void testScanResultsScoreCacheFilter_emptyScanResults() throws Exception {
+        NetworkScoreService.ScanResultsScoreCacheFilter cacheFilter =
+                new NetworkScoreService.ScanResultsScoreCacheFilter(Collections::emptyList);
+
+        List<ScoredNetwork> actualList =
+                cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2));
+
+        assertTrue(actualList.isEmpty());
+    }
+
+    @Test
+    public void testScanResultsScoreCacheFilter_scoresFiltered() throws Exception {
+        NetworkScoreService.ScanResultsScoreCacheFilter cacheFilter =
+                new NetworkScoreService.ScanResultsScoreCacheFilter(() -> mScanResults);
+
+        ScoredNetwork unmatchedScore =
+                new ScoredNetwork(new NetworkKey(new WifiKey(quote("newSsid"),
+                        "00:00:00:00:00:00")), null /* rssiCurve*/);
+
+        List<ScoredNetwork> actualList =
+                cacheFilter.apply(Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2,
+                        unmatchedScore));
+
+        List<ScoredNetwork> expectedList = Lists.newArrayList(SCORED_NETWORK, SCORED_NETWORK_2);
+        assertEquals(expectedList, actualList);
+    }
+
     // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
     private void injectProvider() {
         final ComponentName componentName = new ComponentName(NEW_SCORER.packageName,
@@ -515,4 +856,32 @@
                 isA(UserHandle.class))).thenReturn(true);
         mNetworkScoreService.systemRunning();
     }
+
+    private static class OnResultListener implements RemoteCallback.OnResultListener {
+        private final CountDownLatch countDownLatch = new CountDownLatch(1);
+        private int resultCount;
+        private Bundle receivedBundle;
+
+        @Override
+        public void onResult(Bundle result) {
+            countDownLatch.countDown();
+            resultCount++;
+            receivedBundle = result;
+        }
+    }
+
+    private static class CountDownHandler extends Handler {
+        CountDownLatch latch = new CountDownLatch(1);
+        int receivedWhat;
+
+        CountDownHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            latch.countDown();
+            receivedWhat = msg.what;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountAuthenticatorDummyActivity.java b/services/tests/servicestests/src/com/android/server/accounts/AccountAuthenticatorDummyActivity.java
new file mode 100644
index 0000000..8a14e1b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountAuthenticatorDummyActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.accounts;
+
+import android.accounts.AccountAuthenticatorResponse;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Activity used by {@link com.android.server.accounts.TestAccountAuthenticator} to test the
+ * behavior of {@link AccountManagerService} when authenticator returns intent.
+ */
+public class AccountAuthenticatorDummyActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        Intent intent = getIntent();
+        AccountAuthenticatorResponse response =
+                intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK);
+        Intent result = intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_RESULT);
+        if (response != null) {
+            response.onResult(result.getExtras());
+        }
+        setResult(RESULT_OK, result);
+        finish();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index c74cda6..213fb27 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -17,30 +17,49 @@
 package com.android.server.accounts;
 
 import static android.database.sqlite.SQLiteDatabase.deleteDatabase;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.accounts.AccountManagerInternal;
 import android.accounts.AuthenticatorDescription;
+import android.accounts.CantAddAccountActivity;
+import android.accounts.IAccountManagerResponse;
 import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.app.INotificationManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.RegisteredServicesCache.ServiceInfo;
 import android.content.pm.RegisteredServicesCacheListener;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
 import android.content.pm.UserInfo;
+import android.content.pm.RegisteredServicesCache.ServiceInfo;
 import android.database.Cursor;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.AndroidTestCase;
@@ -48,31 +67,85 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
+import com.android.frameworks.servicestests.R;
+import com.android.server.LocalServices;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 public class AccountManagerServiceTest extends AndroidTestCase {
     private static final String TAG = AccountManagerServiceTest.class.getSimpleName();
+    private static final long ONE_DAY_IN_MILLISECOND = 86400000;
 
+    @Mock private Context mMockContext;
+    @Mock private AppOpsManager mMockAppOpsManager;
+    @Mock private UserManager mMockUserManager;
+    @Mock private PackageManager mMockPackageManager;
+    @Mock private DevicePolicyManagerInternal mMockDevicePolicyManagerInternal;
+    @Mock private DevicePolicyManager mMockDevicePolicyManager;
+    @Mock private IAccountManagerResponse mMockAccountManagerResponse;
+    @Mock private IBinder mMockBinder;
+    @Mock private INotificationManager mMockNotificationManager;
+
+    @Captor private ArgumentCaptor<Intent> mIntentCaptor;
+    @Captor private ArgumentCaptor<Bundle> mBundleCaptor;
+
+    private static final int LATCH_TIMEOUT_MS = 500;
     private static final String PREN_DB = "pren.db";
     private static final String DE_DB = "de.db";
     private static final String CE_DB = "ce.db";
+    private PackageInfo mPackageInfo;
     private AccountManagerService mAms;
     private TestInjector mTestInjector;
 
     @Override
     protected void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockPackageManager.checkSignatures(anyInt(), anyInt()))
+                    .thenReturn(PackageManager.SIGNATURE_MATCH);
+        final UserInfo ui = new UserInfo(UserHandle.USER_SYSTEM, "user0", 0);
+        when(mMockUserManager.getUserInfo(eq(ui.id))).thenReturn(ui);
+        when(mMockContext.createPackageContextAsUser(
+                 anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+
+        mPackageInfo = new PackageInfo();
+        mPackageInfo.signatures = new Signature[1];
+        mPackageInfo.signatures[0] = new Signature(new byte[] {'a', 'b', 'c', 'd'});
+        mPackageInfo.applicationInfo = new ApplicationInfo();
+        mPackageInfo.applicationInfo.privateFlags = ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+        when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
+        when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
+        when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
+        when(mMockContext.getSystemServiceName(AppOpsManager.class)).thenReturn(
+                Context.APP_OPS_SERVICE);
+        when(mMockContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+                PackageManager.PERMISSION_GRANTED);
+        Bundle bundle = new Bundle();
+        when(mMockUserManager.getUserRestrictions(any(UserHandle.class))).thenReturn(bundle);
+        when(mMockContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+                mMockDevicePolicyManager);
+        when(mMockAccountManagerResponse.asBinder()).thenReturn(mMockBinder);
+
         Context realTestContext = getContext();
-        MyMockContext mockContext = new MyMockContext(realTestContext);
+        MyMockContext mockContext = new MyMockContext(realTestContext, mMockContext);
         setContext(mockContext);
-        mTestInjector = new TestInjector(realTestContext, mockContext);
+        mTestInjector = new TestInjector(realTestContext, mockContext, mMockNotificationManager);
         mAms = new AccountManagerService(mTestInjector);
     }
 
@@ -104,12 +177,12 @@
     @SmallTest
     public void testCheckAddAccount() throws Exception {
         unlockSystemUser();
-        Account a11 = new Account("account1", "type1");
-        Account a21 = new Account("account2", "type1");
-        Account a31 = new Account("account3", "type1");
-        Account a12 = new Account("account1", "type2");
-        Account a22 = new Account("account2", "type2");
-        Account a32 = new Account("account3", "type2");
+        Account a11 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        Account a21 = new Account("account2", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        Account a31 = new Account("account3", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        Account a12 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2);
+        Account a22 = new Account("account2", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2);
+        Account a32 = new Account("account3", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2);
         mAms.addAccountExplicitly(a11, "p11", null);
         mAms.addAccountExplicitly(a12, "p12", null);
         mAms.addAccountExplicitly(a21, "p21", null);
@@ -127,7 +200,8 @@
         assertEquals(a22, accounts[4]);
         assertEquals(a32, accounts[5]);
 
-        accounts = mAms.getAccounts("type1", mContext.getOpPackageName());
+        accounts = mAms.getAccounts(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                mContext.getOpPackageName());
         Arrays.sort(accounts, new AccountSorter());
         assertEquals(3, accounts.length);
         assertEquals(a11, accounts[0]);
@@ -136,7 +210,8 @@
 
         mAms.removeAccountInternal(a21);
 
-        accounts = mAms.getAccounts("type1", mContext.getOpPackageName());
+        accounts = mAms.getAccounts(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                mContext.getOpPackageName());
         Arrays.sort(accounts, new AccountSorter());
         assertEquals(2, accounts.length);
         assertEquals(a11, accounts[0]);
@@ -146,8 +221,8 @@
     @SmallTest
     public void testPasswords() throws Exception {
         unlockSystemUser();
-        Account a11 = new Account("account1", "type1");
-        Account a12 = new Account("account1", "type2");
+        Account a11 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        Account a12 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2);
         mAms.addAccountExplicitly(a11, "p11", null);
         mAms.addAccountExplicitly(a12, "p12", null);
 
@@ -163,12 +238,12 @@
     @SmallTest
     public void testUserdata() throws Exception {
         unlockSystemUser();
-        Account a11 = new Account("account1", "type1");
+        Account a11 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
         Bundle u11 = new Bundle();
         u11.putString("a", "a_a11");
         u11.putString("b", "b_a11");
         u11.putString("c", "c_a11");
-        Account a12 = new Account("account1", "type2");
+        Account a12 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2);
         Bundle u12 = new Bundle();
         u12.putString("a", "a_a12");
         u12.putString("b", "b_a12");
@@ -197,8 +272,8 @@
     @SmallTest
     public void testAuthtokens() throws Exception {
         unlockSystemUser();
-        Account a11 = new Account("account1", "type1");
-        Account a12 = new Account("account1", "type2");
+        Account a11 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        Account a12 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2);
         mAms.addAccountExplicitly(a11, "p11", null);
         mAms.addAccountExplicitly(a12, "p12", null);
 
@@ -232,8 +307,8 @@
     @SmallTest
     public void testRemovedAccountSync() throws Exception {
         unlockSystemUser();
-        Account a1 = new Account("account1", "type1");
-        Account a2 = new Account("account2", "type2");
+        Account a1 = new Account("account1", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        Account a2 = new Account("account2", AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2);
         mAms.addAccountExplicitly(a1, "p1", null);
         mAms.addAccountExplicitly(a2, "p2", null);
 
@@ -292,6 +367,2171 @@
                 new File(ceDatabaseName).exists());
     }
 
+    @SmallTest
+    public void testStartAddAccountSessionWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.startAddAccountSession(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null); // optionsIn
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionWithNullAccountType() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.startAddAccountSession(
+                    mMockAccountManagerResponse, // response
+                    null, // accountType
+                    "authTokenType",
+                    null, // requiredFeatures
+                    true, // expectActivityLaunch
+                    null); // optionsIn
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionUserCannotModifyAccountNoDPM() throws Exception {
+        unlockSystemUser();
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, true);
+        when(mMockUserManager.getUserRestrictions(any(UserHandle.class))).thenReturn(bundle);
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+        mAms.startAddAccountSession(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null); // optionsIn
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_USER_RESTRICTED), anyString());
+        verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.SYSTEM));
+
+        // verify the intent for default CantAddAccountActivity is sent.
+        Intent intent = mIntentCaptor.getValue();
+        assertEquals(intent.getComponent().getClassName(), CantAddAccountActivity.class.getName());
+        assertEquals(intent.getIntExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, 0),
+                AccountManager.ERROR_CODE_USER_RESTRICTED);
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionUserCannotModifyAccountWithDPM() throws Exception {
+        unlockSystemUser();
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, true);
+        when(mMockUserManager.getUserRestrictions(any(UserHandle.class))).thenReturn(bundle);
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.addService(
+                DevicePolicyManagerInternal.class, mMockDevicePolicyManagerInternal);
+        when(mMockDevicePolicyManagerInternal.createUserRestrictionSupportIntent(
+                anyInt(), anyString())).thenReturn(new Intent());
+        when(mMockDevicePolicyManagerInternal.createShowAdminSupportIntent(
+                anyInt(), anyBoolean())).thenReturn(new Intent());
+
+        mAms.startAddAccountSession(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null); // optionsIn
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_USER_RESTRICTED), anyString());
+        verify(mMockContext).startActivityAsUser(any(Intent.class), eq(UserHandle.SYSTEM));
+        verify(mMockDevicePolicyManagerInternal).createUserRestrictionSupportIntent(
+                anyInt(), anyString());
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionUserCannotModifyAccountForTypeNoDPM() throws Exception {
+        unlockSystemUser();
+        when(mMockDevicePolicyManager.getAccountTypesWithManagementDisabledAsUser(anyInt()))
+                .thenReturn(new String[]{AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, "BBB"});
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+        mAms.startAddAccountSession(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null); // optionsIn
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE), anyString());
+        verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.SYSTEM));
+
+        // verify the intent for default CantAddAccountActivity is sent.
+        Intent intent = mIntentCaptor.getValue();
+        assertEquals(intent.getComponent().getClassName(), CantAddAccountActivity.class.getName());
+        assertEquals(intent.getIntExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, 0),
+                AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionUserCannotModifyAccountForTypeWithDPM() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+                mMockDevicePolicyManager);
+        when(mMockDevicePolicyManager.getAccountTypesWithManagementDisabledAsUser(anyInt()))
+                .thenReturn(new String[]{AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, "BBB"});
+
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.addService(
+                DevicePolicyManagerInternal.class, mMockDevicePolicyManagerInternal);
+        when(mMockDevicePolicyManagerInternal.createUserRestrictionSupportIntent(
+                anyInt(), anyString())).thenReturn(new Intent());
+        when(mMockDevicePolicyManagerInternal.createShowAdminSupportIntent(
+                anyInt(), anyBoolean())).thenReturn(new Intent());
+
+        mAms.startAddAccountSession(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null); // optionsIn
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE), anyString());
+        verify(mMockContext).startActivityAsUser(any(Intent.class), eq(UserHandle.SYSTEM));
+        verify(mMockDevicePolicyManagerInternal).createShowAdminSupportIntent(
+                anyInt(), anyBoolean());
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionSuccessWithoutPasswordForwarding() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        Bundle options = createOptionsWithAccountName(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
+        mAms.startAddAccountSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                false, // expectActivityLaunch
+                options); // optionsIn
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+        assertNotNull(sessionBundle);
+        // Assert that session bundle is encrypted and hence data not visible.
+        assertNull(sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1));
+        // Assert password is not returned
+        assertNull(result.getString(AccountManager.KEY_PASSWORD));
+        assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN,
+                result.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionSuccessWithPasswordForwarding() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+                PackageManager.PERMISSION_GRANTED);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        Bundle options = createOptionsWithAccountName(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
+        mAms.startAddAccountSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                false, // expectActivityLaunch
+                options); // optionsIn
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+        assertNotNull(sessionBundle);
+        // Assert that session bundle is encrypted and hence data not visible.
+        assertNull(sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1));
+        // Assert password is returned
+        assertEquals(result.getString(AccountManager.KEY_PASSWORD),
+                AccountManagerServiceTestFixtures.ACCOUNT_PASSWORD);
+        assertNull(result.getString(AccountManager.KEY_AUTHTOKEN));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN,
+                result.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionReturnWithInvalidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        Bundle options = createOptionsWithAccountName(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE);
+
+        mAms.startAddAccountSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                options); // optionsIn
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_REMOTE_EXCEPTION), anyString());
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionReturnWithValidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        Bundle options = createOptionsWithAccountName(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE);
+
+        mAms.startAddAccountSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                options); // optionsIn
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+        assertNotNull(intent);
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_RESULT));
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK));
+    }
+
+    @SmallTest
+    public void testStartAddAccountSessionError() throws Exception {
+        unlockSystemUser();
+        Bundle options = createOptionsWithAccountName(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_ERROR);
+        options.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_INVALID_RESPONSE);
+        options.putString(AccountManager.KEY_ERROR_MESSAGE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.startAddAccountSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                false, // expectActivityLaunch
+                options); // optionsIn
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testStartUpdateCredentialsSessionWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.startUpdateCredentialsSession(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                "authTokenType",
+                true, // expectActivityLaunch
+                null); // optionsIn
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testStartUpdateCredentialsSessionWithNullAccount() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.startUpdateCredentialsSession(
+                mMockAccountManagerResponse, // response
+                null,
+                "authTokenType",
+                true, // expectActivityLaunch
+                null); // optionsIn
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testStartUpdateCredentialsSessionSuccessWithoutPasswordForwarding()
+            throws Exception {
+        unlockSystemUser();
+        when(mMockContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        Bundle options = createOptionsWithAccountName(
+            AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
+        mAms.startUpdateCredentialsSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                "authTokenType",
+                false, // expectActivityLaunch
+                options); // optionsIn
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+        assertNotNull(sessionBundle);
+        // Assert that session bundle is encrypted and hence data not visible.
+        assertNull(sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1));
+        // Assert password is not returned
+        assertNull(result.getString(AccountManager.KEY_PASSWORD));
+        assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN,
+                result.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+    }
+
+    @SmallTest
+    public void testStartUpdateCredentialsSessionSuccessWithPasswordForwarding() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+                PackageManager.PERMISSION_GRANTED);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        Bundle options = createOptionsWithAccountName(
+            AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
+        mAms.startUpdateCredentialsSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                "authTokenType",
+                false, // expectActivityLaunch
+                options); // optionsIn
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+        assertNotNull(sessionBundle);
+        // Assert that session bundle is encrypted and hence data not visible.
+        assertNull(sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1));
+        // Assert password is returned
+        assertEquals(result.getString(AccountManager.KEY_PASSWORD),
+                AccountManagerServiceTestFixtures.ACCOUNT_PASSWORD);
+        assertNull(result.getString(AccountManager.KEY_AUTHTOKEN));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN,
+                result.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
+    }
+
+    @SmallTest
+    public void testStartUpdateCredentialsSessionReturnWithInvalidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        Bundle options = createOptionsWithAccountName(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE);
+
+        mAms.startUpdateCredentialsSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                "authTokenType",
+                true,  // expectActivityLaunch
+                options); // optionsIn
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_REMOTE_EXCEPTION), anyString());
+    }
+
+    @SmallTest
+    public void testStartUpdateCredentialsSessionReturnWithValidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        Bundle options = createOptionsWithAccountName(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE);
+
+        mAms.startUpdateCredentialsSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                "authTokenType",
+                true,  // expectActivityLaunch
+                options); // optionsIn
+
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+        assertNotNull(intent);
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_RESULT));
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK));
+    }
+
+    @SmallTest
+    public void testStartUpdateCredentialsSessionError() throws Exception {
+        unlockSystemUser();
+        Bundle options = createOptionsWithAccountName(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_ERROR);
+        options.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_INVALID_RESPONSE);
+        options.putString(AccountManager.KEY_ERROR_MESSAGE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.startUpdateCredentialsSession(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_ERROR,
+                "authTokenType",
+                true,  // expectActivityLaunch
+                options); // optionsIn
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.finishSessionAsUser(
+                null, // response
+                createEncryptedSessionBundle(
+                        AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS),
+                false, // expectActivityLaunch
+                createAppBundle(), // appInfo
+                UserHandle.USER_SYSTEM);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserWithNullSessionBundle() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.finishSessionAsUser(
+                mMockAccountManagerResponse, // response
+                null, // sessionBundle
+                false, // expectActivityLaunch
+                createAppBundle(), // appInfo
+                UserHandle.USER_SYSTEM);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserUserCannotModifyAccountNoDPM() throws Exception {
+        unlockSystemUser();
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, true);
+        when(mMockUserManager.getUserRestrictions(any(UserHandle.class))).thenReturn(bundle);
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+        mAms.finishSessionAsUser(
+            mMockAccountManagerResponse, // response
+            createEncryptedSessionBundle(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS),
+            false, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            2); // fake user id
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_USER_RESTRICTED), anyString());
+        verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.of(2)));
+
+        // verify the intent for default CantAddAccountActivity is sent.
+        Intent intent = mIntentCaptor.getValue();
+        assertEquals(intent.getComponent().getClassName(), CantAddAccountActivity.class.getName());
+        assertEquals(intent.getIntExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, 0),
+                AccountManager.ERROR_CODE_USER_RESTRICTED);
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserUserCannotModifyAccountWithDPM() throws Exception {
+        unlockSystemUser();
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, true);
+        when(mMockUserManager.getUserRestrictions(any(UserHandle.class))).thenReturn(bundle);
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.addService(
+                DevicePolicyManagerInternal.class, mMockDevicePolicyManagerInternal);
+        when(mMockDevicePolicyManagerInternal.createUserRestrictionSupportIntent(
+                anyInt(), anyString())).thenReturn(new Intent());
+        when(mMockDevicePolicyManagerInternal.createShowAdminSupportIntent(
+                anyInt(), anyBoolean())).thenReturn(new Intent());
+
+        mAms.finishSessionAsUser(
+            mMockAccountManagerResponse, // response
+            createEncryptedSessionBundle(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS),
+            false, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            2); // fake user id
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_USER_RESTRICTED), anyString());
+        verify(mMockContext).startActivityAsUser(any(Intent.class), eq(UserHandle.of(2)));
+        verify(mMockDevicePolicyManagerInternal).createUserRestrictionSupportIntent(
+                anyInt(), anyString());
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserWithBadSessionBundle() throws Exception {
+        unlockSystemUser();
+
+        Bundle badSessionBundle = new Bundle();
+        badSessionBundle.putString("any", "any");
+        mAms.finishSessionAsUser(
+            mMockAccountManagerResponse, // response
+            badSessionBundle, // sessionBundle
+            false, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            2); // fake user id
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_BAD_REQUEST), anyString());
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserWithBadAccountType() throws Exception {
+        unlockSystemUser();
+
+        mAms.finishSessionAsUser(
+            mMockAccountManagerResponse, // response
+            createEncryptedSessionBundleWithNoAccountType(
+                    AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS),
+            false, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            2); // fake user id
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_BAD_ARGUMENTS), anyString());
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserUserCannotModifyAccountForTypeNoDPM() throws Exception {
+        unlockSystemUser();
+        when(mMockDevicePolicyManager.getAccountTypesWithManagementDisabledAsUser(anyInt()))
+                .thenReturn(new String[]{AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, "BBB"});
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+        mAms.finishSessionAsUser(
+            mMockAccountManagerResponse, // response
+            createEncryptedSessionBundle(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS),
+            false, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            2); // fake user id
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE), anyString());
+        verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.of(2)));
+
+        // verify the intent for default CantAddAccountActivity is sent.
+        Intent intent = mIntentCaptor.getValue();
+        assertEquals(intent.getComponent().getClassName(), CantAddAccountActivity.class.getName());
+        assertEquals(intent.getIntExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, 0),
+                AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserUserCannotModifyAccountForTypeWithDPM() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+                mMockDevicePolicyManager);
+        when(mMockDevicePolicyManager.getAccountTypesWithManagementDisabledAsUser(anyInt()))
+                .thenReturn(new String[]{AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, "BBB"});
+
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.addService(
+                DevicePolicyManagerInternal.class, mMockDevicePolicyManagerInternal);
+        when(mMockDevicePolicyManagerInternal.createUserRestrictionSupportIntent(
+                anyInt(), anyString())).thenReturn(new Intent());
+        when(mMockDevicePolicyManagerInternal.createShowAdminSupportIntent(
+                anyInt(), anyBoolean())).thenReturn(new Intent());
+
+        mAms.finishSessionAsUser(
+            mMockAccountManagerResponse, // response
+            createEncryptedSessionBundle(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS),
+            false, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            2); // fake user id
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE), anyString());
+        verify(mMockContext).startActivityAsUser(any(Intent.class), eq(UserHandle.of(2)));
+        verify(mMockDevicePolicyManagerInternal).createShowAdminSupportIntent(
+                anyInt(), anyBoolean());
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserSuccess() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.finishSessionAsUser(
+            response, // response
+            createEncryptedSessionBundle(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS),
+            false, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+        // Verify notification is cancelled
+        verify(mMockNotificationManager).cancelNotificationWithTag(
+                anyString(), anyString(), anyInt(), anyInt());
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+        assertNotNull(sessionBundle);
+        // Assert that session bundle is decrypted and hence data is visible.
+        assertEquals(AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1,
+                sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1));
+        // Assert finishSessionAsUser added calling uid and pid into the sessionBundle
+        assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_UID));
+        assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_PID));
+        // Assert App bundle data overrides sessionBundle data
+        assertEquals(sessionBundle.getString(
+                AccountManager.KEY_ANDROID_PACKAGE_NAME), "APCT.package");
+
+        // Verify response data
+        assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_NAME,
+                result.getString(AccountManager.KEY_ACCOUNT_NAME));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserReturnWithInvalidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.finishSessionAsUser(
+            response, // response
+            createEncryptedSessionBundle(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE),
+            true, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_REMOTE_EXCEPTION), anyString());
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserReturnWithValidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.finishSessionAsUser(
+            response, // response
+            createEncryptedSessionBundle(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE),
+            true, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+        assertNotNull(intent);
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_RESULT));
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK));
+    }
+
+    @SmallTest
+    public void testFinishSessionAsUserError() throws Exception {
+        unlockSystemUser();
+        Bundle sessionBundle = createEncryptedSessionBundleWithError(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_ERROR);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.finishSessionAsUser(
+            response, // response
+            sessionBundle,
+            false, // expectActivityLaunch
+            createAppBundle(), // appInfo
+            UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testIsCredentialsUpdatedSuggestedWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.isCredentialsUpdateSuggested(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testIsCredentialsUpdatedSuggestedWithNullAccount() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.isCredentialsUpdateSuggested(
+                mMockAccountManagerResponse,
+                null, // account
+                AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testIsCredentialsUpdatedSuggestedWithEmptyStatusToken() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.isCredentialsUpdateSuggested(
+                mMockAccountManagerResponse,
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                null);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testIsCredentialsUpdatedSuggestedError() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.isCredentialsUpdateSuggested(
+            response,
+            AccountManagerServiceTestFixtures.ACCOUNT_ERROR,
+            AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN);
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testIsCredentialsUpdatedSuggestedSuccess() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.isCredentialsUpdateSuggested(
+            response,
+            AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+            AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN);
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        boolean needUpdate = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
+        assertTrue(needUpdate);
+    }
+
+    @SmallTest
+    public void testHasFeaturesWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.hasFeatures(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                new String[] {"feature1", "feature2"}, // features
+                "testPackage"); // opPackageName
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testHasFeaturesWithNullAccount() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.hasFeatures(
+                mMockAccountManagerResponse, // response
+                null, // account
+                new String[] {"feature1", "feature2"}, // features
+                "testPackage"); // opPackageName
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testHasFeaturesWithNullFeature() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.hasFeatures(
+                    mMockAccountManagerResponse, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, // account
+                    null, // features
+                    "testPackage"); // opPackageName
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testHasFeaturesReturnNullResult() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.hasFeatures(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_ERROR, // account
+                AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, // features
+                "testPackage"); // opPackageName
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), anyString());
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testHasFeaturesSuccess() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.hasFeatures(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, // account
+                AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, // features
+                "testPackage"); // opPackageName
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        boolean hasFeatures = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
+        assertTrue(hasFeatures);
+    }
+
+    @SmallTest
+    public void testRemoveAccountAsUserWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.removeAccountAsUser(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testRemoveAccountAsUserWithNullAccount() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.removeAccountAsUser(
+                mMockAccountManagerResponse, // response
+                null, // account
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testRemoveAccountAsUserAccountNotManagedByCaller() throws Exception {
+        unlockSystemUser();
+        when(mMockPackageManager.checkSignatures(anyInt(), anyInt()))
+                    .thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+        try {
+            mAms.removeAccountAsUser(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+            fail("SecurityException expected. But no exception was thrown.");
+        } catch (SecurityException e) {
+            // SecurityException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testRemoveAccountAsUserUserCannotModifyAccount() throws Exception {
+        unlockSystemUser();
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, true);
+        when(mMockUserManager.getUserRestrictions(any(UserHandle.class))).thenReturn(bundle);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.removeAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_USER_RESTRICTED), anyString());
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testRemoveAccountAsUserUserCannotModifyAccountType() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+                mMockDevicePolicyManager);
+        when(mMockDevicePolicyManager.getAccountTypesWithManagementDisabledAsUser(anyInt()))
+                .thenReturn(new String[]{AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, "BBB"});
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.removeAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE), anyString());
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testRemoveAccountAsUserRemovalAllowed() throws Exception {
+        unlockSystemUser();
+        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p1", null);
+        Account[] addedAccounts =
+                mAms.getAccounts(UserHandle.USER_SYSTEM, mContext.getOpPackageName());
+        assertEquals(1, addedAccounts.length);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.removeAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        boolean allowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
+        assertTrue(allowed);
+        Account[] accounts = mAms.getAccounts(UserHandle.USER_SYSTEM, mContext.getOpPackageName());
+        assertEquals(0, accounts.length);
+    }
+
+    @SmallTest
+    public void testRemoveAccountAsUserRemovalNotAllowed() throws Exception {
+        unlockSystemUser();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.removeAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_ERROR,
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        boolean allowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
+        assertFalse(allowed);
+    }
+
+    @SmallTest
+    public void testRemoveAccountAsUserReturnWithValidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.removeAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+        assertNotNull(intent);
+    }
+
+    @SmallTest
+    public void testGetAuthTokenLabelWithNullAccountType() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.getAuthTokenLabel(
+                mMockAccountManagerResponse, // response
+                null, // accountType
+                "authTokenType");
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testGetAuthTokenLabelWithNullAuthTokenType() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.getAuthTokenLabel(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                null); // authTokenType
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testGetAuthTokenWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.getAuthToken(
+                    null, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                    "authTokenType", // authTokenType
+                    true, // notifyOnAuthFailure
+                    true, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testGetAuthTokenWithNullAccount() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAuthToken(
+                    response, // response
+                    null, // account
+                    "authTokenType", // authTokenType
+                    true, // notifyOnAuthFailure
+                    true, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_BAD_ARGUMENTS), anyString());
+    }
+
+    @SmallTest
+    public void testGetAuthTokenWithNullAuthTokenType() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAuthToken(
+                    response, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                    null, // authTokenType
+                    true, // notifyOnAuthFailure
+                    true, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_BAD_ARGUMENTS), anyString());
+    }
+
+    @SmallTest
+    public void testGetAuthTokenWithInvalidPackage() throws Exception {
+        unlockSystemUser();
+        String[] list = new String[]{"test"};
+        when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
+        try {
+            mAms.getAuthToken(
+                    mMockAccountManagerResponse, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                    "authTokenType", // authTokenType
+                    true, // notifyOnAuthFailure
+                    true, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+            fail("SecurityException expected. But no exception was thrown.");
+        } catch (SecurityException e) {
+            // SecurityException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testGetAuthTokenFromInternal() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.createPackageContextAsUser(
+                 anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
+        when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
+        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
+
+        mAms.setAuthToken(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                "authTokenType", AccountManagerServiceTestFixtures.AUTH_TOKEN);
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAuthToken(
+                    response, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                    "authTokenType", // authTokenType
+                    true, // notifyOnAuthFailure
+                    true, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        assertEquals(result.getString(AccountManager.KEY_AUTHTOKEN),
+                AccountManagerServiceTestFixtures.AUTH_TOKEN);
+        assertEquals(result.getString(AccountManager.KEY_ACCOUNT_NAME),
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
+        assertEquals(result.getString(AccountManager.KEY_ACCOUNT_TYPE),
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+    }
+
+    @SmallTest
+    public void testGetAuthTokenSuccess() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.createPackageContextAsUser(
+                 anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
+        when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAuthToken(
+                    response, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                    "authTokenType", // authTokenType
+                    true, // notifyOnAuthFailure
+                    false, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        assertEquals(result.getString(AccountManager.KEY_AUTHTOKEN),
+                AccountManagerServiceTestFixtures.AUTH_TOKEN);
+        assertEquals(result.getString(AccountManager.KEY_ACCOUNT_NAME),
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
+        assertEquals(result.getString(AccountManager.KEY_ACCOUNT_TYPE),
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+    }
+
+    @SmallTest
+    public void testGetAuthTokenReturnWithInvalidIntent() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.createPackageContextAsUser(
+                 anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
+        when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAuthToken(
+                    response, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                    "authTokenType", // authTokenType
+                    true, // notifyOnAuthFailure
+                    false, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_REMOTE_EXCEPTION), anyString());
+    }
+
+    @SmallTest
+    public void testGetAuthTokenReturnWithValidIntent() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.createPackageContextAsUser(
+                 anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
+        when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
+
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAuthToken(
+                    response, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                    "authTokenType", // authTokenType
+                    false, // notifyOnAuthFailure
+                    true, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+        assertNotNull(intent);
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_RESULT));
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK));
+    }
+
+    @SmallTest
+    public void testGetAuthTokenError() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.createPackageContextAsUser(
+                 anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE};
+        when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list);
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAuthToken(
+                    response, // response
+                    AccountManagerServiceTestFixtures.ACCOUNT_ERROR,
+                    "authTokenType", // authTokenType
+                    true, // notifyOnAuthFailure
+                    false, // expectActivityLaunch
+                    createGetAuthTokenOptions());
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.addAccountAsUser(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null, // optionsIn
+                UserHandle.USER_SYSTEM);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserWithNullAccountType() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.addAccountAsUser(
+                mMockAccountManagerResponse, // response
+                null, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null, // optionsIn
+                UserHandle.USER_SYSTEM);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserUserCannotModifyAccountNoDPM() throws Exception {
+        unlockSystemUser();
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, true);
+        when(mMockUserManager.getUserRestrictions(any(UserHandle.class))).thenReturn(bundle);
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+        mAms.addAccountAsUser(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null, // optionsIn
+                UserHandle.USER_SYSTEM);
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_USER_RESTRICTED), anyString());
+        verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.SYSTEM));
+
+        // verify the intent for default CantAddAccountActivity is sent.
+        Intent intent = mIntentCaptor.getValue();
+        assertEquals(intent.getComponent().getClassName(), CantAddAccountActivity.class.getName());
+        assertEquals(intent.getIntExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, 0),
+                AccountManager.ERROR_CODE_USER_RESTRICTED);
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserUserCannotModifyAccountWithDPM() throws Exception {
+        unlockSystemUser();
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, true);
+        when(mMockUserManager.getUserRestrictions(any(UserHandle.class))).thenReturn(bundle);
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.addService(
+                DevicePolicyManagerInternal.class, mMockDevicePolicyManagerInternal);
+        when(mMockDevicePolicyManagerInternal.createUserRestrictionSupportIntent(
+                anyInt(), anyString())).thenReturn(new Intent());
+        when(mMockDevicePolicyManagerInternal.createShowAdminSupportIntent(
+                anyInt(), anyBoolean())).thenReturn(new Intent());
+
+        mAms.addAccountAsUser(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null, // optionsIn
+                UserHandle.USER_SYSTEM);
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_USER_RESTRICTED), anyString());
+        verify(mMockContext).startActivityAsUser(any(Intent.class), eq(UserHandle.SYSTEM));
+        verify(mMockDevicePolicyManagerInternal).createUserRestrictionSupportIntent(
+                anyInt(), anyString());
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserUserCannotModifyAccountForTypeNoDPM() throws Exception {
+        unlockSystemUser();
+        when(mMockDevicePolicyManager.getAccountTypesWithManagementDisabledAsUser(anyInt()))
+                .thenReturn(new String[]{AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, "BBB"});
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+        mAms.addAccountAsUser(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null, // optionsIn
+                UserHandle.USER_SYSTEM);
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE), anyString());
+        verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.SYSTEM));
+
+        // verify the intent for default CantAddAccountActivity is sent.
+        Intent intent = mIntentCaptor.getValue();
+        assertEquals(intent.getComponent().getClassName(), CantAddAccountActivity.class.getName());
+        assertEquals(intent.getIntExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, 0),
+                AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserUserCannotModifyAccountForTypeWithDPM() throws Exception {
+        unlockSystemUser();
+        when(mMockContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+                mMockDevicePolicyManager);
+        when(mMockDevicePolicyManager.getAccountTypesWithManagementDisabledAsUser(anyInt()))
+                .thenReturn(new String[]{AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, "BBB"});
+
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.addService(
+                DevicePolicyManagerInternal.class, mMockDevicePolicyManagerInternal);
+        when(mMockDevicePolicyManagerInternal.createUserRestrictionSupportIntent(
+                anyInt(), anyString())).thenReturn(new Intent());
+        when(mMockDevicePolicyManagerInternal.createShowAdminSupportIntent(
+                anyInt(), anyBoolean())).thenReturn(new Intent());
+
+        mAms.addAccountAsUser(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                null, // optionsIn
+                UserHandle.USER_SYSTEM);
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE), anyString());
+        verify(mMockContext).startActivityAsUser(any(Intent.class), eq(UserHandle.SYSTEM));
+        verify(mMockDevicePolicyManagerInternal).createShowAdminSupportIntent(
+                anyInt(), anyBoolean());
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserSuccess() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.addAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                createAddAccountOptions(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS),
+                UserHandle.USER_SYSTEM);
+        waitForLatch(latch);
+        // Verify notification is cancelled
+        verify(mMockNotificationManager).cancelNotificationWithTag(
+                anyString(), anyString(), anyInt(), anyInt());
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        // Verify response data
+        assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS,
+                result.getString(AccountManager.KEY_ACCOUNT_NAME));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+
+        Bundle optionBundle = result.getParcelable(
+                AccountManagerServiceTestFixtures.KEY_OPTIONS_BUNDLE);
+        // Assert addAccountAsUser added calling uid and pid into the option bundle
+        assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_UID));
+        assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_PID));
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserReturnWithInvalidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.addAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                createAddAccountOptions(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE),
+                UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_REMOTE_EXCEPTION), anyString());
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserReturnWithValidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.addAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                createAddAccountOptions(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE),
+                UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+        assertNotNull(intent);
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_RESULT));
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK));
+    }
+
+    @SmallTest
+    public void testAddAccountAsUserError() throws Exception {
+        unlockSystemUser();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.addAccountAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                "authTokenType",
+                null, // requiredFeatures
+                true, // expectActivityLaunch
+                createAddAccountOptions(AccountManagerServiceTestFixtures.ACCOUNT_NAME_ERROR),
+                UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testConfirmCredentialsAsUserWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.confirmCredentialsAsUser(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                new Bundle(), // options
+                false, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testConfirmCredentialsAsUserWithNullAccount() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.confirmCredentialsAsUser(
+                mMockAccountManagerResponse, // response
+                null, // account
+                new Bundle(), // options
+                false, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testConfirmCredentialsAsUserSuccess() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.confirmCredentialsAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                new Bundle(), // options
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        // Verify response data
+        assertTrue(result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS,
+                result.getString(AccountManager.KEY_ACCOUNT_NAME));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+    }
+
+    @SmallTest
+    public void testConfirmCredentialsAsUserReturnWithInvalidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.confirmCredentialsAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                new Bundle(), // options
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_REMOTE_EXCEPTION), anyString());
+    }
+
+    @SmallTest
+    public void testConfirmCredentialsAsUserReturnWithValidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.confirmCredentialsAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                new Bundle(), // options
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+        assertNotNull(intent);
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_RESULT));
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK));
+    }
+
+    @SmallTest
+    public void testConfirmCredentialsAsUserError() throws Exception {
+        unlockSystemUser();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.confirmCredentialsAsUser(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_ERROR,
+                new Bundle(), // options
+                true, // expectActivityLaunch
+                UserHandle.USER_SYSTEM);
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testUpdateCredentialsWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.updateCredentials(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                "authTokenType",
+                false, // expectActivityLaunch
+                new Bundle()); // options
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testUpdateCredentialsWithNullAccount() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.updateCredentials(
+                mMockAccountManagerResponse, // response
+                null, // account
+                "authTokenType",
+                false, // expectActivityLaunch
+                new Bundle()); // options
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testUpdateCredentialsSuccess() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.updateCredentials(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS,
+                "authTokenType",
+                false, // expectActivityLaunch
+                new Bundle()); // options
+
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        // Verify response data
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS,
+                result.getString(AccountManager.KEY_ACCOUNT_NAME));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+    }
+
+    @SmallTest
+    public void testUpdateCredentialsReturnWithInvalidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.updateCredentials(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                "authTokenType",
+                true, // expectActivityLaunch
+                new Bundle()); // options
+
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_REMOTE_EXCEPTION), anyString());
+    }
+
+    @SmallTest
+    public void testUpdateCredentialsReturnWithValidIntent() throws Exception {
+        unlockSystemUser();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+        when(mMockPackageManager.resolveActivityAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+        when(mMockPackageManager.checkSignatures(
+                anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.updateCredentials(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE,
+                "authTokenType",
+                true, // expectActivityLaunch
+                new Bundle()); // options
+
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
+        assertNotNull(intent);
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_RESULT));
+        assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK));
+    }
+
+    @SmallTest
+    public void testUpdateCredentialsError() throws Exception {
+        unlockSystemUser();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.updateCredentials(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_ERROR,
+                "authTokenType",
+                false, // expectActivityLaunch
+                new Bundle()); // options
+
+        waitForLatch(latch);
+        verify(mMockAccountManagerResponse).onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    @SmallTest
+    public void testEditPropertiesWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.editProperties(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                false); // expectActivityLaunch
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testEditPropertiesWithNullAccountType() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.editProperties(
+                mMockAccountManagerResponse, // response
+                null, // accountType
+                false); // expectActivityLaunch
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testEditPropertiesAccountNotManagedByCaller() throws Exception {
+        unlockSystemUser();
+        when(mMockPackageManager.checkSignatures(anyInt(), anyInt()))
+                    .thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+        try {
+            mAms.editProperties(
+                mMockAccountManagerResponse, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                false); // expectActivityLaunch
+            fail("SecurityException expected. But no exception was thrown.");
+        } catch (SecurityException e) {
+            // SecurityException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testEditPropertiesSuccess() throws Exception {
+        unlockSystemUser();
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+
+        mAms.editProperties(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                false); // expectActivityLaunch
+
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        // Verify response data
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS,
+                result.getString(AccountManager.KEY_ACCOUNT_NAME));
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                result.getString(AccountManager.KEY_ACCOUNT_TYPE));
+    }
+
+    @SmallTest
+    public void testGetAccountsByFeaturesWithNullResponse() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.getAccountsByFeatures(
+                null, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
+                AccountManagerServiceTestFixtures.ACCOUNT_FEATURES,
+                "testpackage"); // opPackageName
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testGetAccountsByFeaturesWithNullAccountType() throws Exception {
+        unlockSystemUser();
+        try {
+            mAms.getAccountsByFeatures(
+                mMockAccountManagerResponse, // response
+                null, // accountType
+                AccountManagerServiceTestFixtures.ACCOUNT_FEATURES,
+                "testpackage"); // opPackageName
+            fail("IllegalArgumentException expected. But no exception was thrown.");
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException is expected.
+        }
+    }
+
+    @SmallTest
+    public void testGetAccountsByFeaturesAccountNotVisible() throws Exception {
+        unlockSystemUser();
+
+        when(mMockContext.checkCallingOrSelfPermission(anyString())).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+        when(mMockPackageManager.checkSignatures(anyInt(), anyInt()))
+                    .thenReturn(PackageManager.SIGNATURE_NO_MATCH);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAccountsByFeatures(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                AccountManagerServiceTestFixtures.ACCOUNT_FEATURES,
+                "testpackage"); // opPackageName
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Account[] accounts = (Account[]) result.getParcelableArray(AccountManager.KEY_ACCOUNTS);
+        assertTrue(accounts.length == 0);
+    }
+
+    @SmallTest
+    public void testGetAccountsByFeaturesNullFeatureReturnsAllAccounts() throws Exception {
+        unlockSystemUser();
+
+        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
+        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p12", null);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAccountsByFeatures(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                null, // features
+                "testpackage"); // opPackageName
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Account[] accounts = (Account[]) result.getParcelableArray(AccountManager.KEY_ACCOUNTS);
+        Arrays.sort(accounts, new AccountSorter());
+        assertEquals(2, accounts.length);
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, accounts[0]);
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, accounts[1]);
+    }
+
+    @SmallTest
+    public void testGetAccountsByFeaturesReturnsAccountsWithFeaturesOnly() throws Exception {
+        unlockSystemUser();
+
+        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
+        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p12", null);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAccountsByFeatures(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                AccountManagerServiceTestFixtures.ACCOUNT_FEATURES,
+                "testpackage"); // opPackageName
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
+        Bundle result = mBundleCaptor.getValue();
+        Account[] accounts = (Account[]) result.getParcelableArray(AccountManager.KEY_ACCOUNTS);
+        assertEquals(1, accounts.length);
+        assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, accounts[0]);
+    }
+
+    @SmallTest
+    public void testGetAccountsByFeaturesError() throws Exception {
+        unlockSystemUser();
+
+        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
+        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_ERROR, "p12", null);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        Response response = new Response(latch, mMockAccountManagerResponse);
+        mAms.getAccountsByFeatures(
+                response, // response
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+                AccountManagerServiceTestFixtures.ACCOUNT_FEATURES,
+                "testpackage"); // opPackageName
+        waitForLatch(latch);
+
+        verify(mMockAccountManagerResponse).onError(
+                eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), anyString());
+        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
+    }
+
+    private void waitForLatch(CountDownLatch latch) {
+        try {
+            latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            throw new IllegalStateException("Should not throw an InterruptedException", e);
+        }
+    }
+
+    private Bundle createAddAccountOptions(String accountName) {
+        Bundle options = new Bundle();
+        options.putString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME, accountName);
+        return options;
+    }
+
+    private Bundle createGetAuthTokenOptions() {
+        Bundle options = new Bundle();
+        options.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME,
+                AccountManagerServiceTestFixtures.CALLER_PACKAGE);
+        options.putLong(AccountManagerServiceTestFixtures.KEY_TOKEN_EXPIRY,
+                System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND);
+        return options;
+    }
+
+    private Bundle encryptBundleWithCryptoHelper(Bundle sessionBundle) {
+        Bundle encryptedBundle = null;
+        try {
+            CryptoHelper cryptoHelper = CryptoHelper.getInstance();
+            encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
+        } catch (GeneralSecurityException e) {
+            throw new IllegalStateException("Failed to encrypt session bundle.", e);
+        }
+        return encryptedBundle;
+    }
+
+    private Bundle createEncryptedSessionBundle(final String accountName) {
+        Bundle sessionBundle = new Bundle();
+        sessionBundle.putString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME, accountName);
+        sessionBundle.putString(
+                AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1,
+                AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1);
+        sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE,
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        sessionBundle.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME, "APCT.session.package");
+        return encryptBundleWithCryptoHelper(sessionBundle);
+    }
+
+    private Bundle createEncryptedSessionBundleWithError(final String accountName) {
+        Bundle sessionBundle = new Bundle();
+        sessionBundle.putString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME, accountName);
+        sessionBundle.putString(
+                AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1,
+                AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1);
+        sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE,
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        sessionBundle.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME, "APCT.session.package");
+        sessionBundle.putInt(
+                AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_INVALID_RESPONSE);
+        sessionBundle.putString(AccountManager.KEY_ERROR_MESSAGE,
+                AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        return encryptBundleWithCryptoHelper(sessionBundle);
+    }
+
+    private Bundle createEncryptedSessionBundleWithNoAccountType(final String accountName) {
+        Bundle sessionBundle = new Bundle();
+        sessionBundle.putString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME, accountName);
+        sessionBundle.putString(
+                AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1,
+                AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1);
+        sessionBundle.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME, "APCT.session.package");
+        return encryptBundleWithCryptoHelper(sessionBundle);
+    }
+
+    private Bundle createAppBundle() {
+        Bundle appBundle = new Bundle();
+        appBundle.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME, "APCT.package");
+        return appBundle;
+    }
+
+    private Bundle createOptionsWithAccountName(final String accountName) {
+        Bundle sessionBundle = new Bundle();
+        sessionBundle.putString(
+                AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1,
+                AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1);
+        sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE,
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        Bundle options = new Bundle();
+        options.putString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME, accountName);
+        options.putBundle(AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE,
+                sessionBundle);
+        options.putString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD,
+                AccountManagerServiceTestFixtures.ACCOUNT_PASSWORD);
+        return options;
+    }
+
     private int readNumberOfAccountsFromDbFile(Context context, String dbName) {
         SQLiteDatabase ceDb = context.openOrCreateDatabase(dbName, 0, null);
         try (Cursor cursor = ceDb.rawQuery("SELECT count(*) FROM accounts", null)) {
@@ -310,78 +2550,34 @@
         return intent;
     }
 
-    static class MockAccountAuthenticatorCache implements IAccountAuthenticatorCache {
-        private ArrayList<ServiceInfo<AuthenticatorDescription>> mServices;
-
-        MockAccountAuthenticatorCache() {
-            mServices = new ArrayList<>();
-            AuthenticatorDescription d1 = new AuthenticatorDescription("type1", "p1", 0, 0, 0, 0);
-            AuthenticatorDescription d2 = new AuthenticatorDescription("type2", "p2", 0, 0, 0, 0);
-            mServices.add(new ServiceInfo<>(d1, null, null));
-            mServices.add(new ServiceInfo<>(d2, null, null));
-        }
-
-        @Override
-        public ServiceInfo<AuthenticatorDescription> getServiceInfo(
-                AuthenticatorDescription type, int userId) {
-            for (ServiceInfo<AuthenticatorDescription> service : mServices) {
-                if (service.type.equals(type)) {
-                    return service;
-                }
-            }
-            return null;
-        }
-
-        @Override
-        public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices(int userId) {
-            return mServices;
-        }
-
-        @Override
-        public void dump(
-                final FileDescriptor fd, final PrintWriter fout, final String[] args, int userId) {
-        }
-
-        @Override
-        public void setListener(
-                final RegisteredServicesCacheListener<AuthenticatorDescription> listener,
-                final Handler handler) {
-        }
-
-        @Override
-        public void invalidateCache(int userId) {
-        }
-
-        @Override
-        public void updateServices(int userId) {
-        }
-    }
-
     static class MyMockContext extends MockContext {
         private Context mTestContext;
-        private AppOpsManager mAppOpsManager;
-        private UserManager mUserManager;
-        private PackageManager mPackageManager;
+        private Context mMockContext;
 
-        MyMockContext(Context testContext) {
+        MyMockContext(Context testContext, Context mockContext) {
             this.mTestContext = testContext;
-            this.mAppOpsManager = mock(AppOpsManager.class);
-            this.mUserManager = mock(UserManager.class);
-            this.mPackageManager = mock(PackageManager.class);
-            when(mPackageManager.checkSignatures(anyInt(), anyInt()))
-                    .thenReturn(PackageManager.SIGNATURE_MATCH);
-            final UserInfo ui = new UserInfo(UserHandle.USER_SYSTEM, "user0", 0);
-            when(mUserManager.getUserInfo(eq(ui.id))).thenReturn(ui);
+            this.mMockContext = mockContext;
         }
 
         @Override
         public int checkCallingOrSelfPermission(final String permission) {
-            return PackageManager.PERMISSION_GRANTED;
+            return mMockContext.checkCallingOrSelfPermission(permission);
+        }
+
+        @Override
+        public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+                UserHandle user) {
+            return mTestContext.bindServiceAsUser(service, conn, flags, user);
+        }
+
+        @Override
+        public void unbindService(ServiceConnection conn) {
+            mTestContext.unbindService(conn);
         }
 
         @Override
         public PackageManager getPackageManager() {
-            return mPackageManager;
+            return mMockContext.getPackageManager();
         }
 
         @Override
@@ -391,56 +2587,74 @@
 
         @Override
         public Object getSystemService(String name) {
-            if (Context.APP_OPS_SERVICE.equals(name)) {
-                return mAppOpsManager;
-            } else if( Context.USER_SERVICE.equals(name)) {
-                return mUserManager;
-            }
-            return null;
+            return mMockContext.getSystemService(name);
         }
 
         @Override
         public String getSystemServiceName(Class<?> serviceClass) {
-            if (AppOpsManager.class.equals(serviceClass)) {
-                return Context.APP_OPS_SERVICE;
-            }
-            return null;
+            return mMockContext.getSystemServiceName(serviceClass);
+        }
+
+        @Override
+        public void startActivityAsUser(Intent intent, UserHandle user) {
+            mMockContext.startActivityAsUser(intent, user);
         }
 
         @Override
         public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-            return null;
+            return mMockContext.registerReceiver(receiver, filter);
         }
 
         @Override
         public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
                 IntentFilter filter, String broadcastPermission, Handler scheduler) {
-            return null;
+            return mMockContext.registerReceiverAsUser(
+                    receiver, user, filter, broadcastPermission, scheduler);
         }
 
         @Override
         public SQLiteDatabase openOrCreateDatabase(String file, int mode,
                 SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
-            Log.i(TAG, "openOrCreateDatabase " + file + " mode " + mode);
             return mTestContext.openOrCreateDatabase(file, mode, factory,errorHandler);
         }
 
         @Override
         public void sendBroadcastAsUser(Intent intent, UserHandle user) {
-            Log.i(TAG, "sendBroadcastAsUser " + intent + " " + user);
+            mMockContext.sendBroadcastAsUser(intent, user);
         }
 
         @Override
         public String getOpPackageName() {
-            return null;
+            return mMockContext.getOpPackageName();
+        }
+
+        @Override
+        public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
+                throws PackageManager.NameNotFoundException {
+            return mMockContext.createPackageContextAsUser(packageName, flags, user);
+        }
+    }
+
+    static class TestAccountAuthenticatorCache extends AccountAuthenticatorCache {
+        public TestAccountAuthenticatorCache(Context realContext) {
+            super(realContext);
+        }
+
+        @Override
+        protected File getUserSystemDirectory(int userId) {
+            return new File(mContext.getCacheDir(), "authenticator");
         }
     }
 
     static class TestInjector extends AccountManagerService.Injector {
         private Context mRealContext;
-        TestInjector(Context realContext, MyMockContext mockContext) {
+        private INotificationManager mMockNotificationManager;
+        TestInjector(Context realContext,
+                Context mockContext,
+                INotificationManager mockNotificationManager) {
             super(mockContext);
             mRealContext = realContext;
+            mMockNotificationManager = mockNotificationManager;
         }
 
         @Override
@@ -454,7 +2668,7 @@
 
         @Override
         IAccountAuthenticatorCache getAccountAuthenticatorCache() {
-            return new MockAccountAuthenticatorCache();
+            return new TestAccountAuthenticatorCache(mRealContext);
         }
 
         @Override
@@ -474,7 +2688,34 @@
 
         @Override
         INotificationManager getNotificationManager() {
-            return mock(INotificationManager.class);
+            return mMockNotificationManager;
+        }
+    }
+
+    class Response extends IAccountManagerResponse.Stub {
+        private CountDownLatch mLatch;
+        private IAccountManagerResponse mMockResponse;
+        public Response(CountDownLatch latch, IAccountManagerResponse mockResponse) {
+            mLatch = latch;
+            mMockResponse = mockResponse;
+        }
+
+        @Override
+        public void onResult(Bundle bundle) {
+            try {
+                mMockResponse.onResult(bundle);
+            } catch (RemoteException e) {
+            }
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onError(int code, String message) {
+            try {
+                mMockResponse.onError(code, message);
+            } catch (RemoteException e) {
+            }
+            mLatch.countDown();
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java
new file mode 100644
index 0000000..614680e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.accounts;
+
+import android.accounts.Account;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Constants shared between test AccountAuthenticators and AccountManagerServiceTest.
+ */
+public final class AccountManagerServiceTestFixtures {
+    public static final String KEY_ACCOUNT_NAME = "account_manager_service_test:account_name_key";
+    public static final String KEY_ACCOUNT_SESSION_BUNDLE =
+            "account_manager_service_test:account_session_bundle_key";
+    public static final String KEY_ACCOUNT_STATUS_TOKEN =
+            "account_manager_service_test:account_status_token_key";
+    public static final String KEY_ACCOUNT_PASSWORD =
+            "account_manager_service_test:account_password_key";
+    public static final String KEY_OPTIONS_BUNDLE =
+            "account_manager_service_test:option_bundle_key";
+    public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com";
+    public static final String ACCOUNT_NAME_INTERVENE = "intervene@fixture.com";
+    public static final String ACCOUNT_NAME_ERROR = "error@fixture.com";
+
+    public static final String ACCOUNT_NAME =
+            "com.android.server.accounts.account_manager_service_test.account.name";
+    public static final String ACCOUNT_TYPE_1 =
+            "com.android.server.accounts.account_manager_service_test.account.type1";
+    public static final String ACCOUNT_TYPE_2 =
+            "com.android.server.accounts.account_manager_service_test.account.type2";
+    public static final String ACCOUNT_FAKE_TYPE =
+            "com.android.server.accounts.account_manager_service_test.account.type.fake";
+
+    public static final String ACCOUNT_STATUS_TOKEN =
+            "com.android.server.accounts.account_manager_service_test.account.status.token";
+    public static final String AUTH_TOKEN_LABEL =
+            "com.android.server.accounts.account_manager_service_test.auth.token.label";
+    public static final String AUTH_TOKEN =
+            "com.android.server.accounts.account_manager_service_test.auth.token";
+    public static final String KEY_TOKEN_EXPIRY =
+            "com.android.server.accounts.account_manager_service_test.auth.token.expiry";
+    public static final String ACCOUNT_FEATURE1 =
+            "com.android.server.accounts.account_manager_service_test.feature1";
+    public static final String ACCOUNT_FEATURE2 =
+            "com.android.server.accounts.account_manager_service_test.feature2";
+    public static final String[] ACCOUNT_FEATURES =
+            new String[]{ACCOUNT_FEATURE1, ACCOUNT_FEATURE2};
+    public static final String CALLER_PACKAGE =
+            "com.android.server.accounts.account_manager_service_test.caller.package";
+    public static final String ACCOUNT_PASSWORD =
+            "com.android.server.accounts.account_manager_service_test.account.password";
+    public static final String KEY_RESULT = "account_manager_service_test:result";
+    public static final String KEY_CALLBACK = "account_manager_service_test:callback";
+
+    public static final Account ACCOUNT_SUCCESS =
+            new Account(ACCOUNT_NAME_SUCCESS, ACCOUNT_TYPE_1);
+    public static final Account ACCOUNT_INTERVENE =
+            new Account(ACCOUNT_NAME_INTERVENE, ACCOUNT_TYPE_1);
+    public static final Account ACCOUNT_ERROR =
+            new Account(ACCOUNT_NAME_ERROR, ACCOUNT_TYPE_1);
+
+    public static final String SESSION_DATA_NAME_1 = "session.data.name.1";
+    public static final String SESSION_DATA_VALUE_1 = "session.data.value.1";
+
+    public static final String ERROR_MESSAGE =
+        "com.android.server.accounts.account_manager_service_test.error.message";
+
+    private AccountManagerServiceTestFixtures() {}
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index 8591dae..2e045ff 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -374,7 +374,7 @@
         mAccountsDb.setAccountVisibility(accId, uid2, 3);
         assertEquals(mAccountsDb.findAccountVisibility(accId, uid2), Integer.valueOf(3));
 
-        Map<Integer, Integer> vis = mAccountsDb.findAccountVisibilityForAccountId(accId);
+        Map<Integer, Integer> vis = mAccountsDb.findAllVisibilityValuesForAccount(account);
         assertEquals(vis.size(), 2);
         assertEquals(vis.get(uid1), Integer.valueOf(1));
         assertEquals(vis.get(uid2), Integer.valueOf(3));
diff --git a/services/tests/servicestests/src/com/android/server/accounts/PreNTestDatabaseHelper.java b/services/tests/servicestests/src/com/android/server/accounts/PreNTestDatabaseHelper.java
index 97adbe6..38b3735 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/PreNTestDatabaseHelper.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/PreNTestDatabaseHelper.java
@@ -26,7 +26,7 @@
 class PreNTestDatabaseHelper extends SQLiteOpenHelper {
 
     public static final String TOKEN_STRING = "token-string-123";
-    public static final String ACCOUNT_TYPE = "type1";
+    public static final String ACCOUNT_TYPE = AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2;
     public static final String ACCOUNT_NAME = "account@" + ACCOUNT_TYPE;
     public static final String ACCOUNT_PASSWORD = "Password";
     public static final String TOKEN_TYPE = "SID";
diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java
new file mode 100644
index 0000000..eb839a2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.accounts;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.frameworks.servicestests.R;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This authenticator is to mock account authenticator to test AccountManagerService.
+ */
+public class TestAccountType1Authenticator extends AbstractAccountAuthenticator {
+    private final AtomicInteger mTokenCounter  = new AtomicInteger(0);
+
+    private final String mAccountType;
+    private final Context mContext;
+
+    public TestAccountType1Authenticator(Context context, String accountType) {
+        super(context);
+        mAccountType = accountType;
+        mContext = context;
+    }
+
+    @Override
+    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+        Bundle result = new Bundle();
+        result.putString(AccountManager.KEY_ACCOUNT_NAME,
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS);
+        result.putString(AccountManager.KEY_ACCOUNT_TYPE,
+                AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        result.putString(
+                AccountManager.KEY_AUTHTOKEN,
+                Integer.toString(mTokenCounter.incrementAndGet()));
+        return result;
+    }
+
+    @Override
+    public Bundle addAccount(
+            AccountAuthenticatorResponse response,
+            String accountType,
+            String authTokenType,
+            String[] requiredFeatures,
+            Bundle options) throws NetworkErrorException {
+        if (!mAccountType.equals(accountType)) {
+            throw new IllegalArgumentException("Request to the wrong authenticator!");
+        }
+        String accountName = null;
+
+        if (options != null) {
+            accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME);
+        }
+
+        Bundle result = new Bundle();
+        if (accountName.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // fill bundle with a success result.
+            result.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
+            result.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
+            result.putString(AccountManager.KEY_AUTHTOKEN,
+                    Integer.toString(mTokenCounter.incrementAndGet()));
+            result.putParcelable(AccountManagerServiceTestFixtures.KEY_OPTIONS_BUNDLE, options);
+        } else if (accountName.equals(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            // Specify data to be returned by the eventual activity.
+            Intent eventualActivityResultData = new Intent();
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_NAME, accountName);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType);
+            // Fill result with Intent.
+            Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response);
+
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+        } else {
+            fillResultWithError(
+                    result,
+                    AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                    AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        }
+        return result;
+    }
+
+    @Override
+    public Bundle confirmCredentials(
+            AccountAuthenticatorResponse response,
+            Account account,
+            Bundle options) throws NetworkErrorException {
+        if (!mAccountType.equals(account.type)) {
+            throw new IllegalArgumentException("Request to the wrong authenticator!");
+        }
+        Bundle result = new Bundle();
+
+        if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // fill bundle with a success result.
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+        } else if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            // Specify data to be returned by the eventual activity.
+            Intent eventualActivityResultData = new Intent();
+            eventualActivityResultData.putExtra(AccountManager.KEY_BOOLEAN_RESULT, true);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+
+            // Fill result with Intent.
+            Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT,
+                    eventualActivityResultData);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response);
+
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+        } else {
+            // fill with error
+            fillResultWithError(
+                    result,
+                    AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                    AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        }
+        return result;
+    }
+
+    @Override
+    public Bundle getAuthToken(
+            AccountAuthenticatorResponse response,
+            Account account,
+            String authTokenType,
+            Bundle options) throws NetworkErrorException {
+        if (!mAccountType.equals(account.type)) {
+            throw new IllegalArgumentException("Request to the wrong authenticator!");
+        }
+        Bundle result = new Bundle();
+
+        long expiryMillis = (options == null)
+                ? 0 : options.getLong(AccountManagerServiceTestFixtures.KEY_TOKEN_EXPIRY);
+        if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // fill bundle with a success result.
+            result.putString(
+                    AccountManager.KEY_AUTHTOKEN, AccountManagerServiceTestFixtures.AUTH_TOKEN);
+            result.putLong(
+                    AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY,
+                    expiryMillis);
+            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+        } else if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            // Specify data to be returned by the eventual activity.
+            Intent eventualActivityResultData = new Intent();
+            eventualActivityResultData.putExtra(
+                    AccountManager.KEY_AUTHTOKEN, AccountManagerServiceTestFixtures.AUTH_TOKEN);
+            eventualActivityResultData.putExtra(
+                    AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY,
+                    expiryMillis);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+
+            // Fill result with Intent.
+            Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT,
+                    eventualActivityResultData);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response);
+
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+
+        } else {
+            fillResultWithError(
+                    result,
+                    AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                    AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        }
+        return result;
+    }
+
+    @Override
+    public String getAuthTokenLabel(String authTokenType) {
+        return AccountManagerServiceTestFixtures.AUTH_TOKEN_LABEL;
+    }
+
+    @Override
+    public Bundle updateCredentials(
+            AccountAuthenticatorResponse response,
+            Account account,
+            String authTokenType,
+            Bundle options) throws NetworkErrorException {
+        if (!mAccountType.equals(account.type)) {
+            throw new IllegalArgumentException("Request to the wrong authenticator!");
+        }
+        Bundle result = new Bundle();
+
+        if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // fill bundle with a success result.
+            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+        } else if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            // Specify data to be returned by the eventual activity.
+            Intent eventualActivityResultData = new Intent();
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+
+            // Fill result with Intent.
+            Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT,
+                    eventualActivityResultData);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response);
+
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+        } else {
+            // fill with error
+            fillResultWithError(
+                    result,
+                    AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                    AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        }
+        return result;
+    }
+
+    @Override
+    public Bundle hasFeatures(
+            AccountAuthenticatorResponse response,
+            Account account,
+            String[] features) throws NetworkErrorException {
+        Bundle result = new Bundle();
+        if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // fill bundle with true.
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        } else if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            // fill bundle with false.
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+        } else {
+            // return null for error
+            result = null;
+        }
+
+        response.onResult(result);
+        return null;
+    }
+
+    @Override
+    public Bundle startAddAccountSession(
+            AccountAuthenticatorResponse response,
+            String accountType,
+            String authTokenType,
+            String[] requiredFeatures,
+            Bundle options) throws NetworkErrorException {
+        if (!mAccountType.equals(accountType)) {
+            throw new IllegalArgumentException("Request to the wrong authenticator!");
+        }
+
+        String accountName = null;
+        Bundle sessionBundle = null;
+        String password = null;
+        if (options != null) {
+            accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME);
+            sessionBundle = options.getBundle(
+                    AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE);
+            password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD);
+        }
+
+        Bundle result = new Bundle();
+        if (accountName.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // fill bundle with a success result.
+            result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+            result.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN,
+                    AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN);
+            result.putString(AccountManager.KEY_PASSWORD, password);
+            result.putString(AccountManager.KEY_AUTHTOKEN,
+                    Integer.toString(mTokenCounter.incrementAndGet()));
+        } else if (accountName.equals(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            // Specify data to be returned by the eventual activity.
+            Intent eventualActivityResultData = new Intent();
+            eventualActivityResultData.putExtra(AccountManager.KEY_AUTHTOKEN,
+                    Integer.toString(mTokenCounter.incrementAndGet()));
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_STATUS_TOKEN,
+                    AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN);
+            eventualActivityResultData.putExtra(AccountManager.KEY_PASSWORD, password);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE,
+                    sessionBundle);
+            // Fill result with Intent.
+            Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT,
+                    eventualActivityResultData);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response);
+
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+        } else {
+            // fill with error
+            fillResultWithError(result, options);
+        }
+
+        return result;
+    }
+
+    @Override
+    public Bundle startUpdateCredentialsSession(
+            AccountAuthenticatorResponse response,
+            Account account,
+            String authTokenType,
+            Bundle options)
+            throws NetworkErrorException {
+
+        if (!mAccountType.equals(account.type)) {
+            throw new IllegalArgumentException("Request to the wrong authenticator!");
+        }
+
+        String accountName = null;
+        Bundle sessionBundle = null;
+        if (options != null) {
+            accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME);
+            sessionBundle = options.getBundle(
+            AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE);
+        }
+
+        Bundle result = new Bundle();
+        if (accountName.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // fill bundle with a success result.
+            result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+            result.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN,
+                    AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN);
+            result.putString(AccountManager.KEY_PASSWORD,
+                    AccountManagerServiceTestFixtures.ACCOUNT_PASSWORD);
+            result.putString(AccountManager.KEY_AUTHTOKEN,
+                    Integer.toString(mTokenCounter.incrementAndGet()));
+        } else if (accountName.equals(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            // Specify data to be returned by the eventual activity.
+            Intent eventualActivityResultData = new Intent();
+            eventualActivityResultData.putExtra(AccountManager.KEY_AUTHTOKEN,
+                    Integer.toString(mTokenCounter.incrementAndGet()));
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_STATUS_TOKEN,
+                    AccountManagerServiceTestFixtures.ACCOUNT_STATUS_TOKEN);
+            eventualActivityResultData.putExtra(AccountManager.KEY_PASSWORD,
+                    AccountManagerServiceTestFixtures.ACCOUNT_PASSWORD);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE,
+                    sessionBundle);
+            // Fill result with Intent.
+            Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT,
+                    eventualActivityResultData);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response);
+
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+        } else {
+            // fill with error
+            fillResultWithError(result, options);
+        }
+        return result;
+    }
+
+    @Override
+    public Bundle finishSession(AccountAuthenticatorResponse response,
+            String accountType,
+            Bundle sessionBundle) throws NetworkErrorException {
+
+        if (!mAccountType.equals(accountType)) {
+            throw new IllegalArgumentException("Request to the wrong authenticator!");
+        }
+
+        String accountName = null;
+        if (sessionBundle != null) {
+            accountName = sessionBundle.getString(
+            AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME);
+        }
+
+        Bundle result = new Bundle();
+        if (accountName.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // add sessionBundle into result for verification purpose
+            result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+            // fill bundle with a success result.
+            result.putString(AccountManager.KEY_ACCOUNT_NAME,
+                    AccountManagerServiceTestFixtures.ACCOUNT_NAME);
+            result.putString(AccountManager.KEY_ACCOUNT_TYPE,
+                    AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+            result.putString(AccountManager.KEY_AUTHTOKEN,
+                    Integer.toString(mTokenCounter.incrementAndGet()));
+        } else if (accountName.equals(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            // Specify data to be returned by the eventual activity.
+            Intent eventualActivityResultData = new Intent();
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_NAME,
+                    AccountManagerServiceTestFixtures.ACCOUNT_NAME);
+            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_TYPE,
+                    AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+            eventualActivityResultData.putExtra(AccountManager.KEY_AUTHTOKEN,
+                    Integer.toString(mTokenCounter.incrementAndGet()));
+
+            // Fill result with Intent.
+            Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT,
+                    eventualActivityResultData);
+            intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response);
+
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+        } else {
+            // fill with error
+            fillResultWithError(result, sessionBundle);
+        }
+        return result;
+    }
+
+    @Override
+    public Bundle isCredentialsUpdateSuggested(
+            final AccountAuthenticatorResponse response,
+            Account account,
+            String statusToken) throws NetworkErrorException {
+
+        Bundle result = new Bundle();
+        if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            // fill bundle with a success result.
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        } else {
+            // fill with error
+            fillResultWithError(
+                    result,
+                    AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                    AccountManagerServiceTestFixtures.ERROR_MESSAGE);
+        }
+
+        response.onResult(result);
+        return null;
+    }
+
+    @Override
+    public Bundle getAccountRemovalAllowed(
+            AccountAuthenticatorResponse response, Account account) throws NetworkErrorException {
+        Bundle result = new Bundle();
+        if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) {
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        } else if (account.name.equals(
+                AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) {
+            Intent intent = new Intent(mContext, AccountAuthenticatorDummyActivity.class);
+            result.putParcelable(AccountManager.KEY_INTENT, intent);
+        } else {
+            result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+        }
+        return result;
+    }
+
+    private void fillResultWithError(Bundle result, Bundle options) {
+        int errorCode = AccountManager.ERROR_CODE_INVALID_RESPONSE;
+        String errorMsg = "Default Error Message";
+        if (options != null) {
+            errorCode = options.getInt(AccountManager.KEY_ERROR_CODE);
+            errorMsg = options.getString(AccountManager.KEY_ERROR_MESSAGE);
+        }
+        fillResultWithError(result, errorCode, errorMsg);
+    }
+
+    private void fillResultWithError(Bundle result, int errorCode, String errorMsg) {
+        result.putInt(AccountManager.KEY_ERROR_CODE, errorCode);
+        result.putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1AuthenticatorService.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1AuthenticatorService.java
new file mode 100644
index 0000000..acd1046
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1AuthenticatorService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.accounts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * a test Mock Service for wrapping the TestAccountType1Authenticator
+ */
+public class TestAccountType1AuthenticatorService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        TestAccountType1Authenticator authenticator = new TestAccountType1Authenticator(
+                this, AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1);
+        return authenticator.getIBinder();
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType2Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType2Authenticator.java
new file mode 100644
index 0000000..4cbc8cb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType2Authenticator.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.accounts;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.frameworks.servicestests.R;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This authenticator is to mock account authenticator to test AccountManagerService.
+ */
+public class TestAccountType2Authenticator extends AbstractAccountAuthenticator {
+    private final AtomicInteger mTokenCounter  = new AtomicInteger(0);
+
+    private final String mAccountType;
+    private final Context mContext;
+
+    public TestAccountType2Authenticator(Context context, String accountType) {
+        super(context);
+        mAccountType = accountType;
+        mContext = context;
+    }
+
+    @Override
+    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+        throw new UnsupportedOperationException(
+                "editProperties is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle addAccount(
+            AccountAuthenticatorResponse response,
+            String accountType,
+            String authTokenType,
+            String[] requiredFeatures,
+            Bundle options) throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "addAccount is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle confirmCredentials(
+            AccountAuthenticatorResponse response,
+            Account account,
+            Bundle options) throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "confirmCredentials is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle getAuthToken(
+            AccountAuthenticatorResponse response,
+            Account account,
+            String authTokenType,
+            Bundle options) throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "getAuthToken is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public String getAuthTokenLabel(String authTokenType) {
+        throw new UnsupportedOperationException(
+                "getAuthTokenLabel is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle updateCredentials(
+            AccountAuthenticatorResponse response,
+            Account account,
+            String authTokenType,
+            Bundle options) throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "updateCredentials is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle hasFeatures(
+            AccountAuthenticatorResponse response,
+            Account account,
+            String[] features) throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "hasFeatures is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle startAddAccountSession(
+            AccountAuthenticatorResponse response,
+            String accountType,
+            String authTokenType,
+            String[] requiredFeatures,
+            Bundle options) throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "startAddAccountSession is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle startUpdateCredentialsSession(
+            AccountAuthenticatorResponse response,
+            Account account,
+            String authTokenType,
+            Bundle options)
+            throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "startUpdateCredentialsSession is not supported " +
+                "by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle finishSession(AccountAuthenticatorResponse response,
+            String accountType,
+            Bundle sessionBundle) throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "finishSession is not supported by the TestAccountType2Authenticator");
+    }
+
+    @Override
+    public Bundle isCredentialsUpdateSuggested(
+            final AccountAuthenticatorResponse response,
+            Account account,
+            String statusToken) throws NetworkErrorException {
+        throw new UnsupportedOperationException(
+                "isCredentialsUpdateSuggested is not supported " +
+                "by the TestAccountType2Authenticator");
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType2AuthenticatorService.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType2AuthenticatorService.java
new file mode 100644
index 0000000..b80dc78
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType2AuthenticatorService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.accounts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * a test Mock Service for wrapping the TestAccountType2Authenticator
+ */
+public class TestAccountType2AuthenticatorService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        TestAccountType2Authenticator authenticator = new TestAccountType2Authenticator(
+                this, AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2);
+        return authenticator.getIBinder();
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 4886a5f..677e468 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -28,6 +29,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.PendingHostUpdate;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContextWrapper;
@@ -39,11 +41,17 @@
 import android.os.UserHandle;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.widget.RemoteViews;
 
+import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.server.LocalServices;
 
 import org.mockito.ArgumentCaptor;
 
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+
 
 /**
  * Tests for {@link AppWidgetManager} and {@link AppWidgetServiceImpl}.
@@ -57,11 +65,15 @@
 @SmallTest
 public class AppWidgetServiceImplTest extends InstrumentationTestCase {
 
+    private static final int HOST_ID = 42;
+
     private TestContext mTestContext;
+    private String mPkgName;
     private AppWidgetServiceImpl mService;
     private AppWidgetManager mManager;
 
     private ShortcutServiceInternal mMockShortcutService;
+    private IAppWidgetHost mMockHost;
 
     @Override
     protected void setUp() throws Exception {
@@ -70,12 +82,13 @@
         LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
 
         mTestContext = new TestContext();
+        mPkgName = mTestContext.getOpPackageName();
         mService = new AppWidgetServiceImpl(mTestContext);
         mManager = new AppWidgetManager(mTestContext, mService);
 
         mMockShortcutService = mock(ShortcutServiceInternal.class);
+        mMockHost = mock(IAppWidgetHost.class);
         LocalServices.addService(ShortcutServiceInternal.class, mMockShortcutService);
-
         mService.onStart();
     }
 
@@ -108,6 +121,142 @@
         assertEquals(provider, providerCaptor.getValue().provider);
     }
 
+    public void testProviderUpdatesReceived() throws Exception {
+        int widgetId = setupHostAndWidget();
+        RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
+        mManager.updateAppWidget(widgetId, view);
+        mManager.updateAppWidget(widgetId, view);
+        mManager.updateAppWidget(widgetId, view);
+        mManager.updateAppWidget(widgetId, view);
+
+        flushMainThread();
+        verify(mMockHost, times(4)).updateAppWidget(eq(widgetId), any(RemoteViews.class));
+
+        reset(mMockHost);
+        mManager.notifyAppWidgetViewDataChanged(widgetId, 22);
+        flushMainThread();
+        verify(mMockHost, times(1)).viewDataChanged(eq(widgetId), eq(22));
+    }
+
+    public void testProviderUpdatesNotReceived() throws Exception {
+        int widgetId = setupHostAndWidget();
+        mService.stopListening(mPkgName, HOST_ID);
+        RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
+        mManager.updateAppWidget(widgetId, view);
+        mManager.notifyAppWidgetViewDataChanged(widgetId, 22);
+
+        flushMainThread();
+        verify(mMockHost, times(0)).updateAppWidget(anyInt(), any(RemoteViews.class));
+        verify(mMockHost, times(0)).viewDataChanged(anyInt(), eq(22));
+    }
+
+    public void testNoUpdatesReceived_queueEmpty() {
+        int widgetId = setupHostAndWidget();
+        RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
+        mManager.updateAppWidget(widgetId, view);
+        mManager.notifyAppWidgetViewDataChanged(widgetId, 22);
+        mService.stopListening(mPkgName, HOST_ID);
+
+        List<PendingHostUpdate> updates = mService.startListening(
+                mMockHost, mPkgName, HOST_ID, new int[0]).getList();
+        assertTrue(updates.isEmpty());
+    }
+
+    /**
+     * Sends dummy widget updates to {@link #mManager}.
+     * @param widgetId widget to update
+     * @param viewIds a list of view ids for which
+     *                {@link AppWidgetManager#notifyAppWidgetViewDataChanged} will be called
+     */
+    private void sendDummyUpdates(int widgetId, int... viewIds) {
+        Random r = new Random();
+        RemoteViews view = new RemoteViews(mPkgName, android.R.layout.simple_list_item_1);
+        for (int i = r.nextInt(10) + 2; i >= 0; i--) {
+            mManager.updateAppWidget(widgetId, view);
+        }
+
+        for (int viewId : viewIds) {
+            mManager.notifyAppWidgetViewDataChanged(widgetId, viewId);
+            for (int i = r.nextInt(3); i >= 0; i--) {
+                mManager.updateAppWidget(widgetId, view);
+            }
+        }
+    }
+
+    public void testNoUpdatesReceived_queueNonEmpty_noWidgetId() {
+        int widgetId = setupHostAndWidget();
+        mService.stopListening(mPkgName, HOST_ID);
+
+        sendDummyUpdates(widgetId, 22, 23);
+        List<PendingHostUpdate> updates = mService.startListening(
+                mMockHost, mPkgName, HOST_ID, new int[0]).getList();
+        assertTrue(updates.isEmpty());
+    }
+
+    public void testUpdatesReceived_queueNotEmpty_widgetIdProvided() {
+        int widgetId = setupHostAndWidget();
+        int widgetId2 = bindNewWidget();
+        mService.stopListening(mPkgName, HOST_ID);
+
+        sendDummyUpdates(widgetId, 22, 23);
+        sendDummyUpdates(widgetId2, 100, 101, 102);
+
+        List<PendingHostUpdate> updates = mService.startListening(
+                mMockHost, mPkgName, HOST_ID, new int[]{widgetId}).getList();
+        // 3 updates corresponding to the first widget
+        assertEquals(3, updates.size());
+    }
+
+    public void testUpdatesReceived_queueNotEmpty_widgetIdProvided2() {
+        int widgetId = setupHostAndWidget();
+        int widgetId2 = bindNewWidget();
+        mService.stopListening(mPkgName, HOST_ID);
+
+        sendDummyUpdates(widgetId, 22, 23);
+        sendDummyUpdates(widgetId2, 100, 101, 102);
+
+        List<PendingHostUpdate> updates = mService.startListening(
+                mMockHost, mPkgName, HOST_ID, new int[]{widgetId2}).getList();
+        // 4 updates corresponding to the second widget
+        assertEquals(4, updates.size());
+    }
+
+    public void testUpdatesReceived_queueNotEmpty_multipleWidgetIdProvided() {
+        int widgetId = setupHostAndWidget();
+        int widgetId2 = bindNewWidget();
+        mService.stopListening(mPkgName, HOST_ID);
+
+        sendDummyUpdates(widgetId, 22, 23);
+        sendDummyUpdates(widgetId2, 100, 101, 102);
+
+        List<PendingHostUpdate> updates = mService.startListening(
+                mMockHost, mPkgName, HOST_ID, new int[]{widgetId, widgetId2}).getList();
+        // 3 updates for first widget and 4 for second
+        assertEquals(7, updates.size());
+    }
+
+    private int setupHostAndWidget() {
+        List<PendingHostUpdate> updates = mService.startListening(
+                mMockHost, mPkgName, HOST_ID, new int[0]).getList();
+        assertTrue(updates.isEmpty());
+        return bindNewWidget();
+    }
+
+    private int bindNewWidget() {
+        ComponentName provider = new ComponentName(mTestContext, DummyAppWidget.class);
+        int widgetId = mService.allocateAppWidgetId(mPkgName, HOST_ID);
+        assertTrue(mManager.bindAppWidgetIdIfAllowed(widgetId, provider));
+        assertEquals(provider, mManager.getAppWidgetInfo(widgetId).provider);
+
+        return widgetId;
+    }
+
+    private void flushMainThread() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        new Handler(mTestContext.getMainLooper()).post(latch::countDown);
+        latch.await();
+    }
+
     private class TestContext extends ContextWrapper {
 
         public TestContext() {
@@ -125,5 +274,15 @@
         public void unregisterReceiver(BroadcastReceiver receiver) {
             // ignore.
         }
+
+        @Override
+        public void enforceCallingOrSelfPermission(String permission, String message) {
+            // ignore.
+        }
+
+        @Override
+        public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+            // ignore.
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 4927f0c..3b92a34 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -37,6 +37,7 @@
 import com.android.internal.widget.LockPatternUtils;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.Map;
 
 /**
@@ -264,6 +265,12 @@
         }
 
         @Override
+        void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force)
+                throws IOException {
+            context.recoverySystem.rebootWipeUserData(shutdown, reason, force);
+        }
+
+        @Override
         boolean systemPropertiesGetBoolean(String key, boolean def) {
             return context.systemProperties.getBoolean(key, def);
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index c3eb09d..c29668f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -15,6 +15,10 @@
  */
 package com.android.server.devicepolicy;
 
+import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
+import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
+import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED;
+
 import android.Manifest.permission;
 import android.app.Activity;
 import android.app.admin.DeviceAdminReceiver;
@@ -39,6 +43,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.test.MoreAsserts;
@@ -61,9 +66,14 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
+import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -75,6 +85,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 /**
@@ -928,9 +939,8 @@
 
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
-                MockUtils.checkUserRestrictions()
-        );
+                eq(null),
+                eq(true), eq(CAMERA_NOT_DISABLED));
 
         assertFalse(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM));
 
@@ -1122,6 +1132,7 @@
 
     public void testSetGetApplicationRestriction() {
         setAsProfileOwner(admin1);
+        mContext.packageName = admin1.getPackageName();
 
         {
             Bundle rest = new Bundle();
@@ -1153,29 +1164,131 @@
         assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
     }
 
+    /**
+     * Setup a package in the package manager mock. Useful for faking installed applications.
+     *
+     * @param packageName the name of the package to be setup
+     * @param appId the application ID to be given to the package
+     * @return the UID of the package as known by the mock package manager
+     */
+    private int setupPackageInPackageManager(final String packageName, final int appId)
+            throws Exception {
+        // Make the PackageManager return the package instead of throwing a NameNotFoundException
+        final PackageInfo pi = new PackageInfo();
+        pi.applicationInfo = new ApplicationInfo();
+        pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
+        doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
+                eq(packageName),
+                anyInt(),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+        // Setup application UID with the PackageManager
+        final int uid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, appId);
+        doReturn(uid).when(mContext.packageManager).getPackageUidAsUser(
+                eq(packageName),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+        // Associate packageName to uid
+        doReturn(packageName).when(mContext.ipackageManager).getNameForUid(eq(uid));
+        doReturn(new String[]{packageName})
+            .when(mContext.ipackageManager).getPackagesForUid(eq(uid));
+        return uid;
+    }
+
+    /**
+     * Simple test for delegate set/get and general delegation. Tests verifying that delegated
+     * privileges can acually be exercised by a delegate are not covered here.
+     */
+    public void testDelegation() throws Exception {
+        setAsProfileOwner(admin1);
+
+        final int userHandle = DpmMockContext.CALLER_USER_HANDLE;
+
+        // Given two packages
+        final String CERT_DELEGATE = "com.delegate.certs";
+        final String RESTRICTIONS_DELEGATE = "com.delegate.apprestrictions";
+        final int CERT_DELEGATE_UID = setupPackageInPackageManager(CERT_DELEGATE, 20988);
+        final int RESTRICTIONS_DELEGATE_UID = setupPackageInPackageManager(RESTRICTIONS_DELEGATE,
+                20989);
+
+        // On delegation
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        mContext.packageName = admin1.getPackageName();
+        dpm.setCertInstallerPackage(admin1, CERT_DELEGATE);
+        dpm.setApplicationRestrictionsManagingPackage(admin1, RESTRICTIONS_DELEGATE);
+
+        // DPMS correctly stores and retrieves the delegates
+        DevicePolicyManagerService.DevicePolicyData policy = dpms.mUserData.get(userHandle);
+        assertEquals(2, policy.mDelegationMap.size());
+        MoreAsserts.assertContentsInAnyOrder(policy.mDelegationMap.get(CERT_DELEGATE),
+            DELEGATION_CERT_INSTALL);
+        MoreAsserts.assertContentsInAnyOrder(dpm.getDelegatedScopes(admin1, CERT_DELEGATE),
+            DELEGATION_CERT_INSTALL);
+        assertEquals(CERT_DELEGATE, dpm.getCertInstallerPackage(admin1));
+        MoreAsserts.assertContentsInAnyOrder(policy.mDelegationMap.get(RESTRICTIONS_DELEGATE),
+            DELEGATION_APP_RESTRICTIONS);
+        MoreAsserts.assertContentsInAnyOrder(dpm.getDelegatedScopes(admin1, RESTRICTIONS_DELEGATE),
+            DELEGATION_APP_RESTRICTIONS);
+        assertEquals(RESTRICTIONS_DELEGATE, dpm.getApplicationRestrictionsManagingPackage(admin1));
+
+        // On calling install certificate APIs from an unauthorized process
+        mContext.binder.callingUid = RESTRICTIONS_DELEGATE_UID;
+        mContext.packageName = RESTRICTIONS_DELEGATE;
+
+        // DPMS throws a SecurityException
+        try {
+            dpm.installCaCert(null, null);
+            fail("Didn't throw SecurityException on unauthorized access");
+        } catch (SecurityException expected) {
+        }
+
+        // On calling install certificate APIs from an authorized process
+        mContext.binder.callingUid = CERT_DELEGATE_UID;
+        mContext.packageName = CERT_DELEGATE;
+
+        // DPMS executes without a SecurityException
+        try {
+            dpm.installCaCert(null, null);
+        } catch (SecurityException unexpected) {
+            fail("Threw SecurityException on authorized access");
+        } catch (NullPointerException expected) {
+        }
+
+        // On removing a delegate
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        mContext.packageName = admin1.getPackageName();
+        dpm.setCertInstallerPackage(admin1, null);
+
+        // DPMS does not allow access to ex-delegate
+        mContext.binder.callingUid = CERT_DELEGATE_UID;
+        mContext.packageName = CERT_DELEGATE;
+        try {
+            dpm.installCaCert(null, null);
+            fail("Didn't throw SecurityException on unauthorized access");
+        } catch (SecurityException expected) {
+        }
+
+        // But still allows access to other existing delegates
+        mContext.binder.callingUid = RESTRICTIONS_DELEGATE_UID;
+        mContext.packageName = RESTRICTIONS_DELEGATE;
+        try {
+            dpm.getApplicationRestrictions(null, "pkg");
+        } catch (SecurityException expected) {
+            fail("Threw SecurityException on authorized access");
+        }
+    }
+
     public void testApplicationRestrictionsManagingApp() throws Exception {
         setAsProfileOwner(admin1);
 
         final String nonExistAppRestrictionsManagerPackage = "com.google.app.restrictions.manager2";
         final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
         final int appRestrictionsManagerAppId = 20987;
-        final int appRestrictionsManagerUid = UserHandle.getUid(
-                DpmMockContext.CALLER_USER_HANDLE, appRestrictionsManagerAppId);
-        doReturn(appRestrictionsManagerUid).when(mContext.packageManager).getPackageUidAsUser(
-                eq(appRestrictionsManagerPackage),
-                eq(DpmMockContext.CALLER_USER_HANDLE));
-        mContext.binder.callingUid = appRestrictionsManagerUid;
-
-        final PackageInfo pi = new PackageInfo();
-        pi.applicationInfo = new ApplicationInfo();
-        pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
-        doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
-                eq(appRestrictionsManagerPackage),
-                anyInt(),
-                eq(DpmMockContext.CALLER_USER_HANDLE));
+        final int appRestrictionsManagerUid = setupPackageInPackageManager(
+                appRestrictionsManagerPackage, appRestrictionsManagerAppId);
 
         // appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
         // delegated that permission yet.
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        mContext.packageName = admin1.getPackageName();
         assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
         Bundle rest = new Bundle();
         rest.putString("KEY_STRING", "Foo1");
@@ -1184,18 +1297,21 @@
             fail("Didn't throw expected SecurityException");
         } catch (SecurityException expected) {
             MoreAsserts.assertContainsRegex(
-                    "caller cannot manage application restrictions", expected.getMessage());
+                    "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
+                    expected.getMessage());
         }
         try {
             dpm.getApplicationRestrictions(null, "pkg1");
             fail("Didn't throw expected SecurityException");
         } catch (SecurityException expected) {
             MoreAsserts.assertContainsRegex(
-                    "caller cannot manage application restrictions", expected.getMessage());
+                    "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
+                    expected.getMessage());
         }
 
         // Check via the profile owner that no restrictions were set.
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        mContext.packageName = admin1.getPackageName();
         assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
 
         // Check the API does not allow setting a non-existent package
@@ -1215,6 +1331,7 @@
 
         // Now that package should be able to set and retrieve app restrictions.
         mContext.binder.callingUid = appRestrictionsManagerUid;
+        mContext.packageName = appRestrictionsManagerPackage;
         assertTrue(dpm.isCallerApplicationRestrictionsManagingPackage());
         dpm.setApplicationRestrictions(null, "pkg1", rest);
         Bundle returned = dpm.getApplicationRestrictions(null, "pkg1");
@@ -1230,12 +1347,14 @@
             fail("Didn't throw expected SecurityException");
         } catch (SecurityException expected) {
             MoreAsserts.assertContainsRegex(
-                    "caller cannot manage application restrictions", expected.getMessage());
+                    "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
+                    expected.getMessage());
         }
 
         // The DPM is still able to manage app restrictions, even if it allowed another app to do it
         // too.
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        mContext.packageName = admin1.getPackageName();
         assertEquals(returned, dpm.getApplicationRestrictions(admin1, "pkg1"));
         dpm.setApplicationRestrictions(admin1, "pkg1", null);
         assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
@@ -1244,13 +1363,15 @@
         dpm.setApplicationRestrictionsManagingPackage(admin1, null);
         assertNull(dpm.getApplicationRestrictionsManagingPackage(admin1));
         mContext.binder.callingUid = appRestrictionsManagerUid;
+        mContext.packageName = appRestrictionsManagerPackage;
         assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
         try {
             dpm.setApplicationRestrictions(null, "pkg1", null);
             fail("Didn't throw expected SecurityException");
         } catch (SecurityException expected) {
             MoreAsserts.assertContainsRegex(
-                    "caller cannot manage application restrictions", expected.getMessage());
+                    "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
+                    expected.getMessage());
         }
     }
 
@@ -1287,7 +1408,8 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(defaultRestrictions),
-                MockUtils.checkUserRestrictions()
+                eq(true) /* isDeviceOwner */,
+                eq(CAMERA_NOT_DISABLED)
         );
         reset(mContext.userManagerInternal);
 
@@ -1296,21 +1418,21 @@
         }
 
         assertNoDeviceOwnerRestrictions();
+        reset(mContext.userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
-        );
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
-        );
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS,
+                        UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1328,8 +1450,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
-                MockUtils.checkUserRestrictions()
-        );
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1345,8 +1466,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(),
-                MockUtils.checkUserRestrictions()
-        );
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         assertNoDeviceOwnerRestrictions();
@@ -1358,42 +1478,38 @@
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
-                        UserManager.DISALLOW_UNMUTE_MICROPHONE)
-        );
+                        UserManager.DISALLOW_UNMUTE_MICROPHONE),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME);
         dpm.clearUserRestriction(admin1, UserManager.DISALLOW_UNMUTE_MICROPHONE);
-
+        reset(mContext.userManagerInternal);
 
         // More tests.
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER)
-        );
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_FUN);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                MockUtils.checkUserRestrictions(),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
-                        UserManager.DISALLOW_ADD_USER)
-        );
+                        UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.setCameraDisabled(admin1, true);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 // DISALLOW_CAMERA will be applied to both local and global.
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
-                        UserManager.DISALLOW_CAMERA, UserManager.DISALLOW_ADD_USER)
-        );
+                        UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_DISABLED_GLOBALLY));
         reset(mContext.userManagerInternal);
 
         // Set up another DA and let it disable camera.  Now DISALLOW_CAMERA will only be applied
@@ -1407,11 +1523,10 @@
 
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
-                // DISALLOW_CAMERA will be applied to both local and global.
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA),
+                // DISALLOW_CAMERA will be applied to both local and global. <- TODO: fix this
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
-                        UserManager.DISALLOW_ADD_USER)
-        );
+                        UserManager.DISALLOW_ADD_USER),
+                eq(true), eq(CAMERA_DISABLED_LOCALLY));
         reset(mContext.userManagerInternal);
         // TODO Make sure restrictions are written to the file.
     }
@@ -1429,8 +1544,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
@@ -1438,8 +1552,7 @@
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                         UserManager.DISALLOW_OUTGOING_CALLS),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1462,8 +1575,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1484,8 +1596,7 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         DpmTestUtils.assertRestrictions(
@@ -1507,18 +1618,15 @@
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
                         UserManager.DISALLOW_UNMUTE_MICROPHONE),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_NOT_DISABLED));
         reset(mContext.userManagerInternal);
 
         dpm.setCameraDisabled(admin1, true);
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(DpmMockContext.CALLER_USER_HANDLE),
-                MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA,
-                        UserManager.DISALLOW_ADJUST_VOLUME,
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
                         UserManager.DISALLOW_UNMUTE_MICROPHONE),
-                isNull(Bundle.class)
-        );
+                eq(false), eq(CAMERA_DISABLED_LOCALLY));
         reset(mContext.userManagerInternal);
 
         // TODO Make sure restrictions are written to the file.
@@ -1558,7 +1666,8 @@
         verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(defaultRestrictions),
-                MockUtils.checkUserRestrictions()
+                eq(true) /* isDeviceOwner */,
+                eq(CAMERA_NOT_DISABLED)
         );
         reset(mContext.userManagerInternal);
 
@@ -1600,7 +1709,8 @@
             verify(mContext.userManagerInternal, atLeast(1)).setDevicePolicyUserRestrictions(
                 eq(UserHandle.USER_SYSTEM),
                 MockUtils.checkUserRestrictions(newDefaultEnabledRestriction),
-                MockUtils.checkUserRestrictions()
+                eq(true) /* isDeviceOwner */,
+                eq(CAMERA_NOT_DISABLED)
             );
             reset(mContext.userManagerInternal);
 
@@ -1840,6 +1950,81 @@
         }
     }
 
+    public void testCreateAdminSupportIntent() throws Exception {
+        // Setup device owner.
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+
+        // Nonexisting permission returns null
+        Intent intent = dpm.createAdminSupportIntent("disallow_nothing");
+        assertNull(intent);
+
+        // Existing permission that is not set returns null
+        intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME);
+        assertNull(intent);
+
+        // Existing permission that is not set by device/profile owner returns null
+        when(mContext.userManager.hasUserRestriction(
+                eq(UserManager.DISALLOW_ADJUST_VOLUME),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(true);
+        intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME);
+        assertNull(intent);
+
+        // Permission that is set by device owner returns correct intent
+        when(mContext.userManager.getUserRestrictionSource(
+                eq(UserManager.DISALLOW_ADJUST_VOLUME),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+        intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME);
+        assertNotNull(intent);
+        assertEquals(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS, intent.getAction());
+        assertEquals(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID),
+                intent.getIntExtra(Intent.EXTRA_USER_ID, -1));
+        assertEquals(admin1,
+                (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN));
+        assertEquals(UserManager.DISALLOW_ADJUST_VOLUME,
+                intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+        // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not
+        // user restrictions
+
+        // Camera is not disabled
+        intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
+        assertNull(intent);
+
+        // Camera is disabled
+        dpm.setCameraDisabled(admin1, true);
+        intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
+        assertNotNull(intent);
+        assertEquals(DevicePolicyManager.POLICY_DISABLE_CAMERA,
+                intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+        // Screen capture is not disabled
+        intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+        assertNull(intent);
+
+        // Screen capture is disabled
+        dpm.setScreenCaptureDisabled(admin1, true);
+        intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+        assertNotNull(intent);
+        assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
+                intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+        // Same checks for different user
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        // Camera should be disabled by device owner
+        intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
+        assertNotNull(intent);
+        assertEquals(DevicePolicyManager.POLICY_DISABLE_CAMERA,
+                intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+        assertEquals(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID),
+                intent.getIntExtra(Intent.EXTRA_USER_ID, -1));
+        // ScreenCapture should not be disabled by device owner
+        intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+        assertNull(intent);
+    }
+
     /**
      * Test for:
      * {@link DevicePolicyManager#setAffiliationIds}
@@ -2124,8 +2309,37 @@
         setupDeviceOwner();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
-        final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
-        final long ONE_MINUTE = 60 * 1000;
+        final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
+        final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
+        final long MIN_PLUS_ONE_MINUTE = MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE;
+        final long MAX_MINUS_ONE_MINUTE = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS
+                - ONE_MINUTE;
+
+        // verify that the minimum timeout cannot be modified on user builds (system property is
+        // not being read)
+        mContext.buildMock.isDebuggable = false;
+
+        dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
+
+        verify(mContext.systemProperties, never()).getLong(anyString(), anyLong());
+
+        // restore to the debuggable build state
+        mContext.buildMock.isDebuggable = true;
+
+        // Always return the default (second arg) when getting system property for long type
+        when(mContext.systemProperties.getLong(anyString(), anyLong())).thenAnswer(
+                new Answer<Long>() {
+                    @Override
+                    public Long answer(InvocationOnMock invocation) throws Throwable {
+                        return (Long) invocation.getArguments()[1];
+                    }
+                }
+        );
+
+        // reset to default (0 means the admin is not participating, so default should be returned)
+        dpm.setRequiredStrongAuthTimeout(admin1, 0);
 
         // aggregation should be the default if unset by any admin
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
@@ -2142,7 +2356,7 @@
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
                 DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS);
 
-        // 0 means default
+        // 0 means the admin is not participating, so default should be returned
         dpm.setRequiredStrongAuthTimeout(admin1, 0);
         assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), 0);
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
@@ -2153,12 +2367,14 @@
         assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS);
         assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS);
 
-        // value within range
-        dpm.setRequiredStrongAuthTimeout(admin1, MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE);
-        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS
-                + ONE_MINUTE);
-        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS
-                + ONE_MINUTE);
+        // values within range
+        dpm.setRequiredStrongAuthTimeout(admin1, MIN_PLUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MIN_PLUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MIN_PLUS_ONE_MINUTE);
+
+        dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
 
         // reset to default
         dpm.setRequiredStrongAuthTimeout(admin1, 0);
@@ -2332,6 +2548,23 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
     }
 
+    private void setup_nonSplitUser_withDo_primaryUser() throws Exception {
+        setDeviceOwner();
+        setup_nonSplitUser_afterDeviceSetup_primaryUser();
+        setUpPackageManagerForFakeAdmin(adminAnotherPackage, DpmMockContext.ANOTHER_UID, admin2);
+    }
+
+    private void setup_nonSplitUser_withDo_primaryUser_ManagedProfile() throws Exception {
+        setup_nonSplitUser_withDo_primaryUser();
+        final int MANAGED_PROFILE_USER_ID = 18;
+        final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 1308);
+        addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+        when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM,
+                false /* we can't remove a managed profile */)).thenReturn(false);
+        when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM,
+                true)).thenReturn(true);
+    }
+
     public void testIsProvisioningAllowed_nonSplitUser_afterDeviceSetup_primaryUser()
             throws Exception {
         setup_nonSplitUser_afterDeviceSetup_primaryUser();
@@ -2361,144 +2594,124 @@
                 DevicePolicyManager.CODE_NOT_SYSTEM_USER_SPLIT);
     }
 
-    public void testIsProvisioningAllowed_nonSplitUser_withDo_primaryUser() throws Exception {
-        setDeviceOwner();
-        setup_nonSplitUser_afterDeviceSetup_primaryUser();
-        setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
+    public void testProvisioning_nonSplitUser_withDo_primaryUser() throws Exception {
+        setup_nonSplitUser_withDo_primaryUser();
         mContext.packageName = admin1.getPackageName();
-
-        final ComponentName adminDifferentPackage =
-                new ComponentName("another.package", "whatever.random.class");
-        final int ANOTHER_UID = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, 948);
-        setUpPackageManagerForFakeAdmin(adminDifferentPackage, ANOTHER_UID, admin2);
-
-        // COMP mode is allowed.
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
-
-        when(mContext.userManager.hasUserRestriction(
-                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
-                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
-                .thenReturn(true);
-
-        // The DO should be allowed to initiate provisioning if it set the restriction itself.
-        when(mContext.userManager.getUserRestrictionSource(
-                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
-                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
-                .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
-
-        // But another app should not
-        mContext.binder.callingUid = ANOTHER_UID;
-        mContext.packageName = adminDifferentPackage.getPackageName();
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
-
-        // The DO should not be allowed to initiate provisioning if the restriction is set by
-        // another entity.
-        when(mContext.userManager.getUserRestrictionSource(
-                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
-                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
-                .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
-        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
-        mContext.packageName = admin1.getPackageName();
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
-
-        mContext.binder.callingUid = ANOTHER_UID;
-        mContext.packageName = adminDifferentPackage.getPackageName();
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
-    }
-
-    public void testIsProvisioningAllowed_nonSplitUser_comp() throws Exception {
-        setDeviceOwner();
-        setup_nonSplitUser_afterDeviceSetup_primaryUser();
-        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
-
-        final ComponentName adminDifferentPackage =
-                new ComponentName("another.package", "whatever.class");
-        final int ANOTHER_UID = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, 948);
-        setUpPackageManagerForFakeAdmin(adminDifferentPackage, ANOTHER_UID, admin2);
-
-        final int MANAGED_PROFILE_USER_ID = 18;
-        final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 1308);
-        addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
-
-        when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
-                false /* we can't remove a managed profile */)).thenReturn(false);
-        when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
-                true)).thenReturn(true);
-
-        // We can delete the managed profile to create a new one, so provisioning is allowed.
-        mContext.packageName = admin1.getPackageName();
-        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
-
-        mContext.packageName = adminDifferentPackage.getPackageName();
-        mContext.binder.callingUid = ANOTHER_UID;
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
-
-        when(mContext.userManager.hasUserRestriction(
-                eq(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE),
-                eq(UserHandle.of(DpmMockContext.CALLER_USER_HANDLE))))
-                .thenReturn(true);
-
-        // Now, we can't remove the profile any more to create a new one.
-        mContext.packageName = admin1.getPackageName();
-        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
-
-        mContext.packageName = adminDifferentPackage.getPackageName();
-        mContext.binder.callingUid = ANOTHER_UID;
-        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
-    }
-
-    public void
-    testCheckProvisioningPreCondition_nonSplitUser_withDo_primaryUser() throws Exception {
-        setDeviceOwner();
-        setup_nonSplitUser_afterDeviceSetup_primaryUser();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_HAS_DEVICE_OWNER);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
 
         // COMP mode is allowed.
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_OK);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
 
-        // And other DPCs can also provisioning a managed profile (DO + BYOD case).
+        // And other DPCs can also provision a managed profile (DO + BYOD case).
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                "some.other.dpc.package.name",
+                DpmMockContext.ANOTHER_PACKAGE_NAME,
                 DevicePolicyManager.CODE_OK);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true,
+                DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
+    }
 
+    public void testProvisioning_nonSplitUser_withDo_primaryUser_restrictedByDo() throws Exception {
+        setup_nonSplitUser_withDo_primaryUser();
+        mContext.packageName = admin1.getPackageName();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        // The DO should be allowed to initiate provisioning if it set the restriction itself, but
+        // other packages should be forbidden.
         when(mContext.userManager.hasUserRestriction(
                 eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
                 eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
                 .thenReturn(true);
-
-        // The DO should be allowed to initiate provisioning if it set the restriction itself, but
-        // other packages should be forbidden.
         when(mContext.userManager.getUserRestrictionSource(
                 eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
                 eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
                 .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_OK);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                "some.other.dpc.package.name",
+                DpmMockContext.ANOTHER_PACKAGE_NAME,
                 DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
+                DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
+    }
 
+    public void testProvisioning_nonSplitUser_withDo_primaryUser_restrictedBySystem()
+            throws Exception {
+        setup_nonSplitUser_withDo_primaryUser();
+        mContext.packageName = admin1.getPackageName();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         // The DO should not be allowed to initiate provisioning if the restriction is set by
         // another entity.
+        when(mContext.userManager.hasUserRestriction(
+                eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
+                eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+                .thenReturn(true);
         when(mContext.userManager.getUserRestrictionSource(
                 eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
                 eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
                 .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
                 DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
-                assertCheckProvisioningPreCondition(
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+
+        assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                "some.other.dpc.package.name",
+                DpmMockContext.ANOTHER_PACKAGE_NAME,
                 DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
+                DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
+    }
+
+    public void testCheckProvisioningPreCondition_nonSplitUser_comp() throws Exception {
+        setup_nonSplitUser_withDo_primaryUser_ManagedProfile();
+        mContext.packageName = admin1.getPackageName();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+
+        // We can delete the managed profile to create a new one, so provisioning is allowed.
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                DevicePolicyManager.CODE_OK);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+        assertCheckProvisioningPreCondition(
+                DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                DpmMockContext.ANOTHER_PACKAGE_NAME,
+                DevicePolicyManager.CODE_OK);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true,
+                DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
+    }
+
+    public void testCheckProvisioningPreCondition_nonSplitUser_comp_cannot_remove_profile()
+            throws Exception {
+        setup_nonSplitUser_withDo_primaryUser_ManagedProfile();
+        mContext.packageName = admin1.getPackageName();
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        when(mContext.userManager.hasUserRestriction(
+                eq(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE),
+                eq(UserHandle.SYSTEM)))
+                .thenReturn(true);
+        when(mContext.userManager.getUserRestrictionSource(
+                eq(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE),
+                eq(UserHandle.SYSTEM)))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+
+        // We can't remove the profile to create a new one.
+        assertCheckProvisioningPreCondition(
+                DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                DpmMockContext.ANOTHER_PACKAGE_NAME,
+                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false,
+                DpmMockContext.ANOTHER_PACKAGE_NAME, DpmMockContext.ANOTHER_UID);
+
+        // But the device owner can still do it because it has set the restriction itself.
+        assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+                DevicePolicyManager.CODE_OK);
+        assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
     }
 
     private void setup_splitUser_firstBoot_systemUser() throws Exception {
@@ -3207,6 +3420,182 @@
         }
     }
 
+    public void testWipeDataDeviceOwner() throws Exception {
+        setDeviceOwner();
+        when(mContext.userManager.getUserRestrictionSource(
+                UserManager.DISALLOW_FACTORY_RESET,
+                UserHandle.SYSTEM))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+
+        dpm.wipeData(0);
+        verify(mContext.recoverySystem).rebootWipeUserData(
+                /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true));
+    }
+
+    public void testWipeDataDeviceOwnerDisallowed() throws Exception {
+        setDeviceOwner();
+        when(mContext.userManager.getUserRestrictionSource(
+                UserManager.DISALLOW_FACTORY_RESET,
+                UserHandle.SYSTEM))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
+        try {
+            // The DO is not allowed to wipe the device if the user restriction was set
+            // by the system
+            dpm.wipeData(0);
+            fail("SecurityException not thrown");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testMaximumFailedPasswordAttemptsReachedManagedProfile() throws Exception {
+        final int MANAGED_PROFILE_USER_ID = 15;
+        final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+        addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+        // Even if the caller is the managed profile, the current user is the user 0
+        when(mContext.iactivityManager.getCurrentUser())
+                .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+        when(mContext.userManager.getUserRestrictionSource(
+                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                UserHandle.of(MANAGED_PROFILE_USER_ID)))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_PROFILE_OWNER);
+
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        dpm.setMaximumFailedPasswordsForWipe(admin1, 3);
+
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+        // Failed password attempts on the parent user are taken into account, as there isn't a
+        // separate work challenge.
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+
+        // The profile should be wiped even if DISALLOW_REMOVE_MANAGED_PROFILE is enabled, because
+        // both the user restriction and the policy were set by the PO.
+        verify(mContext.userManagerInternal).removeUserEvenWhenDisallowed(
+                MANAGED_PROFILE_USER_ID);
+        verifyZeroInteractions(mContext.recoverySystem);
+    }
+
+    public void testMaximumFailedPasswordAttemptsReachedManagedProfileDisallowed()
+            throws Exception {
+        final int MANAGED_PROFILE_USER_ID = 15;
+        final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+        addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+        // Even if the caller is the managed profile, the current user is the user 0
+        when(mContext.iactivityManager.getCurrentUser())
+                .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+        when(mContext.userManager.getUserRestrictionSource(
+                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                UserHandle.of(MANAGED_PROFILE_USER_ID)))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
+
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        dpm.setMaximumFailedPasswordsForWipe(admin1, 3);
+
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+        // Failed password attempts on the parent user are taken into account, as there isn't a
+        // separate work challenge.
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+
+        // DISALLOW_REMOVE_MANAGED_PROFILE was set by the system, not the PO, so the profile is
+        // not wiped.
+        verify(mContext.userManagerInternal, never())
+                .removeUserEvenWhenDisallowed(anyInt());
+        verifyZeroInteractions(mContext.recoverySystem);
+    }
+
+    public void testMaximumFailedPasswordAttemptsReachedDeviceOwner() throws Exception {
+        setDeviceOwner();
+        when(mContext.userManager.getUserRestrictionSource(
+                UserManager.DISALLOW_FACTORY_RESET,
+                UserHandle.SYSTEM))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+
+        dpm.setMaximumFailedPasswordsForWipe(admin1, 3);
+
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+
+        // The device should be wiped even if DISALLOW_FACTORY_RESET is enabled, because both the
+        // user restriction and the policy were set by the DO.
+        verify(mContext.recoverySystem).rebootWipeUserData(
+                /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true));
+    }
+
+    public void testMaximumFailedPasswordAttemptsReachedDeviceOwnerDisallowed() throws Exception {
+        setDeviceOwner();
+        when(mContext.userManager.getUserRestrictionSource(
+                UserManager.DISALLOW_FACTORY_RESET,
+                UserHandle.SYSTEM))
+                .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
+
+        dpm.setMaximumFailedPasswordsForWipe(admin1, 3);
+
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+
+        // DISALLOW_FACTORY_RESET was set by the system, not the DO, so the device is not wiped.
+        verifyZeroInteractions(mContext.recoverySystem);
+        verify(mContext.userManagerInternal, never())
+                .removeUserEvenWhenDisallowed(anyInt());
+    }
+
+    public void testGetPermissionGrantState() throws Exception {
+        final String permission = "some.permission";
+        final String app1 = "com.example.app1";
+        final String app2 = "com.example.app2";
+
+        when(mContext.ipackageManager.checkPermission(eq(permission), eq(app1), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        doReturn(PackageManager.FLAG_PERMISSION_POLICY_FIXED).when(mContext.packageManager)
+                .getPermissionFlags(permission, app1, UserHandle.SYSTEM);
+        when(mContext.packageManager.getPermissionFlags(permission, app1,
+                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE)))
+                .thenReturn(PackageManager.FLAG_PERMISSION_POLICY_FIXED);
+        when(mContext.ipackageManager.checkPermission(eq(permission), eq(app2), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        doReturn(0).when(mContext.packageManager).getPermissionFlags(permission, app2,
+                UserHandle.SYSTEM);
+        when(mContext.packageManager.getPermissionFlags(permission, app2,
+                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(0);
+
+        // System can retrieve permission grant state.
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
+                dpm.getPermissionGrantState(null, app1, permission));
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT,
+                dpm.getPermissionGrantState(null, app2, permission));
+
+        // A regular app cannot retrieve permission grant state.
+        mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        try {
+            dpm.getPermissionGrantState(null, app1, permission);
+            fail("Didn't throw IllegalStateException");
+        } catch (IllegalStateException expected) {
+        }
+
+        // Profile owner can retrieve permission grant state.
+        setAsProfileOwner(admin1);
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
+                dpm.getPermissionGrantState(admin1, app1, permission));
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT,
+                dpm.getPermissionGrantState(admin1, app2, permission));
+    }
+
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
         when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                 userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
@@ -3219,6 +3608,21 @@
                 dpm.isProvisioningAllowed(action));
     }
 
+    private void assertProvisioningAllowed(String action, boolean expected, String packageName,
+            int uid) {
+        String previousPackageName = mContext.packageName;
+        int previousUid = mMockContext.binder.callingUid;
+
+        // Call assertProvisioningAllowed with the packageName / uid passed as arguments.
+        mContext.packageName = packageName;
+        mMockContext.binder.callingUid = uid;
+        assertProvisioningAllowed(action, expected);
+
+        // Set the previous package name / calling uid to go back to the initial state.
+        mContext.packageName = previousPackageName;
+        mMockContext.binder.callingUid = previousUid;
+    }
+
     private void assertCheckProvisioningPreCondition(String action, int provisioningCondition) {
         assertCheckProvisioningPreCondition(action, admin1.getPackageName(), provisioningCondition);
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 44bf547..22cd135 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -55,6 +55,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -99,6 +100,10 @@
      */
     public static final int SYSTEM_PID = 11111;
 
+    public static final String ANOTHER_PACKAGE_NAME = "com.another.package.name";
+
+    public static final int ANOTHER_UID = UserHandle.getUid(UserHandle.USER_SYSTEM, 18434);
+
     public static class MockBinder {
         public int callingUid = CALLER_UID;
         public int callingPid = CALLER_PID;
@@ -154,6 +159,12 @@
         }
     }
 
+    public static class RecoverySystemForMock {
+        public void rebootWipeUserData(
+                boolean shutdown, String reason, boolean force) throws IOException {
+        }
+    }
+
     public static class SystemPropertiesForMock {
         public boolean getBoolean(String key, boolean def) {
             return false;
@@ -263,6 +274,7 @@
     public final UserManagerForMock userManagerForMock;
     public final PowerManagerForMock powerManager;
     public final PowerManagerInternal powerManagerInternal;
+    public final RecoverySystemForMock recoverySystem;
     public final NotificationManager notificationManager;
     public final IIpConnectivityMetrics iipConnectivityMetrics;
     public final IWindowManager iwindowManager;
@@ -308,6 +320,7 @@
         packageManagerInternal = mock(PackageManagerInternal.class);
         powerManager = mock(PowerManagerForMock.class);
         powerManagerInternal = mock(PowerManagerInternal.class);
+        recoverySystem = mock(RecoverySystemForMock.class);
         notificationManager = mock(NotificationManager.class);
         iipConnectivityMetrics = mock(IIpConnectivityMetrics.class);
         iwindowManager = mock(IWindowManager.class);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 8a11976..ed6779c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -45,6 +45,7 @@
     public ComponentName admin1;
     public ComponentName admin2;
     public ComponentName admin3;
+    public ComponentName adminAnotherPackage;
     public ComponentName adminNoPerm;
 
     @Override
@@ -59,6 +60,8 @@
         admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
         admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
         admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
+        adminAnotherPackage = new ComponentName(DpmMockContext.ANOTHER_PACKAGE_NAME,
+                "whatever.random.class");
         adminNoPerm = new ComponentName(mRealTestContext, DummyDeviceAdmins.AdminNoPerm.class);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 228b8e0..9835c88 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -566,6 +566,7 @@
     protected Map<String, PackageInfo> mInjectedPackages;
 
     protected Set<PackageWithUser> mUninstalledPackages;
+    protected Set<PackageWithUser> mEphemeralPackages;
     protected Set<String> mSystemPackages;
 
     protected PackageManager mMockPackageManager;
@@ -618,9 +619,16 @@
     protected static final UserInfo USER_INFO_11 =
             new UserInfo(USER_11, "user11", UserInfo.FLAG_INITIALIZED);
 
+    /*
+     * Cheat: USER_P0 is a sub profile of USER_0, but it doesn't have the MANAGED_PROFILE flag set.
+     * Due to a change made to LauncherApps (b/34340531), work profile apps a no longer able
+     * to see the main profile, which would break tons of unit tests.  We avoid it by not setting
+     * MANAGED_PROFILE for P0.
+     * We cover this negative case in CTS. (i.e. CTS has tests to make sure maanged profile
+     * can't access main profile's shortcuts.)
+     */
     protected static final UserInfo USER_INFO_P0 = withProfileGroupId(
-            new UserInfo(USER_P0, "userP0",
-                    UserInfo.FLAG_MANAGED_PROFILE), 0);
+            new UserInfo(USER_P0, "userP0", UserInfo.FLAG_INITIALIZED), 0);
 
     protected BiPredicate<String, Integer> mDefaultLauncherChecker =
             (callingPackage, userId) ->
@@ -724,6 +732,7 @@
 
         mUninstalledPackages = new HashSet<>();
         mSystemPackages = new HashSet<>();
+        mEphemeralPackages = new HashSet<>();
 
         mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
 
@@ -1027,6 +1036,9 @@
         if (mUninstalledPackages.contains(PackageWithUser.of(userId, packageName))) {
             ret.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
         }
+        if (mEphemeralPackages.contains(PackageWithUser.of(userId, packageName))) {
+            ret.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_EPHEMERAL;
+        }
         if (mSystemPackages.contains(packageName)) {
             ret.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
index 5ab9020..b5a6178 100644
--- a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
@@ -80,11 +80,19 @@
     }
 
     public void testGetAppSize() throws Exception {
+        int[] appIds = null;
+
         final PackageManager pm = getContext().getPackageManager();
         for (ApplicationInfo app : pm.getInstalledApplications(0)) {
             final int userId = UserHandle.getUserId(app.uid);
             final int appId = UserHandle.getAppId(app.uid);
 
+            if (ArrayUtils.contains(appIds, appId)) {
+                continue;
+            } else {
+                appIds = ArrayUtils.appendInt(appIds, appId);
+            }
+
             final String[] packageNames = pm.getPackagesForUid(app.uid);
             final long[] ceDataInodes = new long[packageNames.length];
             final String[] codePaths = new String[packageNames.length];
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 64c5622..fec3267 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -39,7 +39,8 @@
     public PackageSetting generateFakePackageSetting(String name) {
         return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
                 new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
-                "", 1, 0, 0, null, null, 0 /*sharedUserId*/);
+                "", 1, 0, 0, null, null, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java
index 5c552a2..e6b4540f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerPresubmitTest.java
@@ -20,7 +20,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionInfo;
-import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.GlobalPresubmit;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -63,7 +63,7 @@
      */
     @Test
     @SmallTest
-    @Presubmit
+    @GlobalPresubmit
     public void testPrivAppPermissions() throws PackageManager.NameNotFoundException {
         List<PackageInfo> installedPackages = mPackageManager
                 .getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES | GET_PERMISSIONS);
@@ -103,12 +103,10 @@
                 // if privapp permissions are enforced, platform permissions must be whitelisted
                 // in SystemConfig
                 if (platformPermission && RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
-                    assertTrue("Permission " + pName
-                                    + " should be declared in "
-                                    + "/etc/permissions/privapp-permissions-platform.xml "
-                                    + "or privapp-permissions-<device>.xml file for package "
+                    assertTrue("Permission " + pName + " should be declared in "
+                                    + "privapp-permissions-<category>.xml file for package "
                                     + packageName,
-                            privAppPermissions.contains(pName));
+                            privAppPermissions != null && privAppPermissions.contains(pName));
                 }
                 assertTrue("Permission " + pName + " should be granted to " + packageName, granted);
             }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index bd2bb6c..baf60c5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -216,7 +216,9 @@
                 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
                 PARENT_PACKAGE_NAME,
                 childPackageNames,
-                0);
+                0,
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01);
         verifySettingCopy(origPkgSetting01, testPkgSetting01);
     }
@@ -241,7 +243,9 @@
                 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
                 PARENT_PACKAGE_NAME,
                 childPackageNames,
-                0);
+                0,
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         final PackageSetting testPkgSetting01 = new PackageSetting(
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
@@ -256,7 +260,9 @@
                 0 /*pkgPrivateFlags*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
-                0);
+                0,
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         testPkgSetting01.copyFrom(origPkgSetting01);
         verifySettingCopy(origPkgSetting01, testPkgSetting01);
     }
@@ -281,7 +287,9 @@
                 0 /*pkgFlags*/,
                 0 /*pkgPrivateFlags*/,
                 null /*childPkgNames*/,
-                UserManagerService.getInstance());
+                UserManagerService.getInstance(),
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
         assertThat(testPkgSetting01.origPackage, is(nullValue()));
@@ -313,7 +321,9 @@
                 ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
                 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
                 null /*childPkgNames*/,
-                UserManagerService.getInstance());
+                UserManagerService.getInstance(),
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
         assertThat(testPkgSetting01.origPackage, is(nullValue()));
@@ -348,7 +358,9 @@
                     0 /*pkgFlags*/,
                     0 /*pkgPrivateFlags*/,
                     null /*childPkgNames*/,
-                    UserManagerService.getInstance());
+                    UserManagerService.getInstance(),
+                    null /*usesStaticLibraries*/,
+                    null /*usesStaticLibrariesVersions*/);
             fail("Expected a PackageManagerException");
         } catch (PackageManagerException expected) {
         }
@@ -378,7 +390,9 @@
                 false /*allowInstall*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
-                UserManagerService.getInstance());
+                UserManagerService.getInstance(),
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
@@ -416,7 +430,9 @@
                 true /*allowInstall*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
-                UserManagerService.getInstance());
+                UserManagerService.getInstance(),
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         assertThat(testPkgSetting01.appId, is(0));
         assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -457,7 +473,9 @@
                 false /*allowInstall*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
-                UserManagerService.getInstance());
+                UserManagerService.getInstance(),
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         assertThat(testPkgSetting01.appId, is(10064));
         assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -498,7 +516,9 @@
                 false /*allowInstall*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
-                UserManagerService.getInstance());
+                UserManagerService.getInstance(),
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
         assertThat(testPkgSetting01.appId, is(10064));
         assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
@@ -624,7 +644,9 @@
                 0 /*privateFlags*/,
                 null /*parentPackageName*/,
                 null /*childPackageNames*/,
-                sharedUserId);
+                sharedUserId,
+                null /*usesStaticLibraries*/,
+                null /*usesStaticLibrariesVersions*/);
     }
 
     private @NonNull List<UserInfo> createFakeUsers() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
new file mode 100644
index 0000000..ca1e6af
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
+        .assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.os.Process;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.mockito.Mockito.*;
+
+/**
+ * Tests for {@link ShortcutManager#createShortcutResultIntent(ShortcutInfo)} and relevant APIs.
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest10 \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+public class ShortcutManagerTest10 extends BaseShortcutManagerTest {
+
+    private PinItemRequest mRequest;
+
+    private PinItemRequest verifyAndGetCreateShortcutResult(Intent resultIntent) {
+        PinItemRequest request = mLauncherApps.getPinItemRequest(resultIntent);
+        assertNotNull(request);
+        assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, request.getRequestType());
+        return request;
+    }
+
+    public void testCreateShortcutResult_noDefaultLauncher() {
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            assertNull(mManager.createShortcutResultIntent(s1));
+        });
+    }
+
+    public void testCreateShortcutResult_validResult() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            Intent intent = mManager.createShortcutResultIntent(s1);
+            mRequest = verifyAndGetCreateShortcutResult(intent);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(mRequest.isValid());
+            assertTrue(mRequest.accept());
+        });
+    }
+
+    public void testCreateShortcutResult_alreadyPinned() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            Intent intent = mManager.createShortcutResultIntent(s1);
+            mRequest = verifyAndGetCreateShortcutResult(intent);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(mRequest.isValid());
+            assertTrue(mRequest.getShortcutInfo().isPinned());
+            assertTrue(mRequest.accept());
+        });
+    }
+
+    public void testCreateShortcutResult_alreadyPinnedByAnother() {
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+        });
+
+        // Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            Intent intent = mManager.createShortcutResultIntent(s1);
+            mRequest = verifyAndGetCreateShortcutResult(intent);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(mRequest.isValid());
+            assertFalse(mRequest.getShortcutInfo().isPinned());
+            assertTrue(mRequest.accept());
+        });
+    }
+
+    public void testCreateShortcutResult_defaultLauncherChanges() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            Intent intent = mManager.createShortcutResultIntent(s1);
+            mRequest = verifyAndGetCreateShortcutResult(intent);
+        });
+
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_2, USER_0));
+        // Verify that other launcher can't use this request
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertFalse(mRequest.isValid());
+            assertExpectException(SecurityException.class, "Calling uid mismatch",
+                    mRequest::accept);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Set some random caller UID.
+            mInjectedCallingUid = 12345;
+
+            assertFalse(mRequest.isValid());
+            assertExpectException(SecurityException.class, "Calling uid mismatch",
+                    mRequest::accept);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(mRequest.isValid());
+            assertTrue(mRequest.accept());
+        });
+    }
+
+    private LauncherActivityInfo setupMockActivityInfo() {
+        doReturn(getTestContext().getPackageName()).when(mServiceContext).getPackageName();
+        doReturn(getTestContext().getContentResolver()).when(mServiceContext).getContentResolver();
+
+        LauncherActivityInfo info = mock(LauncherActivityInfo.class);
+        when(info.getComponentName()).thenReturn(
+                new ComponentName(getTestContext(), "a.ShortcutConfigActivity"));
+        when(info.getUser()).thenReturn(Process.myUserHandle());
+        return info;
+    }
+
+    public void testStartConfigActivity_defaultLauncher() {
+        LauncherActivityInfo info = setupMockActivityInfo();
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        runWithCaller(LAUNCHER_1, USER_0, () ->
+            assertNotNull(mLauncherApps.getShortcutConfigActivityIntent(info))
+        );
+    }
+
+    public void testStartConfigActivity_nonDefaultLauncher() {
+        LauncherActivityInfo info = setupMockActivityInfo();
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        runWithCaller(LAUNCHER_2, USER_0, () ->
+            assertExpectException(SecurityException.class, null, () ->
+                    mLauncherApps.getShortcutConfigActivityIntent(info))
+        );
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index d25923c..562de414 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -46,6 +46,7 @@
 
 import com.android.frameworks.servicestests.R;
 import com.android.server.pm.ShortcutService.ConfigConstants;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
 
 import java.io.File;
 import java.io.FileWriter;
@@ -2037,4 +2038,32 @@
         assertFalse(mService.isUserUnlockedL(USER_0));
         assertFalse(mService.isUserUnlockedL(USER_10));
     }
+
+    public void testEphemeralApp() {
+        mRunningUsers.put(USER_10, true); // this test needs user 10.
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertWith(mManager.getDynamicShortcuts()).isEmpty();
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertWith(mManager.getDynamicShortcuts()).isEmpty();
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertWith(mManager.getDynamicShortcuts()).isEmpty();
+        });
+        // Make package 1 ephemeral.
+        mEphemeralPackages.add(PackageWithUser.of(USER_0, CALLING_PACKAGE_1));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertExpectException(IllegalStateException.class, "Ephemeral apps", () -> {
+                mManager.getDynamicShortcuts();
+            });
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertWith(mManager.getDynamicShortcuts()).isEmpty();
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertWith(mManager.getDynamicShortcuts()).isEmpty();
+        });
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index bcd72fc..96e8948 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -22,6 +22,9 @@
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Matchers.notNull;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -238,7 +241,7 @@
      * - Launcher supports the feature.
      * - Shortcut doesn't pre-exist.
      */
-    private void checkRequestPinShortcut(@Nullable PendingIntent resultIntent) {
+    private void checkRequestPinShortcut(@Nullable IntentSender resultIntent) {
         setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
         setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
 
@@ -254,8 +257,7 @@
 
             assertNull(s.getActivity());
 
-            assertTrue(mManager.requestPinShortcut(s,
-                    resultIntent == null ? null : resultIntent.getIntentSender()));
+            assertTrue(mManager.requestPinShortcut(s, resultIntent));
 
             verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
 
@@ -294,9 +296,9 @@
 
         // This method is always called, even with PI == null.
         if (resultIntent == null) {
-            verify(mServiceContext, times(1)).sendIntentSender(eq(null));
+            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
         } else {
-            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
+            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
         }
 
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -314,11 +316,12 @@
         checkRequestPinShortcut(/* resultIntent=*/ null);
     }
 
-    public void testRequestPinShortcut_withCallback() {
-        final PendingIntent resultIntent =
-                PendingIntent.getActivity(getTestContext(), 0, new Intent(), 0);
+    private IntentSender makeResultIntent() {
+        return PendingIntent.getActivity(getTestContext(), 0, new Intent(), 0).getIntentSender();
+    }
 
-        checkRequestPinShortcut(resultIntent);
+    public void testRequestPinShortcut_withCallback() {
+        checkRequestPinShortcut(makeResultIntent());
     }
 
     public void testRequestPinShortcut_explicitTargetActivity() {
@@ -578,8 +581,15 @@
     public void testRequestPinShortcut_dynamicExists_alreadyPinned() {
         setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
 
+        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
+
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
-            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+            final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, "s1")
+                    .setShortLabel("Title-" + "s1")
+                    .setIcon(res32x32)
+                    .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class));
+            final ShortcutInfo s = b.build();
+            assertTrue(mManager.setDynamicShortcuts(list(s)));
         });
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
@@ -590,14 +600,66 @@
             assertWith(getCallerShortcuts())
                     .haveIds("s1")
                     .areAllDynamic()
-                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
+                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
                     .areAllPinned();
 
             assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
-                    /* resultIntent=*/ null));
+                    makeResultIntent()));
 
             // The intent should be sent right away.
-            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
+            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
+        });
+
+        // Already pinned.
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllEnabled()
+                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
+                    .areAllPinned();
+        });
+
+        // ... But the launcher will still receive the request.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllPinned() // Note it's pinned already.
+                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
+                    .areAllWithNoIntent();
+
+            assertAllHaveIcon(list(request.getShortcutInfo()));
+
+            reset(mServiceContext);
+
+            // Accept the request.
+            assertTrue(request.accept());
+
+            // The intent is only sent once, so times(1).
+            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
+        });
+
+        // Still pinned.
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllEnabled()
+                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
+                    .areAllPinned();
         });
     }
 
@@ -621,10 +683,65 @@
                     .areAllPinned();
 
             assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
-                    /* resultIntent=*/ null));
+                    makeResultIntent()));
 
             // The intent should be sent right away.
-            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
+            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
+        });
+
+        // Already pinned.
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllEnabled()
+                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+                            ShortcutActivity.class.getName()))
+                    .areAllPinned();
+        });
+
+        // ... But the launcher will still receive the request.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllPinned() // Note it's pinned already.
+                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+                            ShortcutActivity.class.getName()))
+                    .areAllWithNoIntent();
+
+            assertAllHaveIcon(list(request.getShortcutInfo()));
+
+            reset(mServiceContext);
+
+            // Accept the request.
+            assertTrue(request.accept());
+
+            // The intent is only sent once, so times(1).
+            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
+        });
+
+        // Still pinned.
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllEnabled()
+                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
+                            ShortcutActivity.class.getName()))
+                    .areAllPinned();
         });
     }
 
@@ -1421,6 +1538,35 @@
         });
     }
 
+    public void testRequestPinShortcut_wrongLauncherCannotAccept() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            assertTrue(mManager.requestPinShortcut(s1, null));
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+
+        final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+        verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+        final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+        // Verify that other launcher can't use this request
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Set some random caller UID.
+            mInjectedCallingUid = 12345;
+
+            assertFalse(request.isValid());
+            assertExpectException(SecurityException.class, "Calling uid mismatch", request::accept);
+        });
+
+        // The default launcher can still use this request
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(request.isValid());
+            assertTrue(request.accept());
+        });
+    }
+
     // TODO More tests:
 
     // Cancel previous pending request and release memory?
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
index 84a7c19..cfa35c2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest9.java
@@ -83,7 +83,8 @@
     private void assertPinItemRequest(PinItemRequest actualRequest, String className) {
         assertNotNull(actualRequest);
         assertEquals(PinItemRequest.REQUEST_TYPE_APPWIDGET, actualRequest.getRequestType());
-        assertEquals(className, actualRequest.getAppWidgetProviderInfo().provider.getClassName());
+        assertEquals(className, actualRequest.getAppWidgetProviderInfo(getTestContext())
+                .provider.getClassName());
     }
 
     public void testNotForeground() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 9b2c94e..1964cad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -177,10 +177,12 @@
         UserInfo userInfo = createProfileForUser("Profile",
                 UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
         assertNotNull(userInfo);
-
+        assertNull(mUserManager.getProfileParent(primaryUserId));
         UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id);
         assertNotNull(parentProfileInfo);
         assertEquals(parentProfileInfo.id, primaryUserId);
+        removeUser(userInfo.id);
+        assertNull(mUserManager.getProfileParent(primaryUserId));
     }
 
     // Make sure only one managed profile can be created
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 11f9ebb..480be2e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -16,13 +16,16 @@
 
 package com.android.server.pm;
 
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.server.devicepolicy.DpmTestUtils;
+import android.util.SparseArray;
 
 /**
  * Tests for {@link com.android.server.pm.UserRestrictionsUtils}.
@@ -49,14 +52,14 @@
     public void testIsEmpty() {
         assertTrue(UserRestrictionsUtils.isEmpty(null));
         assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
-        assertFalse(UserRestrictionsUtils.isEmpty(DpmTestUtils.newRestrictions("a")));
+        assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
     }
 
     public void testClone() {
         Bundle in = new Bundle();
         Bundle out = UserRestrictionsUtils.clone(in);
         assertNotSame(in, out);
-        DpmTestUtils.assertRestrictions(out, new Bundle());
+        assertRestrictions(out, new Bundle());
 
         out = UserRestrictionsUtils.clone(null);
         assertNotNull(out);
@@ -64,16 +67,16 @@
     }
 
     public void testMerge() {
-        Bundle a = DpmTestUtils.newRestrictions("a", "d");
-        Bundle b = DpmTestUtils.newRestrictions("b", "d", "e");
+        Bundle a = newRestrictions("a", "d");
+        Bundle b = newRestrictions("b", "d", "e");
 
         UserRestrictionsUtils.merge(a, b);
 
-        DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions("a", "b", "d", "e"), a);
+        assertRestrictions(newRestrictions("a", "b", "d", "e"), a);
 
         UserRestrictionsUtils.merge(a, null);
 
-        DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions("a", "b", "d", "e"), a);
+        assertRestrictions(newRestrictions("a", "b", "d", "e"), a);
 
         try {
             UserRestrictionsUtils.merge(a, a);
@@ -114,25 +117,32 @@
         final Bundle local = new Bundle();
         final Bundle global = new Bundle();
 
-        UserRestrictionsUtils.sortToGlobalAndLocal(null, global, local);
+        UserRestrictionsUtils.sortToGlobalAndLocal(null, false /* isDeviceOwner */,
+                UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
         assertEquals(0, global.size());
         assertEquals(0, local.size());
 
-        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, global, local);
+        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false /* isDeviceOwner */,
+                UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
         assertEquals(0, global.size());
         assertEquals(0, local.size());
 
-        UserRestrictionsUtils.sortToGlobalAndLocal(DpmTestUtils.newRestrictions(
+        // Restrictions set by DO.
+        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(
                 UserManager.DISALLOW_ADJUST_VOLUME,
                 UserManager.DISALLOW_UNMUTE_MICROPHONE,
                 UserManager.DISALLOW_USB_FILE_TRANSFER,
                 UserManager.DISALLOW_CONFIG_TETHERING,
                 UserManager.DISALLOW_OUTGOING_BEAM,
-                UserManager.DISALLOW_APPS_CONTROL
-        ), global, local);
+                UserManager.DISALLOW_APPS_CONTROL,
+                UserManager.ENSURE_VERIFY_APPS
+        ), true /* isDeviceOwner */, UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
 
 
-        DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(
+        assertRestrictions(newRestrictions(
+                // This one is global no matter who sets it.
+                UserManager.ENSURE_VERIFY_APPS,
+
                 // These can be set by PO too, but when DO sets them, they're global.
                 UserManager.DISALLOW_ADJUST_VOLUME,
                 UserManager.DISALLOW_UNMUTE_MICROPHONE,
@@ -142,11 +152,117 @@
                 UserManager.DISALLOW_CONFIG_TETHERING
         ), global);
 
-        DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(
+        assertRestrictions(newRestrictions(
                 // They can be set by both DO/PO.
                 UserManager.DISALLOW_OUTGOING_BEAM,
                 UserManager.DISALLOW_APPS_CONTROL
         ), local);
+
+        local.clear();
+        global.clear();
+
+        // Restrictions set by PO.
+        UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(
+                UserManager.DISALLOW_ADJUST_VOLUME,
+                UserManager.DISALLOW_UNMUTE_MICROPHONE,
+                UserManager.DISALLOW_USB_FILE_TRANSFER,
+                UserManager.DISALLOW_CONFIG_TETHERING,
+                UserManager.DISALLOW_OUTGOING_BEAM,
+                UserManager.DISALLOW_APPS_CONTROL,
+                UserManager.ENSURE_VERIFY_APPS
+        ), false /* isDeviceOwner */, UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
+
+        assertRestrictions(newRestrictions(
+                // This one is global no matter who sets it.
+                UserManager.ENSURE_VERIFY_APPS
+        ), global);
+
+        assertRestrictions(newRestrictions(
+                // These can be set by PO too, but when PO sets them, they're local.
+                UserManager.DISALLOW_ADJUST_VOLUME,
+                UserManager.DISALLOW_UNMUTE_MICROPHONE,
+
+                // They can be set by both DO/PO.
+                UserManager.DISALLOW_OUTGOING_BEAM,
+                UserManager.DISALLOW_APPS_CONTROL,
+
+                // These can only be set by DO.
+                UserManager.DISALLOW_USB_FILE_TRANSFER,
+                UserManager.DISALLOW_CONFIG_TETHERING
+        ), local);
+
+    }
+
+    public void testSortToLocalAndGlobalWithCameraDisabled() {
+        final Bundle local = new Bundle();
+        final Bundle global = new Bundle();
+
+        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false,
+                UserManagerInternal.CAMERA_DISABLED_GLOBALLY, global, local);
+        assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), global);
+        assertEquals(0, local.size());
+        global.clear();
+
+        UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false,
+                UserManagerInternal.CAMERA_DISABLED_LOCALLY, global, local);
+        assertEquals(0, global.size());
+        assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), local);
+    }
+
+    public void testMergeAll() {
+        SparseArray<Bundle> restrictions = new SparseArray<>();
+        assertNull(UserRestrictionsUtils.mergeAll(restrictions));
+
+        restrictions.put(0, newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME));
+        restrictions.put(1, newRestrictions(UserManager.DISALLOW_USB_FILE_TRANSFER));
+        restrictions.put(2, newRestrictions(UserManager.DISALLOW_APPS_CONTROL));
+
+        Bundle result = UserRestrictionsUtils.mergeAll(restrictions);
+        assertRestrictions(
+                newRestrictions(
+                        UserManager.DISALLOW_ADJUST_VOLUME,
+                        UserManager.DISALLOW_USB_FILE_TRANSFER,
+                        UserManager.DISALLOW_APPS_CONTROL),
+                result);
+    }
+
+    public void testMoveRestriction() {
+        SparseArray<Bundle> localRestrictions = new SparseArray<>();
+        SparseArray<Bundle> globalRestrictions = new SparseArray<>();
+
+        // User 0 has only local restrictions, nothing should change.
+        localRestrictions.put(0, newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME));
+        // User 1 has a local restriction to be moved to global and some global already. Local
+        // restrictions should be removed for this user.
+        localRestrictions.put(1, newRestrictions(UserManager.ENSURE_VERIFY_APPS));
+        globalRestrictions.put(1, newRestrictions(UserManager.DISALLOW_ADD_USER));
+        // User 2 has a local restriction to be moved and one to leave local.
+        localRestrictions.put(2, newRestrictions(
+                UserManager.ENSURE_VERIFY_APPS,
+                UserManager.DISALLOW_CONFIG_VPN));
+
+        UserRestrictionsUtils.moveRestriction(
+                UserManager.ENSURE_VERIFY_APPS, localRestrictions, globalRestrictions);
+
+        // Check user 0.
+        assertRestrictions(
+                newRestrictions(UserManager.DISALLOW_ADJUST_VOLUME),
+                localRestrictions.get(0));
+        assertNull(globalRestrictions.get(0));
+
+        // Check user 1.
+        assertNull(localRestrictions.get(1));
+        assertRestrictions(
+                newRestrictions(UserManager.ENSURE_VERIFY_APPS, UserManager.DISALLOW_ADD_USER),
+                globalRestrictions.get(1));
+
+        // Check user 2.
+        assertRestrictions(
+                newRestrictions(UserManager.DISALLOW_CONFIG_VPN),
+                localRestrictions.get(2));
+        assertRestrictions(
+                newRestrictions(UserManager.ENSURE_VERIFY_APPS),
+                globalRestrictions.get(2));
     }
 
     public void testAreEqual() {
@@ -172,33 +288,33 @@
 
         assertFalse(UserRestrictionsUtils.areEqual(
                 null,
-                DpmTestUtils.newRestrictions("a")));
+                newRestrictions("a")));
 
         assertFalse(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("a"),
+                newRestrictions("a"),
                 null));
 
         assertTrue(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("a"),
-                DpmTestUtils.newRestrictions("a")));
+                newRestrictions("a"),
+                newRestrictions("a")));
 
         assertFalse(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("a"),
-                DpmTestUtils.newRestrictions("a", "b")));
+                newRestrictions("a"),
+                newRestrictions("a", "b")));
 
         assertFalse(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("a", "b"),
-                DpmTestUtils.newRestrictions("a")));
+                newRestrictions("a", "b"),
+                newRestrictions("a")));
 
         assertFalse(UserRestrictionsUtils.areEqual(
-                DpmTestUtils.newRestrictions("b", "a"),
-                DpmTestUtils.newRestrictions("a", "a")));
+                newRestrictions("b", "a"),
+                newRestrictions("a", "a")));
 
         // Make sure false restrictions are handled correctly.
-        final Bundle a = DpmTestUtils.newRestrictions("a");
+        final Bundle a = newRestrictions("a");
         a.putBoolean("b", true);
 
-        final Bundle b = DpmTestUtils.newRestrictions("a");
+        final Bundle b = newRestrictions("a");
         b.putBoolean("b", false);
 
         assertFalse(UserRestrictionsUtils.areEqual(a, b));
diff --git a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java b/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
new file mode 100644
index 0000000..e2aff16
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.widget.Toast;
+import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.policy.AccessibilityShortcutController.FrameworkObjectProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.internal.util.reflection.Whitebox;
+
+import java.util.Collections;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityShortcutControllerTest {
+    private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
+
+    private @Mock Context mContext;
+    private @Mock FrameworkObjectProvider mFrameworkObjectProvider;
+    private @Mock IAccessibilityManager mAccessibilityManagerService;
+    private @Mock Handler mHandler;
+    private @Mock AlertDialog.Builder mAlertDialogBuilder;
+    private @Mock AlertDialog mAlertDialog;
+    private @Mock AccessibilityServiceInfo mServiceInfo;
+    private @Mock Resources mResources;
+    private @Mock Toast mToast;
+
+    private MockContentResolver mContentResolver;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContentResolver = new MockContentResolver(mContext);
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mContext.getResources()).thenReturn(mResources);
+
+        when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt()))
+                .thenReturn(Collections.singletonList(mServiceInfo));
+
+        // Use the extra level of indirection in the object to mock framework objects
+        AccessibilityManager accessibilityManager =
+                new AccessibilityManager(mHandler, mAccessibilityManagerService, 0);
+        when(mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext))
+                .thenReturn(accessibilityManager);
+        when(mFrameworkObjectProvider.getAlertDialogBuilder(mContext))
+                .thenReturn(mAlertDialogBuilder);
+        when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt()))
+                .thenReturn(mToast);
+
+        when(mResources.getString(anyInt())).thenReturn("Howdy %s");
+        ResolveInfo resolveInfo = mock(ResolveInfo.class);
+        when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name");
+        when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
+        when(mServiceInfo.getComponentName())
+                .thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING));
+
+        when(mAlertDialogBuilder.setTitle(anyInt())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setMessage(anyObject())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setPositiveButton(anyInt(), anyObject()))
+                .thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setNegativeButton(anyInt(), anyObject()))
+                .thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setOnCancelListener(anyObject())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.create()).thenReturn(mAlertDialog);
+
+        Window window = mock(Window.class);
+        Whitebox.setInternalState(window, "mWindowAttributes", new WindowManager.LayoutParams());
+        when(mAlertDialog.getWindow()).thenReturn(window);
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void testShortcutAvailable_withNullServiceIdWhenCreated_shouldReturnFalse() {
+        configureShortcutDisabled();
+        assertFalse(getController().isAccessibilityShortcutAvailable());
+    }
+
+    @Test
+    public void testShortcutAvailable_withNonNullServiceIdWhenCreated_shouldReturnTrue() {
+        configureShortcutEnabled();
+        assertTrue(getController().isAccessibilityShortcutAvailable());
+    }
+
+    @Test
+    public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
+        accessibilityShortcutController.onSettingsChanged();
+        assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable());
+    }
+
+    @Test
+    public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue() {
+        configureShortcutDisabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        configureShortcutEnabled();
+        accessibilityShortcutController.onSettingsChanged();
+        assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable());
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_firstTime_showsWarningDialog()
+            throws Exception {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        accessibilityShortcutController.performAccessibilityShortcut();
+
+        assertEquals(1, Settings.Secure.getInt(
+                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0));
+        verify(mResources).getString(R.string.accessibility_shortcut_toogle_warning);
+        verify(mAlertDialog).show();
+        verify(mAccessibilityManagerService).getInstalledAccessibilityServiceList(anyInt());
+        verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut();
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_withDialogShowing_callsServer()
+        throws Exception {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        accessibilityShortcutController.performAccessibilityShortcut();
+        accessibilityShortcutController.performAccessibilityShortcut();
+        verify(mToast).show();
+        verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_ifCanceledFirstTime_showsWarningDialog()
+        throws Exception {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        accessibilityShortcutController.performAccessibilityShortcut();
+        ArgumentCaptor<AlertDialog.OnCancelListener> cancelListenerCaptor =
+                ArgumentCaptor.forClass(AlertDialog.OnCancelListener.class);
+        verify(mAlertDialogBuilder).setOnCancelListener(cancelListenerCaptor.capture());
+        // Call the cancel callback
+        cancelListenerCaptor.getValue().onCancel(null);
+
+        accessibilityShortcutController.performAccessibilityShortcut();
+        verify(mAlertDialog, times(2)).show();
+    }
+
+    @Test
+    public void testClickingDisableButtonInDialog_shouldClearShortcutId() {
+        configureShortcutEnabled();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        getController().performAccessibilityShortcut();
+
+        ArgumentCaptor<DialogInterface.OnClickListener> captor =
+                ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
+        verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.disable_accessibility_shortcut),
+                captor.capture());
+        // Call the button callback
+        captor.getValue().onClick(null, 0);
+        assertTrue(TextUtils.isEmpty(
+                Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)));
+    }
+
+    @Test
+    public void testClickingLeaveOnButtonInDialog_shouldLeaveShortcutReady() throws Exception {
+        configureShortcutEnabled();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        getController().performAccessibilityShortcut();
+
+        ArgumentCaptor<DialogInterface.OnClickListener> captor =
+            ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
+        verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.leave_accessibility_shortcut_on),
+            captor.capture());
+        // Call the button callback, if one exists
+        if (captor.getValue() != null) {
+            captor.getValue().onClick(null, 0);
+        }
+        assertEquals(SERVICE_NAME_STRING,
+                Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
+        assertEquals(1, Settings.Secure.getInt(
+            mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN));
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception {
+        configureShortcutEnabled();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
+        getController().performAccessibilityShortcut();
+
+        verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
+        verify(mToast).show();
+        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+    }
+
+    private void configureShortcutDisabled() {
+        Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
+    }
+
+    private void configureShortcutEnabled() {
+        Settings.Secure.putString(
+                mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING);
+    }
+
+    private AccessibilityShortcutController getController() {
+        AccessibilityShortcutController accessibilityShortcutController =
+                new AccessibilityShortcutController(mContext, mHandler);
+        accessibilityShortcutController.mFrameworkObjectProvider = mFrameworkObjectProvider;
+        return accessibilityShortcutController;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
index d53fdf6..e1dda51 100644
--- a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
@@ -449,6 +449,11 @@
         }
 
         @Override
+        File getDataPreloadsFileCacheDirectory() {
+            return new File(mTestPreloadsDir, "file_cache");
+        }
+
+        @Override
         void publishLocalService(RetailDemoModeService service,
                 RetailDemoModeServiceInternal localService) {
         }
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
index 2aca702..13a3c2f 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
@@ -155,7 +155,7 @@
     }
 
     @Test
-    public void testDuplicatePackageNameIsMergedAcrossMultipleUsers() throws Exception {
+    public void testDuplicatePackageNameIsNotMergedAcrossMultipleUsers() throws Exception {
         PackageStats app = new PackageStats("com.test.app");
         app.dataSize = 1000;
         app.externalDataSize = 1000;
@@ -175,19 +175,19 @@
         logger.dumpToFile(mOutputFile);
 
         JSONObject output = getOutputFileAsJson();
-        assertThat(output.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY)).isEqualTo(2200);
-        assertThat(output.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY)).isEqualTo(22);
+        assertThat(output.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY)).isEqualTo(2000);
+        assertThat(output.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY)).isEqualTo(20);
         JSONArray packageNames = output.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
         assertThat(packageNames.length()).isEqualTo(1);
         assertThat(packageNames.getString(0)).isEqualTo("com.test.app");
 
         JSONArray appSizes = output.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
         assertThat(appSizes.length()).isEqualTo(1);
-        assertThat(appSizes.getLong(0)).isEqualTo(2200);
+        assertThat(appSizes.getLong(0)).isEqualTo(2000);
 
         JSONArray cacheSizes = output.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
         assertThat(cacheSizes.length()).isEqualTo(1);
-        assertThat(cacheSizes.getLong(0)).isEqualTo(22);
+        assertThat(cacheSizes.getLong(0)).isEqualTo(20);
     }
 
     private JSONObject getOutputFileAsJson() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index d2512ac..cd3ae1a 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -19,25 +19,41 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
+import android.content.pm.UserInfo;
+import android.webkit.UserPackage;
 import android.webkit.WebViewProviderInfo;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 public class TestSystemImpl implements SystemInterface {
     private String mUserProvider = null;
     private final WebViewProviderInfo[] mPackageConfigs;
-    HashMap<String, PackageInfo> mPackages = new HashMap();
+    List<Integer> mUsers = new ArrayList<>();
+    // Package -> [user, package]
+    Map<String, Map<Integer, PackageInfo>> mPackages = new HashMap();
     private boolean mFallbackLogicEnabled;
     private final int mNumRelros;
     private final boolean mIsDebuggable;
+    private int mMultiProcessSetting;
+    private final boolean mMultiProcessDefault;
+
+    public static final int PRIMARY_USER_ID = 0;
 
     public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
-            int numRelros, boolean isDebuggable) {
+            int numRelros, boolean isDebuggable, boolean multiProcessDefault) {
         mPackageConfigs = packageConfigs;
         mFallbackLogicEnabled = fallbackLogicEnabled;
         mNumRelros = numRelros;
         mIsDebuggable = isDebuggable;
+        mUsers.add(PRIMARY_USER_ID);
+        mMultiProcessDefault = multiProcessDefault;
+    }
+
+    public void addUser(int userId) {
+        mUsers.add(userId);
     }
 
     @Override
@@ -78,17 +94,20 @@
 
     @Override
     public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
-        enablePackageForUser(packageName, enable, 0);
+        for(int userId : mUsers) {
+            enablePackageForUser(packageName, enable, userId);
+        }
     }
 
     @Override
     public void enablePackageForUser(String packageName, boolean enable, int userId) {
-        PackageInfo packageInfo = mPackages.get(packageName);
-        if (packageInfo == null) {
+        Map<Integer, PackageInfo> userPackages = mPackages.get(packageName);
+        if (userPackages == null) {
             throw new IllegalArgumentException("There is no package called " + packageName);
         }
+        PackageInfo packageInfo = userPackages.get(userId);
         packageInfo.applicationInfo.enabled = enable;
-        setPackageInfo(packageInfo);
+        setPackageInfoForUser(userId, packageInfo);
     }
 
     @Override
@@ -97,23 +116,61 @@
     @Override
     public PackageInfo getPackageInfoForProvider(WebViewProviderInfo info) throws
             NameNotFoundException {
-        PackageInfo ret = mPackages.get(info.packageName);
+        Map<Integer, PackageInfo> userPackages = mPackages.get(info.packageName);
+        if (userPackages == null) throw new NameNotFoundException(info.packageName);
+        PackageInfo ret = userPackages.get(PRIMARY_USER_ID);
         if (ret == null) throw new NameNotFoundException(info.packageName);
         return ret;
     }
 
-    public void setPackageInfo(PackageInfo pi) {
-        mPackages.put(pi.packageName, pi);
+    @Override
+    public List<UserPackage> getPackageInfoForProviderAllUsers(
+            Context context, WebViewProviderInfo info) {
+        Map<Integer, PackageInfo> userPackages = mPackages.get(info.packageName);
+        List<UserPackage> ret = new ArrayList();
+        // Loop over defined users, and find the corresponding package for each user.
+        for (int userId : mUsers) {
+            ret.add(new UserPackage(createUserInfo(userId),
+                    userPackages == null ? null : userPackages.get(userId)));
+        }
+        return ret;
     }
 
+    private static UserInfo createUserInfo(int userId) {
+        return new UserInfo(userId, "User nr. " + userId, 0 /* flags */);
+    }
+
+    /**
+     * Set package for primary user.
+     */
+    public void setPackageInfo(PackageInfo pi) {
+        setPackageInfoForUser(PRIMARY_USER_ID, pi);
+    }
+
+    public void setPackageInfoForUser(int userId, PackageInfo pi) {
+        if (!mUsers.contains(userId)) {
+            throw new IllegalArgumentException("User nr. " + userId + " doesn't exist");
+        }
+        if (!mPackages.containsKey(pi.packageName)) {
+            mPackages.put(pi.packageName, new HashMap<Integer, PackageInfo>());
+        }
+        mPackages.get(pi.packageName).put(userId, pi);
+    }
+
+    /**
+     * Removes the package {@param packageName} for the primary user.
+     */
     public void removePackageInfo(String packageName) {
-        mPackages.remove(packageName);
+        mPackages.get(packageName).remove(PRIMARY_USER_ID);
     }
 
     @Override
     public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
         PackageInfo pi = null;
-        pi = mPackages.get(packageName);
+        Map<Integer, PackageInfo> userPackages = mPackages.get(packageName);
+        if (userPackages == null) throw new NameNotFoundException();
+
+        pi = userPackages.get(PRIMARY_USER_ID);
         if (pi != null && pi.applicationInfo.isSystemApp()) {
             return pi.applicationInfo.versionCode;
         }
@@ -121,8 +178,20 @@
     }
 
     @Override
-    public void setMultiProcessEnabledFromContext(Context context) {}
+    public int getMultiProcessSetting(Context context) {
+        return mMultiProcessSetting;
+    }
 
     @Override
-    public void registerContentObserver(Context context, ContentObserver contentObserver) {}
+    public void setMultiProcessSetting(Context context, int value) {
+        mMultiProcessSetting = value;
+    }
+
+    @Override
+    public void notifyZygote(boolean enableMultiProcess) {}
+
+    @Override
+    public boolean isMultiProcessDefaultEnabled() {
+        return mMultiProcessDefault;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 0519448..4c0f042 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -84,17 +85,30 @@
 
     private void setupWithPackages(WebViewProviderInfo[] packages,
             boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable) {
+        setupWithPackages(packages, fallbackLogicEnabled, numRelros, isDebuggable,
+                false /* multiProcessDefault */);
+    }
+
+    private void setupWithPackages(WebViewProviderInfo[] packages,
+            boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable,
+            boolean multiProcessDefault) {
         TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
-                isDebuggable);
+                isDebuggable, multiProcessDefault);
         mTestSystemImpl = Mockito.spy(testing);
         mWebViewUpdateServiceImpl =
             new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
     }
 
     private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
+        // Set package infos for the primary user (user 0).
+        setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, providers);
+    }
+
+    private void setEnabledAndValidPackageInfosForUser(int userId,
+            WebViewProviderInfo[] providers) {
         for(WebViewProviderInfo wpi : providers) {
-            mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true /* enabled */,
-                        true /* valid */, true /* installed */));
+            mTestSystemImpl.setPackageInfoForUser(userId, createPackageInfo(wpi.packageName,
+                    true /* enabled */, true /* valid */, true /* installed */));
         }
     }
 
@@ -335,7 +349,7 @@
         setEnabledAndValidPackageInfos(packages);
 
         mWebViewUpdateServiceImpl.packageStateChanged(singlePackage,
-                WebViewUpdateService.PACKAGE_ADDED, 0);
+                WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
 
         checkPreparationPhasesForPackage(singlePackage, 1 /* number of finished preparations */);
         assertEquals(singlePackage,
@@ -344,7 +358,7 @@
         // Remove the package again
         mTestSystemImpl.removePackageInfo(singlePackage);
         mWebViewUpdateServiceImpl.packageStateChanged(singlePackage,
-                WebViewUpdateService.PACKAGE_ADDED, 0);
+                WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
 
         // Package removed - ensure our interface states that there is no package
         response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
@@ -374,7 +388,7 @@
                 createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */,
                     true /* installed */));
         mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName,
-                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
 
         checkPreparationPhasesForPackage(wpi.packageName, 1);
     }
@@ -429,16 +443,8 @@
             new WebViewProviderInfo(firstPackage, "", true, false, null),
             new WebViewProviderInfo(secondPackage, "", true, false, null)};
         setupWithPackages(packages);
-        if (settingsChange) {
-            // Have all packages be enabled, so that we can change provider however we want to
-            setEnabledAndValidPackageInfos(packages);
-        } else {
-            // Have all packages be disabled so that we can change one to enabled later
-            for(WebViewProviderInfo wpi : packages) {
-                mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName,
-                            false /* enabled */, true /* valid */, true /* installed */));
-            }
-        }
+        // Have all packages be enabled, so that we can change provider however we want to
+        setEnabledAndValidPackageInfos(packages);
 
         CountDownLatch countdown = new CountDownLatch(1);
 
@@ -457,8 +463,12 @@
                     mWebViewUpdateServiceImpl.waitForAndGetProvider();
                 assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status);
                 assertEquals(secondPackage, threadResponse.packageInfo.packageName);
-                // Verify that we killed the first package
-                Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
+                // Verify that we killed the first package if we performed a settings change -
+                // otherwise we had to disable the first package, in which case its dependents
+                // should have been killed by the framework.
+                if (settingsChange) {
+                    Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
+                }
                 countdown.countDown();
             }
         }).start();
@@ -470,11 +480,21 @@
         if (settingsChange) {
             mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
         } else {
-            // Switch provider by enabling the second one
+            // Enable the second provider
             mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
                         true /* valid */, true /* installed */));
             mWebViewUpdateServiceImpl.packageStateChanged(
-                    secondPackage, WebViewUpdateService.PACKAGE_CHANGED, 0);
+                    secondPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
+
+            // Ensure we haven't changed package yet.
+            assertEquals(firstPackage,
+                    mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
+
+            // Switch provider by disabling the first one
+            mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, false /* enabled */,
+                        true /* valid */, true /* installed */));
+            mWebViewUpdateServiceImpl.packageStateChanged(
+                    firstPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
         }
         mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
         // first package done, should start on second
@@ -528,7 +548,7 @@
         mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
                         true /* valid */, true /* installed */));
         mWebViewUpdateServiceImpl.packageStateChanged(
-                fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED, 0);
+                fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
 
         if (fallbackLogicEnabled) {
             // Check that we have now disabled the fallback package twice
@@ -573,7 +593,7 @@
                 createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */,
                     true /* installed */));
         mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
-                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
 
         // Verify fallback disabled, primary package used as provider, and fallback package killed
         Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
@@ -583,7 +603,31 @@
     }
 
     @Test
-    public void testFallbackChangesEnabledState() {
+    public void testFallbackChangesEnabledStateSingleUser() {
+        for (PackageRemovalType removalType : REMOVAL_TYPES) {
+            checkFallbackChangesEnabledState(false /* multiUser */, removalType);
+        }
+    }
+
+    @Test
+    public void testFallbackChangesEnabledStateMultiUser() {
+        for (PackageRemovalType removalType : REMOVAL_TYPES) {
+            checkFallbackChangesEnabledState(true /* multiUser */, removalType);
+        }
+    }
+
+    /**
+     * Represents how to remove a package during a tests (disabling it / uninstalling it / hiding
+     * it).
+     */
+    private enum PackageRemovalType {
+        UNINSTALL, DISABLE, HIDE
+    }
+
+    private PackageRemovalType[] REMOVAL_TYPES = PackageRemovalType.class.getEnumConstants();
+
+    public void checkFallbackChangesEnabledState(boolean multiUser,
+            PackageRemovalType removalType) {
         String primaryPackage = "primary";
         String fallbackPackage = "fallback";
         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
@@ -592,46 +636,68 @@
             new WebViewProviderInfo(
                     fallbackPackage, "", true /* default available */, true /* fallback */, null)};
         setupWithPackages(packages, true /* fallbackLogicEnabled */);
-        setEnabledAndValidPackageInfos(packages);
+        int secondaryUserId = 10;
+        int userIdToChangePackageFor = multiUser ? secondaryUserId : TestSystemImpl.PRIMARY_USER_ID;
+        if (multiUser) {
+            mTestSystemImpl.addUser(secondaryUserId);
+            setEnabledAndValidPackageInfosForUser(secondaryUserId, packages);
+        }
+        setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
 
         runWebViewBootPreparationOnMainSync();
 
         // Verify fallback disabled at boot when primary package enabled
-        Mockito.verify(mTestSystemImpl).enablePackageForUser(
-                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
-                Matchers.anyInt());
+        checkEnablePackageForUserCalled(fallbackPackage, false, multiUser
+                ? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId}
+                : new int[] {TestSystemImpl.PRIMARY_USER_ID}, 1 /* numUsages */);
 
         checkPreparationPhasesForPackage(primaryPackage, 1);
 
+        boolean enabled = !(removalType == PackageRemovalType.DISABLE);
+        boolean installed = !(removalType == PackageRemovalType.UNINSTALL);
+        boolean hidden = (removalType == PackageRemovalType.HIDE);
         // Disable primary package and ensure fallback becomes enabled and used
-        mTestSystemImpl.setPackageInfo(
-                createPackageInfo(primaryPackage, false /* enabled */, true /* valid */,
-                    true /* installed */));
+        mTestSystemImpl.setPackageInfoForUser(userIdToChangePackageFor,
+                createPackageInfo(primaryPackage, enabled /* enabled */, true /* valid */,
+                    installed /* installed */, null /* signature */, 0 /* updateTime */,
+                    hidden /* hidden */));
         mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
-                WebViewUpdateService.PACKAGE_CHANGED, 0);
+                removalType == PackageRemovalType.DISABLE
+                ? WebViewUpdateService.PACKAGE_CHANGED : WebViewUpdateService.PACKAGE_REMOVED,
+                userIdToChangePackageFor); // USER ID
 
-        Mockito.verify(mTestSystemImpl).enablePackageForUser(
-                Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
-                Matchers.anyInt());
+        checkEnablePackageForUserCalled(fallbackPackage, true, multiUser
+                ? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId}
+                : new int[] {TestSystemImpl.PRIMARY_USER_ID}, 1 /* numUsages */);
 
         checkPreparationPhasesForPackage(fallbackPackage, 1);
 
 
         // Again enable primary package and verify primary is used and fallback becomes disabled
-        mTestSystemImpl.setPackageInfo(
+        mTestSystemImpl.setPackageInfoForUser(userIdToChangePackageFor,
                 createPackageInfo(primaryPackage, true /* enabled */, true /* valid */,
                     true /* installed */));
         mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
-                WebViewUpdateService.PACKAGE_CHANGED, 0);
+                removalType == PackageRemovalType.DISABLE
+                ? WebViewUpdateService.PACKAGE_CHANGED : WebViewUpdateService.PACKAGE_ADDED,
+                userIdToChangePackageFor);
 
         // Verify fallback is disabled a second time when primary package becomes enabled
-        Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
-                Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
-                Matchers.anyInt());
+        checkEnablePackageForUserCalled(fallbackPackage, false, multiUser
+                ? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId}
+                : new int[] {TestSystemImpl.PRIMARY_USER_ID}, 2 /* numUsages */);
 
         checkPreparationPhasesForPackage(primaryPackage, 2);
     }
 
+    private void checkEnablePackageForUserCalled(String packageName, boolean expectEnabled,
+            int[] userIds, int numUsages) {
+        for (int userId : userIds) {
+            Mockito.verify(mTestSystemImpl, Mockito.times(numUsages)).enablePackageForUser(
+                    Mockito.eq(packageName), Mockito.eq(expectEnabled), Mockito.eq(userId));
+        }
+    }
+
     @Test
     public void testAddUserWhenFallbackLogicEnabled() {
         checkAddingNewUser(true);
@@ -651,8 +717,10 @@
             new WebViewProviderInfo(
                     fallbackPackage, "", true /* default available */, true /* fallback */, null)};
         setupWithPackages(packages, fallbackLogicEnabled);
-        setEnabledAndValidPackageInfos(packages);
+        setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
         int newUser = 100;
+        mTestSystemImpl.addUser(newUser);
+        setEnabledAndValidPackageInfosForUser(newUser, packages);
         mWebViewUpdateServiceImpl.handleNewUser(newUser);
         if (fallbackLogicEnabled) {
             // Verify fallback package becomes disabled for new user
@@ -668,6 +736,42 @@
     }
 
     /**
+     * Ensures that adding a new user for which the current WebView package is uninstalled causes a
+     * change of WebView provider.
+     */
+    @Test
+    public void testAddingNewUserWithUninstalledPackage() {
+        String primaryPackage = "primary";
+        String fallbackPackage = "fallback";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(
+                    primaryPackage, "", true /* default available */, false /* fallback */, null),
+            new WebViewProviderInfo(
+                    fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+        setupWithPackages(packages, true /* fallbackLogicEnabled */);
+        setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
+        int newUser = 100;
+        mTestSystemImpl.addUser(newUser);
+        // Let the primary package be uninstalled for the new user
+        mTestSystemImpl.setPackageInfoForUser(newUser,
+                createPackageInfo(primaryPackage, true /* enabled */, true /* valid */,
+                        false /* installed */));
+        mTestSystemImpl.setPackageInfoForUser(newUser,
+                createPackageInfo(fallbackPackage, false /* enabled */, true /* valid */,
+                        true /* installed */));
+        mWebViewUpdateServiceImpl.handleNewUser(newUser);
+        // Verify fallback package doesn't become disabled for the primary user.
+        Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser(
+                Mockito.anyObject(), Mockito.eq(false) /* enable */,
+                Mockito.eq(TestSystemImpl.PRIMARY_USER_ID) /* user */);
+        // Verify that we enable the fallback package for the secondary user.
+        Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser(
+                Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
+                Mockito.eq(newUser) /* user */);
+        checkPreparationPhasesForPackage(fallbackPackage, 1 /* numRelros */);
+    }
+
+    /**
      * Timing dependent test where we verify that the list of valid webview packages becoming empty
      * at a certain point doesn't crash us or break our state.
      */
@@ -713,7 +817,7 @@
                     1 /* updateTime */ ));
 
         mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
-                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
 
         // Ensure we use firstPackage
         checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */);
@@ -742,14 +846,14 @@
         mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
                     false /* valid */, true /* installed */));
         mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
-                WebViewUpdateService.PACKAGE_ADDED, 0);
+                WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
         checkPreparationPhasesForPackage(firstPackage, 2 /* second time for this package */);
 
         // Now make the second package valid again and verify that it is used again
         mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
                     true /* valid */, true /* installed */));
         mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
-                WebViewUpdateService.PACKAGE_ADDED, 0);
+                WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
         checkPreparationPhasesForPackage(secondPackage, 2 /* second time for this package */);
     }
 
@@ -820,7 +924,7 @@
             mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
         } else {
             mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
-                    WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+                    WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
         }
 
         WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
@@ -831,7 +935,7 @@
                     true /* valid */, true /* installed */));
 
         mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
-                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
 
 
         checkPreparationPhasesForPackage(secondPackage, 1);
@@ -863,11 +967,11 @@
                     createPackageInfo(firstPackage, true /* enabled */, false /* valid */,
                         true /* installed */));
             mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
-                    WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+                    WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
         } else {
             mTestSystemImpl.removePackageInfo(firstPackage);
             mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
-                    WebViewUpdateService.PACKAGE_REMOVED, 0);
+                    WebViewUpdateService.PACKAGE_REMOVED, TestSystemImpl.PRIMARY_USER_ID);
         }
 
         checkPreparationPhasesForPackage(secondPackage, 1);
@@ -1098,8 +1202,10 @@
         }
     }
 
-    // Ensure that the update service uses an uninstalled package if that is the only package
-    // available.
+    /**
+     * Ensure that the update service does use an uninstalled package when that is the only
+     * package available.
+     */
     @Test
     public void testWithSingleUninstalledPackage() {
         String testPackageName = "test.package.name";
@@ -1113,21 +1219,32 @@
         runWebViewBootPreparationOnMainSync();
 
         checkPreparationPhasesForPackage(testPackageName, 1 /* first preparation phase */);
+        // TODO(gsennton) change this logic to use the code below when we have created a functional
+        // stub.
+        //Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
+        //        Matchers.anyObject());
+        //WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+        //assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+        //assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage());
     }
 
     @Test
     public void testNonhiddenPackageUserOverHidden() {
-        checkVisiblePackageUserOverNonVisible(false /* true == uninstalled, false == hidden */);
+        checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.HIDE);
+        checkVisiblePackageUserOverNonVisible(true /* multiUser*/, PackageRemovalType.HIDE);
     }
 
     @Test
     public void testInstalledPackageUsedOverUninstalled() {
-        checkVisiblePackageUserOverNonVisible(true /* true == uninstalled, false == hidden */);
+        checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.UNINSTALL);
+        checkVisiblePackageUserOverNonVisible(true /* multiUser*/, PackageRemovalType.UNINSTALL);
     }
 
-    private void checkVisiblePackageUserOverNonVisible(boolean uninstalledNotHidden) {
-        boolean testUninstalled = uninstalledNotHidden;
-        boolean testHidden = !uninstalledNotHidden;
+    private void checkVisiblePackageUserOverNonVisible(boolean multiUser,
+            PackageRemovalType removalType) {
+        assert removalType != PackageRemovalType.DISABLE;
+        boolean testUninstalled = removalType == PackageRemovalType.UNINSTALL;
+        boolean testHidden = removalType == PackageRemovalType.HIDE;
         String installedPackage = "installedPackage";
         String uninstalledPackage = "uninstalledPackage";
         WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
@@ -1137,11 +1254,25 @@
                     false /* fallback */, null)};
 
         setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
-        mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
-                    true /* valid */, true /* installed */));
-        mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
+        int secondaryUserId = 5;
+        if (multiUser) {
+            mTestSystemImpl.addUser(secondaryUserId);
+            // Install all packages for the primary user.
+            setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
+            mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(
+                    installedPackage, true /* enabled */, true /* valid */, true /* installed */));
+            // Hide or uninstall the primary package for the second user
+            mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
                     true /* valid */, (testUninstalled ? false : true) /* installed */,
                     null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
+        } else {
+            mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
+                    true /* valid */, true /* installed */));
+            // Hide or uninstall the primary package
+            mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
+                    true /* valid */, (testUninstalled ? false : true) /* installed */,
+                    null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
+        }
 
         runWebViewBootPreparationOnMainSync();
 
@@ -1160,9 +1291,7 @@
     }
 
     /**
-     * Ensure that we won't prioritize an uninstalled (or hidden) package even if it is user-chosen,
-     * and that an uninstalled (or hidden) package is not considered valid (in the
-     * getValidWebViewPackages() API).
+     * Ensure that we won't prioritize an uninstalled (or hidden) package even if it is user-chosen.
      */
     private void checkCantSwitchToNonVisiblePackage(boolean uninstalledNotHidden) {
         boolean testUninstalled = uninstalledNotHidden;
@@ -1176,27 +1305,31 @@
                     false /* fallback */, null)};
 
         setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
-        mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
-                    true /* valid */, true /* installed */));
-        mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
-                    true /* valid */, (testUninstalled ? false : true) /* installed */,
-                    null /* signatures */, 0 /* updateTime */,
-                    (testHidden ? true : false) /* hidden */));
+        int secondaryUserId = 412;
+        mTestSystemImpl.addUser(secondaryUserId);
+
+        // Let all packages be installed and enabled for the primary user.
+        setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
+        // Only uninstall the 'uninstalled package' for the secondary user.
+        mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(installedPackage,
+                true /* enabled */, true /* valid */, true /* installed */));
+        mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(uninstalledPackage,
+                true /* enabled */, true /* valid */, !testUninstalled /* installed */,
+                null /* signatures */, 0 /* updateTime */, testHidden /* hidden */));
 
         runWebViewBootPreparationOnMainSync();
 
         checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
 
-        // Ensure that only the installed package is considered valid
-        WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
-        assertEquals(1, validPackages.length);
-        assertEquals(installedPackage, validPackages[0].packageName);
-
         // ensure that we don't switch to the uninstalled package (it will be used if it becomes
         // installed later)
         assertEquals(installedPackage,
                 mWebViewUpdateServiceImpl.changeProviderAndSetting(uninstalledPackage));
 
+        // Ensure both packages are considered valid.
+        assertEquals(2, mWebViewUpdateServiceImpl.getValidWebViewPackages().length);
+
+
         // We should only have called onWebViewProviderChanged once (before calling
         // changeProviderAndSetting
         Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged(
@@ -1227,12 +1360,16 @@
                     false /* fallback */, null)};
 
         setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
-        mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
-                    true /* valid */, true /* installed */));
-        mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
-                    true /* valid */, (testUninstalled ? false : true) /* installed */,
-                    null /* signatures */, 0 /* updateTime */,
-                    (testHidden ? true : false) /* hidden */));
+        int secondaryUserId = 4;
+        mTestSystemImpl.addUser(secondaryUserId);
+
+        setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
+        mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(installedPackage,
+                true /* enabled */, true /* valid */, true /* installed */));
+        mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(uninstalledPackage,
+                true /* enabled */, true /* valid */,
+                (testUninstalled ? false : true) /* installed */, null /* signatures */,
+                0 /* updateTime */, (testHidden ? true : false) /* hidden */));
 
         // Start with the setting pointing to the uninstalled package
         mTestSystemImpl.updateUserSetting(null, uninstalledPackage);
@@ -1242,12 +1379,21 @@
         checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
     }
 
+    @Test
+    public void testFallbackEnabledIfPrimaryUninstalledSingleUser() {
+        checkFallbackEnabledIfPrimaryUninstalled(false /* multiUser */);
+    }
+
+    @Test
+    public void testFallbackEnabledIfPrimaryUninstalledMultiUser() {
+        checkFallbackEnabledIfPrimaryUninstalled(true /* multiUser */);
+    }
+
     /**
-     * Ensures that fallback becomes enabled if the primary package is uninstalled for the current
+     * Ensures that fallback becomes enabled at boot if the primary package is uninstalled for some
      * user.
      */
-    @Test
-    public void testFallbackEnabledIfPrimaryUninstalled() {
+    private void checkFallbackEnabledIfPrimaryUninstalled(boolean multiUser) {
         String primaryPackage = "primary";
         String fallbackPackage = "fallback";
         WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
@@ -1256,10 +1402,24 @@
             new WebViewProviderInfo(
                     fallbackPackage, "", true /* default available */, true /* fallback */, null)};
         setupWithPackages(packages, true /* fallback logic enabled */);
-        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+        int secondaryUserId = 5;
+        if (multiUser) {
+            mTestSystemImpl.addUser(secondaryUserId);
+            // Install all packages for the primary user.
+            setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
+            // Only install fallback package for secondary user.
+            mTestSystemImpl.setPackageInfoForUser(secondaryUserId,
+                    createPackageInfo(primaryPackage, true /* enabled */,
+                            true /* valid */, false /* installed */));
+            mTestSystemImpl.setPackageInfoForUser(secondaryUserId,
+                    createPackageInfo(fallbackPackage, false /* enabled */,
+                            true /* valid */, true /* installed */));
+        } else {
+            mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
                     true /* valid */, false /* installed */));
-        mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
+            mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, false /* enabled */,
                     true /* valid */, true /* installed */));
+        }
 
         runWebViewBootPreparationOnMainSync();
         // Verify that we enable the fallback package
@@ -1369,4 +1529,89 @@
         assertEquals(firstPackage.versionName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName);
     }
+
+    @Test
+    public void testMultiProcessEnabledByDefault() {
+        String primaryPackage = "primary";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(
+                    primaryPackage, "", true /* default available */, false /* fallback */, null)};
+        setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
+                          true /* debuggable */, true /* multiprocess by default */);
+        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+                    true /* valid */, true /* installed */, null /* signatures */,
+                    10 /* lastUpdateTime*/, false /* not hidden */, 1000 /* versionCode */,
+                    false /* isSystemApp */));
+
+        runWebViewBootPreparationOnMainSync();
+        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
+
+        // Check it's on by default
+        assertTrue(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+
+        // Test toggling it
+        mWebViewUpdateServiceImpl.enableMultiProcess(false);
+        assertFalse(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+        mWebViewUpdateServiceImpl.enableMultiProcess(true);
+        assertTrue(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+
+        // Disable, then upgrade provider, which should re-enable it
+        mWebViewUpdateServiceImpl.enableMultiProcess(false);
+        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+                    true /* valid */, true /* installed */, null /* signatures */,
+                    20 /* lastUpdateTime*/, false /* not hidden */, 2000 /* versionCode */,
+                    false /* isSystemApp */));
+        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+        checkPreparationPhasesForPackage(primaryPackage, 2);
+        assertTrue(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+    }
+
+    @Test
+    public void testMultiProcessDisabledByDefault() {
+        String primaryPackage = "primary";
+        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+            new WebViewProviderInfo(
+                    primaryPackage, "", true /* default available */, false /* fallback */, null)};
+        setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
+                          true /* debuggable */, false /* not multiprocess by default */);
+        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+                    true /* valid */, true /* installed */, null /* signatures */,
+                    10 /* lastUpdateTime*/, false /* not hidden */, 1000 /* versionCode */,
+                    false /* isSystemApp */));
+
+        runWebViewBootPreparationOnMainSync();
+        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
+
+        // Check it's off by default
+        assertFalse(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+
+        // Test toggling it
+        mWebViewUpdateServiceImpl.enableMultiProcess(true);
+        assertTrue(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+        mWebViewUpdateServiceImpl.enableMultiProcess(false);
+        assertFalse(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+
+        // Disable, then upgrade provider, which should not re-enable it
+        mWebViewUpdateServiceImpl.enableMultiProcess(false);
+        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+                    true /* valid */, true /* installed */, null /* signatures */,
+                    20 /* lastUpdateTime*/, false /* not hidden */, 2000 /* versionCode */,
+                    false /* isSystemApp */));
+        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+        checkPreparationPhasesForPackage(primaryPackage, 2);
+        assertFalse(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+
+        // Enable, then upgrade provider, which should leave it on
+        mWebViewUpdateServiceImpl.enableMultiProcess(true);
+        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+                    true /* valid */, true /* installed */, null /* signatures */,
+                    30 /* lastUpdateTime*/, false /* not hidden */, 3000 /* versionCode */,
+                    false /* isSystemApp */));
+        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+                WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+        checkPreparationPhasesForPackage(primaryPackage, 3);
+        assertTrue(mWebViewUpdateServiceImpl.isMultiProcessEnabled());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 26accc3..2af4163 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -18,12 +18,10 @@
 
 import org.junit.Test;
 
-import android.os.Binder;
-import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.IApplicationToken;
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -74,6 +72,67 @@
         controller.removeContainer(sDisplayContent.getDisplayId());
         // Assert orientation is unspecified to after container is removed.
         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
+
+        // Reset display frozen state
+        sWm.mDisplayFrozen = false;
+    }
+
+    private void assertHasStartingWindow(AppWindowToken atoken) {
+        assertNotNull(atoken.startingSurface);
+        assertNotNull(atoken.startingData);
+        assertNotNull(atoken.startingWindow);
+    }
+
+    private void assertNoStartingWindow(AppWindowToken atoken) {
+        assertNull(atoken.startingSurface);
+        assertNull(atoken.startingWindow);
+        assertNull(atoken.startingData);
+    }
+
+    @Test
+    public void testCreateRemoveStartingWindow() throws Exception {
+        final TestAppWindowContainerController controller = createAppWindowController();
+        controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
+        waitUntilHandlerIdle();
+        final AppWindowToken atoken = controller.getAppWindowToken();
+        assertHasStartingWindow(atoken);
+        controller.removeStartingWindow();
+        waitUntilHandlerIdle();
+        assertNoStartingWindow(atoken);
+    }
+
+    @Test
+    public void testTransferStartingWindow() throws Exception {
+        final TestAppWindowContainerController controller1 = createAppWindowController();
+        final TestAppWindowContainerController controller2 = createAppWindowController();
+        controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
+        waitUntilHandlerIdle();
+        controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
+                true, true, false);
+        waitUntilHandlerIdle();
+        assertNoStartingWindow(controller1.getAppWindowToken());
+        assertHasStartingWindow(controller2.getAppWindowToken());
+    }
+
+    @Test
+    public void testTransferStartingWindowWhileCreating() throws Exception {
+        final TestAppWindowContainerController controller1 = createAppWindowController();
+        final TestAppWindowContainerController controller2 = createAppWindowController();
+        sPolicy.setRunnableWhenAddingSplashScreen(() -> {
+
+            // Surprise, ...! Transfer window in the middle of the creation flow.
+            controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
+                    true, true, false);
+        });
+        controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
+        waitUntilHandlerIdle();
+        assertNoStartingWindow(controller1.getAppWindowToken());
+        assertHasStartingWindow(controller2.getAppWindowToken());
     }
 
     private TestAppWindowContainerController createAppWindowController() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 1d9875f..154fa91 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -119,6 +119,7 @@
         sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
         assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, sWm.mLastOrientation);
         assertTrue(appWindow.resizeReported);
+        appWindow.removeImmediately();
     }
 
     @Test
@@ -148,5 +149,6 @@
         sWm.updateRotation(false, false);
         sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
         assertTrue(appWindow.resizeReported);
+        appWindow.removeImmediately();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 85931e8..e54e319 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -236,7 +236,7 @@
         assertEquals(dc, token.getDisplayContent());
 
         // Move stack to first display.
-        sWm.moveStackToDisplay(stack.mStackId, sDisplayContent.getDisplayId());
+        sDisplayContent.moveStackToDisplay(stack);
         assertEquals(sDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId());
         assertEquals(sDisplayContent, stack.getParent().getParent());
         assertEquals(sDisplayContent, stack.getDisplayContent());
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
new file mode 100644
index 0000000..7a789d4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
+import android.view.Display;
+import android.view.DisplayInfo;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test class for {@link StackWindowController}.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.wm.StackWindowControllerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class StackWindowControllerTests extends WindowTestsBase {
+    @Test
+    public void testRemoveContainer() throws Exception {
+        final StackWindowController stackController =
+                createStackControllerOnDisplay(sDisplayContent);
+        final TestTaskWindowContainerController taskController =
+                new TestTaskWindowContainerController(stackController);
+
+        final TaskStack stack = stackController.mContainer;
+        final Task task = taskController.mContainer;
+        assertNotNull(stack);
+        assertNotNull(task);
+        stackController.removeContainer();
+        // Assert that the container was removed.
+        assertNull(stackController.mContainer);
+        assertNull(taskController.mContainer);
+        assertNull(stack.getDisplayContent());
+        assertNull(task.getDisplayContent());
+        assertNull(task.mStack);
+    }
+
+    @Test
+    public void testRemoveContainer_deferRemoval() throws Exception {
+        final StackWindowController stackController =
+                createStackControllerOnDisplay(sDisplayContent);
+        final TestTaskWindowContainerController taskController =
+                new TestTaskWindowContainerController(stackController);
+
+        final TaskStack stack = stackController.mContainer;
+        final TestTask task = (TestTask) taskController.mContainer;
+        // Stack removal is deferred if one of its child is animating.
+        task.setLocalIsAnimating(true);
+
+        stackController.removeContainer();
+        // For the case of deferred removal the stack controller will no longer be connected to the
+        // container, but the task controller will still be connected to the its container until
+        // the stack window container is removed.
+        assertNull(stackController.mContainer);
+        assertNull(stack.getController());
+        assertNotNull(taskController.mContainer);
+        assertNotNull(task.getController());
+
+        stack.removeImmediately();
+        assertNull(taskController.mContainer);
+        assertNull(task.getController());
+    }
+
+    @Test
+    public void testReparent() throws Exception {
+        // Create first stack on primary display.
+        final StackWindowController stack1Controller =
+                createStackControllerOnDisplay(sDisplayContent);
+        final TaskStack stack1 = stack1Controller.mContainer;
+        final TestTaskWindowContainerController taskController =
+                new TestTaskWindowContainerController(stack1Controller);
+        final TestTask task1 = (TestTask) taskController.mContainer;
+        task1.mOnDisplayChangedCalled = false;
+
+        // Create second display and put second stack on it.
+        final Display display = new Display(DisplayManagerGlobal.getInstance(),
+                sDisplayContent.getDisplayId() + 1, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+        final DisplayContent dc = new DisplayContent(display, sWm, sLayersController,
+                new WallpaperController(sWm));
+        sWm.mRoot.addChild(dc, 1);
+
+        final StackWindowController stack2Controller =
+                createStackControllerOnDisplay(dc);
+        final TaskStack stack2 = stack2Controller.mContainer;
+
+        // Reparent
+        stack1Controller.reparent(dc.getDisplayId(), new Rect());
+        assertEquals(dc, stack1.getDisplayContent());
+        final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
+        final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
+        assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
+        assertTrue(task1.mOnDisplayChangedCalled);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
new file mode 100644
index 0000000..e1a22d9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
+import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER;
+import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY;
+import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER;
+import static android.graphics.GraphicBuffer.create;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Debug;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link TaskSnapshotCache}.
+ *
+ * runtest frameworks-services -c com.android.server.wm.TaskSnapshotCacheTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
+
+    private TaskSnapshotCache mCache;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mCache = new TaskSnapshotCache(sWm, mLoader);
+    }
+
+    @Test
+    public void testAppRemoved() throws Exception {
+        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        mCache.putSnapshot(window.getTask(), createSnapshot());
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
+                false /* restoreFromDisk */));
+        mCache.onAppRemoved(window.mAppToken);
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
+                false /* restoreFromDisk */));
+    }
+
+    @Test
+    public void testAppDied() throws Exception {
+        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        mCache.putSnapshot(window.getTask(), createSnapshot());
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
+                false /* restoreFromDisk */));
+        mCache.onAppDied(window.mAppToken);
+
+        // Should still be in the retrieval cache.
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
+                false /* restoreFromDisk */));
+
+        // Trash retrieval cache.
+        for (int i = 0; i < 20; i++) {
+            mCache.putSnapshot(createWindow(null, FIRST_APPLICATION_WINDOW, "window").getTask(),
+                    createSnapshot());
+        }
+
+        // Should not be in cache anymore
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
+                false /* restoreFromDisk */));
+    }
+
+    @Test
+    public void testTaskRemoved() throws Exception {
+        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        mCache.putSnapshot(window.getTask(), createSnapshot());
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
+                false /* restoreFromDisk */));
+        mCache.onTaskRemoved(window.getTask().mTaskId);
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
+                false /* restoreFromDisk */));
+    }
+
+    @Test
+    public void testRestoreFromDisk() throws Exception {
+        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        mPersister.persistSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId, createSnapshot());
+        mPersister.waitForQueueEmpty();
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+                false /* restoreFromDisk */));
+
+        // Load it from disk
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+                true /* restoreFromDisk */));
+
+        // Make sure it's in the cache now.
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+                false /* restoreFromDisk */));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
new file mode 100644
index 0000000..dc008b5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+
+import com.android.internal.util.Predicate;
+import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader}
+ *
+ * runtest frameworks-services -c com.android.server.wm.TaskSnapshotPersisterLoaderTest
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
+
+    private static final String TEST_USER_NAME = "TaskSnapshotPersisterTest User";
+    private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
+
+    @Test
+    public void testPersistAndLoadSnapshot() {
+        mPersister.persistSnapshot(1 , sTestUserId, createSnapshot());
+        mPersister.waitForQueueEmpty();
+        final File[] files = new File[] { new File(sFilesDir.getPath() + "/snapshots/1.proto"),
+                new File(sFilesDir.getPath() + "/snapshots/1.png") };
+        assertTrueForFiles(files, File::exists, " must exist");
+        final TaskSnapshot snapshot = mLoader.loadTask(1, sTestUserId);
+        assertNotNull(snapshot);
+        assertEquals(TEST_INSETS, snapshot.getContentInsets());
+        assertNotNull(snapshot.getSnapshot());
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
+    }
+
+    private void assertTrueForFiles(File[] files, Predicate<File> predicate, String message) {
+        for (File file : files) {
+            assertTrue(file.getName() + message, predicate.apply(file));
+        }
+    }
+
+    @Test
+    public void testTaskRemovedFromRecents() {
+        mPersister.persistSnapshot(1, sTestUserId, createSnapshot());
+        mPersister.onTaskRemovedFromRecents(1, sTestUserId);
+        mPersister.waitForQueueEmpty();
+        assertFalse(new File(sFilesDir.getPath() + "/snapshots/1.proto").exists());
+        assertFalse(new File(sFilesDir.getPath() + "/snapshots/1.png").exists());
+    }
+
+    /**
+     * Tests that persisting a couple of snapshots is being throttled.
+     */
+    @Test
+    public void testThrottling() {
+        long ms = SystemClock.elapsedRealtime();
+        mPersister.persistSnapshot(1, sTestUserId, createSnapshot());
+        mPersister.persistSnapshot(2, sTestUserId, createSnapshot());
+        mPersister.persistSnapshot(3, sTestUserId, createSnapshot());
+        mPersister.persistSnapshot(4, sTestUserId, createSnapshot());
+        mPersister.persistSnapshot(5, sTestUserId, createSnapshot());
+        mPersister.persistSnapshot(6, sTestUserId, createSnapshot());
+        mPersister.waitForQueueEmpty();
+        assertTrue(SystemClock.elapsedRealtime() - ms > 500);
+    }
+
+    @Test
+    public void testGetTaskId() {
+        RemoveObsoleteFilesQueueItem removeObsoleteFilesQueueItem =
+                mPersister.new RemoveObsoleteFilesQueueItem(new ArraySet<>(), new int[] {});
+        assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("blablablulp"));
+        assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("nothing.err"));
+        assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("/invalid/"));
+        assertEquals(12, removeObsoleteFilesQueueItem.getTaskId("12.png"));
+        assertEquals(12, removeObsoleteFilesQueueItem.getTaskId("12.proto"));
+        assertEquals(1, removeObsoleteFilesQueueItem.getTaskId("1.png"));
+    }
+
+    @Test
+    public void testRemoveObsoleteFiles() {
+        mPersister.persistSnapshot(1, sTestUserId, createSnapshot());
+        mPersister.persistSnapshot(2, sTestUserId, createSnapshot());
+        final ArraySet<Integer> taskIds = new ArraySet<>();
+        taskIds.add(1);
+        mPersister.removeObsoleteFiles(taskIds, new int[] { sTestUserId });
+        mPersister.waitForQueueEmpty();
+        final File[] existsFiles = new File[] {
+                new File(sFilesDir.getPath() + "/snapshots/1.proto"),
+                new File(sFilesDir.getPath() + "/snapshots/1.png") };
+        final File[] nonExistsFiles = new File[] {
+                new File(sFilesDir.getPath() + "/snapshots/2.proto"),
+                new File(sFilesDir.getPath() + "/snapshots/2.png") };
+        assertTrueForFiles(existsFiles, File::exists, " must exist");
+        assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
+    }
+
+    @Test
+    public void testRemoveObsoleteFiles_addedOneInTheMeantime() {
+        mPersister.persistSnapshot(1, sTestUserId, createSnapshot());
+        final ArraySet<Integer> taskIds = new ArraySet<>();
+        taskIds.add(1);
+        mPersister.removeObsoleteFiles(taskIds, new int[] { sTestUserId });
+        mPersister.persistSnapshot(2, sTestUserId, createSnapshot());
+        mPersister.waitForQueueEmpty();
+        final File[] existsFiles = new File[] {
+                new File(sFilesDir.getPath() + "/snapshots/1.proto"),
+                new File(sFilesDir.getPath() + "/snapshots/1.png"),
+                new File(sFilesDir.getPath() + "/snapshots/2.proto"),
+                new File(sFilesDir.getPath() + "/snapshots/2.png") };
+        assertTrueForFiles(existsFiles, File::exists, " must exist");
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
new file mode 100644
index 0000000..6fc6edb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
+import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.content.pm.UserInfo;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import java.io.File;
+
+/**
+ * Base class for tests that use a {@link TaskSnapshotPersister}.
+ */
+class TaskSnapshotPersisterTestBase extends WindowTestsBase {
+
+    private static final String TEST_USER_NAME = "TaskSnapshotPersisterTest User";
+    private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
+
+    TaskSnapshotPersister mPersister;
+    TaskSnapshotLoader mLoader;
+    static int sTestUserId;
+    static File sFilesDir;
+    private static UserManager sUserManager;
+
+    @BeforeClass
+    public static void setUpUser() {
+        sUserManager = UserManager.get(InstrumentationRegistry.getContext());
+        sTestUserId = createUser(TEST_USER_NAME, 0);
+        sFilesDir = InstrumentationRegistry.getContext().getFilesDir();
+    }
+
+    @AfterClass
+    public static void tearDownUser() {
+        removeUser(sTestUserId);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mPersister = new TaskSnapshotPersister(
+                userId -> sFilesDir);
+        mLoader = new TaskSnapshotLoader(mPersister);
+        mPersister.start();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        cleanDirectory();
+    }
+
+    private static int createUser(String name, int flags) {
+        UserInfo user = sUserManager.createUser(name, flags);
+        if (user == null) {
+            Assert.fail("Error while creating the test user: " + TEST_USER_NAME);
+        }
+        return user.id;
+    }
+
+    private static void removeUser(int userId) {
+        if (!sUserManager.removeUser(userId)) {
+            Assert.fail("Error while removing the test user: " + TEST_USER_NAME);
+        }
+    }
+
+    private void cleanDirectory() {
+        for (File file : new File(sFilesDir, "snapshots").listFiles()) {
+            if (!file.isDirectory()) {
+                file.delete();
+            }
+        }
+    }
+
+    TaskSnapshot createSnapshot() {
+        GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888,
+                USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY);
+        Canvas c = buffer.lockCanvas();
+        c.drawColor(Color.RED);
+        buffer.unlockCanvasAndPost(c);
+        return new TaskSnapshot(buffer, ORIENTATION_PORTRAIT, TEST_INSETS);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
new file mode 100644
index 0000000..aab75ee
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link TaskSnapshotSurface}.
+ *
+ * runtest frameworks-services -c com.android.server.wm.TaskSnapshotSurfaceTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskSnapshotSurfaceTest extends WindowTestsBase {
+
+    private TaskSnapshotSurface mSurface;
+
+    @Before
+    public void setUp() {
+        mSurface = new TaskSnapshotSurface(null, null, null, Color.WHITE);
+    }
+
+    @Test
+    public void fillEmptyBackground_fillHorizontally() throws Exception {
+        final Canvas mockCanvas = mock(Canvas.class);
+        when(mockCanvas.getWidth()).thenReturn(200);
+        when(mockCanvas.getHeight()).thenReturn(100);
+        final Bitmap b = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
+        verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
+    }
+
+    @Test
+    public void fillEmptyBackground_fillVertically() throws Exception {
+        final Canvas mockCanvas = mock(Canvas.class);
+        when(mockCanvas.getWidth()).thenReturn(100);
+        when(mockCanvas.getHeight()).thenReturn(200);
+        final Bitmap b = Bitmap.createBitmap(200, 100, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
+        verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
+    }
+
+    @Test
+    public void fillEmptyBackground_fillBoth() throws Exception {
+        final Canvas mockCanvas = mock(Canvas.class);
+        when(mockCanvas.getWidth()).thenReturn(200);
+        when(mockCanvas.getHeight()).thenReturn(200);
+        final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
+        verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
+        verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
+    }
+
+    @Test
+    public void fillEmptyBackground_dontFill_sameSize() throws Exception {
+        final Canvas mockCanvas = mock(Canvas.class);
+        when(mockCanvas.getWidth()).thenReturn(100);
+        when(mockCanvas.getHeight()).thenReturn(100);
+        final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
+        verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception {
+        final Canvas mockCanvas = mock(Canvas.class);
+        when(mockCanvas.getWidth()).thenReturn(100);
+        when(mockCanvas.getHeight()).thenReturn(100);
+        final Bitmap b = Bitmap.createBitmap(200, 200, Config.ARGB_8888);
+        mSurface.fillEmptyBackground(mockCanvas, b);
+        verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
index 24893a1..462bd68 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -20,7 +20,10 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.After;
 
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -42,68 +45,69 @@
 @RunWith(AndroidJUnit4.class)
 public class TaskStackContainersTests extends WindowTestsBase {
 
+    private TaskStack mPinnedStack;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mPinnedStack = new StackWindowController(PINNED_STACK_ID, null,
+                sDisplayContent.getDisplayId(), true /* onTop */, new Rect(), sWm).mContainer;
+
+        // Stack should contain visible app window to be considered visible.
+        final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
+        assertFalse(mPinnedStack.isVisible());
+        final TestAppWindowToken pinnedApp = new TestAppWindowToken(sDisplayContent);
+        pinnedTask.addChild(pinnedApp, 0 /* addPos */);
+        assertTrue(mPinnedStack.isVisible());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mPinnedStack.removeImmediately();
+    }
+
     @Test
     public void testStackPositionChildAt() throws Exception {
         // Test that always-on-top stack can't be moved to position other than top.
         final TaskStack stack1 = createTaskStackOnDisplay(sDisplayContent);
         final TaskStack stack2 = createTaskStackOnDisplay(sDisplayContent);
-        final TaskStack pinnedStack = addPinnedStack();
 
         final WindowContainer taskStackContainer = stack1.getParent();
 
         final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1);
         final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2);
-        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(pinnedStack);
+        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack);
         assertGreaterThan(pinnedStackPos, stack2Pos);
         assertGreaterThan(stack2Pos, stack1Pos);
 
-        taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, pinnedStack, false);
+        taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedStack, false);
         assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
         assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);
+        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
 
-        taskStackContainer.positionChildAt(1, pinnedStack, false);
+        taskStackContainer.positionChildAt(1, mPinnedStack, false);
         assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
         assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);
+        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
     }
+
     @Test
     public void testStackPositionBelowPinnedStack() throws Exception {
         // Test that no stack can be above pinned stack.
-        final TaskStack pinnedStack = addPinnedStack();
         final TaskStack stack1 = createTaskStackOnDisplay(sDisplayContent);
 
         final WindowContainer taskStackContainer = stack1.getParent();
 
         final int stackPos = taskStackContainer.mChildren.indexOf(stack1);
-        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(pinnedStack);
+        final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack);
         assertGreaterThan(pinnedStackPos, stackPos);
 
         taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false);
         assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);
+        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
 
         taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false);
         assertEquals(taskStackContainer.mChildren.get(stackPos), stack1);
-        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);
-    }
-
-    private TaskStack addPinnedStack() {
-        TaskStack pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID);
-        if (pinnedStack == null) {
-            sDisplayContent.addStackToDisplay(PINNED_STACK_ID, true);
-            pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID);
-        }
-
-        if (!pinnedStack.isVisible()) {
-            // Stack should contain visible app window to be considered visible.
-            final Task pinnedTask = createTaskInStack(pinnedStack, 0 /* userId */);
-            assertFalse(pinnedStack.isVisible());
-            final TestAppWindowToken pinnedApp = new TestAppWindowToken(sDisplayContent);
-            pinnedTask.addChild(pinnedApp, 0 /* addPos */);
-            assertTrue(pinnedStack.isVisible());
-        }
-
-        return pinnedStack;
+        assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index f84bf60..3ee1da43 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -16,11 +16,16 @@
 
 package com.android.server.wm;
 
-import org.junit.Test;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 
+import android.hardware.display.DisplayManagerGlobal;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+import android.view.DisplayInfo;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -35,7 +40,7 @@
  */
 @SmallTest
 @Presubmit
-@org.junit.runner.RunWith(AndroidJUnit4.class)
+@RunWith(AndroidJUnit4.class)
 public class TaskWindowContainerControllerTests extends WindowTestsBase {
 
     @Test
@@ -52,7 +57,7 @@
     }
 
     @Test
-    public void testRemoveContainer_DeferRemoval() throws Exception {
+    public void testRemoveContainer_deferRemoval() throws Exception {
         final TestTaskWindowContainerController taskController =
                 new TestTaskWindowContainerController();
         final TestAppWindowContainerController appController =
@@ -78,32 +83,69 @@
 
     @Test
     public void testReparent() throws Exception {
-        final TaskStack stack1 = createTaskStackOnDisplay(sDisplayContent);
+        final StackWindowController stackController1 =
+                createStackControllerOnDisplay(sDisplayContent);
         final TestTaskWindowContainerController taskController =
-                new TestTaskWindowContainerController(stack1.mStackId);
-        final TaskStack stack2 = createTaskStackOnDisplay(sDisplayContent);
+                new TestTaskWindowContainerController(stackController1);
+        final StackWindowController stackController2 =
+                createStackControllerOnDisplay(sDisplayContent);
         final TestTaskWindowContainerController taskController2 =
-                new TestTaskWindowContainerController(stack2.mStackId);
+                new TestTaskWindowContainerController(stackController2);
 
         boolean gotException = false;
         try {
-            taskController.reparent(stack1.mStackId, 0);
+            taskController.reparent(stackController1, 0);
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
         assertTrue("Should not be able to reparent to the same parent", gotException);
 
+        final StackWindowController stackController3 =
+                createStackControllerOnDisplay(sDisplayContent);
+        stackController3.setContainer(null);
         gotException = false;
         try {
-            taskController.reparent(sNextStackId + 1, 0);
+            taskController.reparent(stackController3, 0);
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
-        assertTrue("Should not be able to reparent to a stackId that doesn't exist", gotException);
+        assertTrue("Should not be able to reparent to a stack that doesn't have a container",
+                gotException);
 
-        taskController.reparent(stack2.mStackId, 0);
-        assertEquals(stack2, taskController.mContainer.getParent());
+        taskController.reparent(stackController2, 0);
+        assertEquals(stackController2.mContainer, taskController.mContainer.getParent());
         assertEquals(0, ((TestTask) taskController.mContainer).positionInParent());
         assertEquals(1, ((TestTask) taskController2.mContainer).positionInParent());
     }
+
+    @Test
+    public void testReparent_BetweenDisplays() throws Exception {
+        // Create first stack on primary display.
+        final StackWindowController stack1Controller =
+                createStackControllerOnDisplay(sDisplayContent);
+        final TaskStack stack1 = stack1Controller.mContainer;
+        final TestTaskWindowContainerController taskController =
+                new TestTaskWindowContainerController(stack1Controller);
+        final TestTask task1 = (TestTask) taskController.mContainer;
+        task1.mOnDisplayChangedCalled = false;
+
+        // Create second display and put second stack on it.
+        final Display display = new Display(DisplayManagerGlobal.getInstance(),
+                sDisplayContent.getDisplayId() + 1, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
+        final DisplayContent dc = new DisplayContent(display, sWm, sLayersController,
+                new WallpaperController(sWm));
+        sWm.mRoot.addChild(dc, 1);
+        final StackWindowController stack2Controller = createStackControllerOnDisplay(dc);
+        final TaskStack stack2 = stack2Controller.mContainer;
+        final TestTaskWindowContainerController taskController2 =
+                new TestTaskWindowContainerController(stack2Controller);
+        final TestTask task2 = (TestTask) taskController2.mContainer;
+
+        // Reparent and check state
+        taskController.reparent(stack2Controller, 0);
+        assertEquals(stack2, task1.getParent());
+        assertEquals(0, task1.positionInParent());
+        assertEquals(1, task2.positionInParent());
+        assertTrue(task1.mOnDisplayChangedCalled);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index 1514e69..c029a9f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -107,4 +107,8 @@
             throws RemoteException {
 
     }
+
+    @Override
+    public void dispatchPointerCaptureChanged(boolean hasCapture) {
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c0c8fb0..ec429a0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -24,6 +24,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
@@ -74,6 +75,7 @@
 import android.view.IWindowManager;
 import android.view.KeyEvent;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy;
 import android.view.animation.Animation;
 import android.os.PowerManagerInternal;
@@ -92,6 +94,8 @@
 
     int rotationToReport = 0;
 
+    private Runnable mRunnableWhenAddingSplashScreen;
+
     static synchronized WindowManagerService getWindowManagerService(Context context) {
         if (sWm == null) {
             // We only want to do this once for the test process as we don't want WM to try to
@@ -318,11 +322,36 @@
         return false;
     }
 
+    /**
+     * Sets a runnable to run when adding a splash screen which gets executed after the window has
+     * been added but before returning the surface.
+     */
+    void setRunnableWhenAddingSplashScreen(Runnable r) {
+        mRunnableWhenAddingSplashScreen = r;
+    }
+
     @Override
     public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
             CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
-            int logo, int windowFlags, Configuration overrideConfig) {
-        return null;
+            int logo, int windowFlags, Configuration overrideConfig, int displayId) {
+        final com.android.server.wm.WindowState window;
+        final AppWindowToken atoken;
+        synchronized (sWm.mWindowMap) {
+            atoken = WindowTestsBase.sDisplayContent.getAppWindowToken(appToken);
+            window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
+                    "Starting window");
+            atoken.startingWindow = window;
+        }
+        if (mRunnableWhenAddingSplashScreen != null) {
+            mRunnableWhenAddingSplashScreen.run();
+            mRunnableWhenAddingSplashScreen = null;
+        }
+        return () -> {
+            synchronized (sWm.mWindowMap) {
+                atoken.removeChild(window);
+                atoken.startingWindow = null;
+            }
+        };
     }
 
     @Override
@@ -482,7 +511,7 @@
 
     @Override
     public boolean isScreenOn() {
-        return false;
+        return true;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 6129198..772bfb4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -45,20 +45,18 @@
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
-public class UnknownAppVisibilityControllerTest {
+public class UnknownAppVisibilityControllerTest extends WindowTestsBase {
 
     private WindowManagerService mWm;
-    private @Mock ActivityManagerInternal mAm;
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+        super.setUp();
         final Context context = InstrumentationRegistry.getTargetContext();
-        LocalServices.addService(ActivityManagerInternal.class, mAm);
         doAnswer((InvocationOnMock invocationOnMock) -> {
             invocationOnMock.getArgumentAt(0, Runnable.class).run();
             return null;
-        }).when(mAm).notifyKeyguardFlagsChanged(any());
+        }).when(sMockAm).notifyKeyguardFlagsChanged(any());
         mWm = TestWindowManagerPolicy.getWindowManagerService(context);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 466da94..186884b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -20,6 +20,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import android.app.ActivityManager.TaskDescription;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -76,7 +77,8 @@
         final Rect mInsetBounds = new Rect();
         boolean mFullscreenForTest = true;
         TaskWithBounds(Rect bounds) {
-            super(0, mStubStack, 0, sWm, null, null, false, 0, false, null);
+            super(0, mStubStack, 0, sWm, null, null, false, 0, false, false, new TaskDescription(),
+                    null);
             mBounds = bounds;
         }
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 44d5055..72157b6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -16,13 +16,19 @@
 
 package com.android.server.wm;
 
+import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityManagerInternal;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.view.IApplicationToken;
 import org.junit.Assert;
 import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 
+import android.app.ActivityManager.TaskSnapshot;
 import android.content.Context;
 import android.os.IBinder;
 import android.support.test.InstrumentationRegistry;
@@ -48,13 +54,17 @@
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static org.mockito.Mockito.mock;
 
+import com.android.server.AttributeCache;
+import com.android.server.LocalServices;
+
 /**
  * Common base class for window manager unit test classes.
  */
 class WindowTestsBase {
     static WindowManagerService sWm = null;
-    private final IWindow mIWindow = new TestIWindow();
-    private final Session mMockSession = mock(Session.class);
+    static TestWindowManagerPolicy sPolicy = null;
+    private final static IWindow sIWindow = new TestIWindow();
+    private final static Session sMockSession = mock(Session.class);
     static int sNextStackId = FIRST_DYNAMIC_STACK_ID;
     private static int sNextTaskId = 0;
 
@@ -70,19 +80,31 @@
     static WindowState sAppWindow;
     static WindowState sChildAppWindowAbove;
     static WindowState sChildAppWindowBelow;
+    static @Mock ActivityManagerInternal sMockAm;
 
     @Before
     public void setUp() throws Exception {
         if (sOneTimeSetupDone) {
+            Mockito.reset(sMockAm);
             return;
         }
         sOneTimeSetupDone = true;
+        MockitoAnnotations.initMocks(this);
         final Context context = InstrumentationRegistry.getTargetContext();
+        LocalServices.addService(ActivityManagerInternal.class, sMockAm);
+        AttributeCache.init(context);
         sWm = TestWindowManagerPolicy.getWindowManagerService(context);
+        sPolicy = (TestWindowManagerPolicy) sWm.mPolicy;
         sLayersController = new WindowLayersController(sWm);
+        sDisplayContent = sWm.mRoot.getDisplayContent(context.getDisplay().getDisplayId());
+        if (sDisplayContent != null) {
+            sDisplayContent.removeImmediately();
+        }
         sDisplayContent = new DisplayContent(context.getDisplay(), sWm, sLayersController,
                 new WallpaperController(sWm));
         sWm.mRoot.addChild(sDisplayContent, 0);
+        sWm.mDisplayEnabled = true;
+        sWm.mDisplayReady = true;
 
         // Set-up some common windows.
         sWallpaperWindow = createWindow(null, TYPE_WALLPAPER, sDisplayContent, "wallpaperWindow");
@@ -106,7 +128,14 @@
         Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
     }
 
-    private WindowToken createWindowToken(DisplayContent dc, int type) {
+    /**
+     * Waits until the main handler for WM has processed all messages.
+     */
+    void waitUntilHandlerIdle() {
+        sWm.mH.runWithScissors(() -> { }, 0);
+    }
+
+    private static WindowToken createWindowToken(DisplayContent dc, int type) {
         if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
             return new TestWindowToken(type, dc);
         }
@@ -118,7 +147,7 @@
         return token;
     }
 
-    WindowState createWindow(WindowState parent, int type, String name) {
+    static WindowState createWindow(WindowState parent, int type, String name) {
         return (parent == null)
                 ? createWindow(parent, type, sDisplayContent, name)
                 : createWindow(parent, type, parent.mToken, name);
@@ -130,16 +159,16 @@
         return createWindow(null, type, token, name);
     }
 
-    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
+    static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
         final WindowToken token = createWindowToken(dc, type);
         return createWindow(parent, type, token, name);
     }
 
-    WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
+    static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
         attrs.setTitle(name);
 
-        final WindowState w = new WindowState(sWm, mMockSession, mIWindow, token, parent, OP_NONE,
+        final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
                 0, attrs, 0, 0);
         // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
         // adding it to the token...
@@ -148,22 +177,26 @@
     }
 
     /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
-    TaskStack createTaskStackOnDisplay(DisplayContent dc) {
-        final int stackId = sNextStackId++;
-        dc.addStackToDisplay(stackId, true);
-        return sWm.mStackIdToStack.get(stackId);
+    static TaskStack createTaskStackOnDisplay(DisplayContent dc) {
+        return createStackControllerOnDisplay(dc).mContainer;
+    }
+
+    static StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
+        final int stackId = ++sNextStackId;
+        return new StackWindowController(stackId, null, dc.getDisplayId(),
+                true /* onTop */, new Rect(), sWm);
     }
 
     /**Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
-    Task createTaskInStack(TaskStack stack, int userId) {
+    static Task createTaskInStack(TaskStack stack, int userId) {
         final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0,
-                false, null);
+                false, false, new TaskDescription(), null);
         stack.addTask(newTask, POSITION_TOP);
         return newTask;
     }
 
     /* Used so we can gain access to some protected members of the {@link WindowToken} class */
-    class TestWindowToken extends WindowToken {
+    static class TestWindowToken extends WindowToken {
 
         TestWindowToken(int type, DisplayContent dc) {
             this(type, dc, false /* persistOnEmpty */);
@@ -183,7 +216,7 @@
     }
 
     /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
-    class TestAppWindowToken extends AppWindowToken {
+    static class TestAppWindowToken extends AppWindowToken {
 
         TestAppWindowToken(DisplayContent dc) {
             super(sWm, null, false, dc);
@@ -210,12 +243,17 @@
     class TestTask extends Task {
 
         boolean mShouldDeferRemoval = false;
+        boolean mOnDisplayChangedCalled = false;
+        private boolean mUseLocalIsAnimating = false;
+        private boolean mIsAnimating = false;
 
         TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
                 Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode,
-                boolean homeTask, TaskWindowContainerController controller) {
+                boolean supportsPictureInPicture, boolean homeTask,
+                TaskWindowContainerController controller) {
             super(taskId, stack, userId, service, bounds, overrideConfig, isOnTopLauncher,
-                    resizeMode, homeTask, controller);
+                    resizeMode, supportsPictureInPicture, homeTask, new TaskDescription(),
+                            controller);
         }
 
         boolean shouldDeferRemoval() {
@@ -225,6 +263,22 @@
         int positionInParent() {
             return getParent().mChildren.indexOf(this);
         }
+
+        @Override
+        void onDisplayChanged(DisplayContent dc) {
+            super.onDisplayChanged(dc);
+            mOnDisplayChangedCalled = true;
+        }
+
+        @Override
+        boolean isAnimating() {
+            return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating();
+        }
+
+        void setLocalIsAnimating(boolean isAnimating) {
+            mUseLocalIsAnimating = true;
+            mIsAnimating = isAnimating;
+        }
     }
 
     /**
@@ -234,21 +288,33 @@
     class TestTaskWindowContainerController extends TaskWindowContainerController {
 
         TestTaskWindowContainerController() {
-            this(createTaskStackOnDisplay(sDisplayContent).mStackId);
+            this(createStackControllerOnDisplay(sDisplayContent));
         }
 
-        TestTaskWindowContainerController(int stackId) {
-            super(sNextTaskId++, stackId, 0 /* userId */, null /* bounds */,
-                    EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, false /* homeTask*/,
-                    false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */);
+        TestTaskWindowContainerController(StackWindowController stackController) {
+            super(sNextTaskId++, new TaskWindowContainerListener() {
+                        @Override
+                        public void onSnapshotChanged(TaskSnapshot snapshot) {
+
+                        }
+
+                        @Override
+                        public void requestResize(Rect bounds, int resizeMode) {
+
+                        }
+                    }, stackController, 0 /* userId */, null /* bounds */,
+                    EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE,
+                    false /* supportsPictureInPicture */, false /* homeTask*/,
+                    false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */,
+                    new TaskDescription(), sWm);
         }
 
         @Override
         TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds,
-                Configuration overrideConfig, int resizeMode, boolean homeTask,
-                boolean isOnTopLauncher) {
+                Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+                boolean homeTask, boolean isOnTopLauncher, TaskDescription taskDescription) {
             return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig,
-                    isOnTopLauncher, resizeMode, homeTask, this);
+                    isOnTopLauncher, resizeMode, supportsPictureInPicture, homeTask, this);
         }
     }
 
@@ -270,6 +336,10 @@
                     0 /* inputDispatchingTimeoutNanos */, sWm);
             mToken = token;
         }
+
+        AppWindowToken getAppWindowToken() {
+            return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder());
+        }
     }
 
     class TestIApplicationToken implements IApplicationToken {
@@ -286,7 +356,7 @@
         boolean resizeReported;
 
         TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) {
-            super(sWm, mMockSession, mIWindow, token, null, OP_NONE, 0, attrs, 0, 0);
+            super(sWm, sMockSession, sIWindow, token, null, OP_NONE, 0, attrs, 0, 0);
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 3be04d4..6826975 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -31,11 +31,13 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
-import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
@@ -45,8 +47,6 @@
 
     private static final String PROP_VERIFY_STORAGE = "fw.verify_storage";
 
-    // TODO: pivot all methods to manual mode when quota isn't supported
-
     public static class Lifecycle extends SystemService {
         private StorageStatsService mService;
 
@@ -66,16 +66,37 @@
     private final UserManager mUser;
     private final PackageManager mPackage;
     private final StorageManager mStorage;
+
     private final Installer mInstaller;
 
     public StorageStatsService(Context context) {
-        mContext = context;
-        mAppOps = context.getSystemService(AppOpsManager.class);
-        mUser = context.getSystemService(UserManager.class);
-        mPackage = context.getSystemService(PackageManager.class);
-        mStorage = context.getSystemService(StorageManager.class);
+        mContext = Preconditions.checkNotNull(context);
+        mAppOps = Preconditions.checkNotNull(context.getSystemService(AppOpsManager.class));
+        mUser = Preconditions.checkNotNull(context.getSystemService(UserManager.class));
+        mPackage = Preconditions.checkNotNull(context.getPackageManager());
+        mStorage = Preconditions.checkNotNull(context.getSystemService(StorageManager.class));
+
         mInstaller = new Installer(context);
         mInstaller.onStart();
+        invalidateMounts();
+
+        mStorage.registerListener(new StorageEventListener() {
+            @Override
+            public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+                if ((vol.type == VolumeInfo.TYPE_PRIVATE)
+                        && (newState == VolumeInfo.STATE_MOUNTED)) {
+                    invalidateMounts();
+                }
+            }
+        });
+    }
+
+    private void invalidateMounts() {
+        try {
+            mInstaller.invalidateMounts();
+        } catch (InstallerException e) {
+            Slog.wtf(TAG, "Failed to invalidate mounts", e);
+        }
     }
 
     private void enforcePermission(int callingUid, String callingPackage) {
@@ -85,7 +106,7 @@
             case AppOpsManager.MODE_ALLOWED:
                 return;
             case AppOpsManager.MODE_DEFAULT:
-                mContext.enforceCallingPermission(
+                mContext.enforceCallingOrSelfPermission(
                         android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
                 return;
             default:
@@ -242,7 +263,7 @@
 
     private static void checkEquals(String msg, long expected, long actual) {
         if (expected != actual) {
-            Log.e(TAG, msg + " expected " + expected + " actual " + actual);
+            Slog.e(TAG, msg + " expected " + expected + " actual " + actual);
         }
     }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4bfc3df..7a69803 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -20,6 +20,7 @@
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.IUidObserver;
 import android.app.admin.DevicePolicyManager;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
@@ -38,9 +39,9 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.hardware.display.DisplayManager;
@@ -49,6 +50,7 @@
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IDeviceIdleController;
 import android.os.Looper;
@@ -80,6 +82,7 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -103,6 +106,9 @@
     private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
     private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
 
+    private static final boolean ENABLE_KERNEL_UPDATES = true;
+    private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
+
     long mAppIdleScreenThresholdMillis;
     long mCheckIdleIntervalMillis;
     long mAppIdleWallclockThresholdMillis;
@@ -134,6 +140,7 @@
     private IBatteryStats mBatteryStats;
 
     private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
+    private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
     private File mUsageStatsDir;
     long mRealTimeSnapshot;
     long mSystemTimeSnapshot;
@@ -235,6 +242,19 @@
                 postOneTimeCheckIdleStates();
             }
 
+            if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) {
+                try {
+                    ActivityManager.getService().registerUidObserver(mUidObserver,
+                            ActivityManager.UID_OBSERVER_PROCSTATE
+                                    | ActivityManager.UID_OBSERVER_GONE,
+                            ActivityManager.PROCESS_STATE_UNKNOWN, null);
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            } else {
+                Slog.w(TAG, "Missing procfs interface: " + KERNEL_COUNTER_FILE);
+            }
+
             mSystemServicesReady = true;
         } else if (phase == PHASE_BOOT_COMPLETED) {
             setChargingState(getContext().getSystemService(BatteryManager.class).isCharging());
@@ -311,6 +331,39 @@
         }
     };
 
+    private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+        @Override
+        public void onUidStateChanged(int uid, int procState) {
+            final int newCounter = (procState <= ActivityManager.PROCESS_STATE_TOP) ? 0 : 1;
+            synchronized (mUidToKernelCounter) {
+                final int oldCounter = mUidToKernelCounter.get(uid, 0);
+                if (newCounter != oldCounter) {
+                    mUidToKernelCounter.put(uid, newCounter);
+                    try {
+                        FileUtils.stringToFile(KERNEL_COUNTER_FILE, uid + " " + newCounter);
+                    } catch (IOException e) {
+                        Slog.w(TAG, "Failed to update counter set: " + e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onUidIdle(int uid, boolean disabled) throws RemoteException {
+            // Ignored
+        }
+
+        @Override
+        public void onUidGone(int uid, boolean disabled) throws RemoteException {
+            onUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
+        }
+
+        @Override
+        public void onUidActive(int uid) throws RemoteException {
+            // Ignored
+        }
+    };
+
     @Override
     public void onStatsUpdated() {
         mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
diff --git a/services/usb/Android.mk b/services/usb/Android.mk
index feabf0a..f560e71 100644
--- a/services/usb/Android.mk
+++ b/services/usb/Android.mk
@@ -8,5 +8,7 @@
       $(call all-java-files-under,java)
 
 LOCAL_JAVA_LIBRARIES := services.core
+LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.usb@1.0-java-static \
+android.hidl.manager@1.0-java-static
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index db7a31a..fbbe636 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -656,6 +656,7 @@
             // send a sticky broadcast containing current USB state
             Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                     | Intent.FLAG_RECEIVER_FOREGROUND);
             intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
             intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 7f182a4..4b8e4c8 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,36 +16,40 @@
 
 package com.android.server.usb;
 
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.FgThread;
-
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.V1_0.IUsb;
+import android.hardware.usb.V1_0.IUsbCallback;
+import android.hardware.usb.V1_0.PortRole;
+import android.hardware.usb.V1_0.PortRoleType;
+import android.hardware.usb.V1_0.PortStatus;
+import android.hardware.usb.V1_0.Status;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.Bundle;
 import android.os.Handler;
+import android.os.HwBinder;
 import android.os.Message;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UEventObserver;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
 import android.os.UserHandle;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.FgThread;
 
-import libcore.io.IoUtils;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
 
 /**
  * Allows trusted components to control the properties of physical USB ports
- * via the "/sys/class/dual_role_usb" kernel interface.
+ * via the IUsb.hal.
  * <p>
  * Note: This interface may not be supported on all chipsets since the USB drivers
  * must be changed to publish this information through the module.  At the moment
@@ -60,43 +64,6 @@
 
     private static final int MSG_UPDATE_PORTS = 1;
 
-    // UEvent path to watch.
-    private static final String UEVENT_FILTER = "SUBSYSTEM=dual_role_usb";
-
-    // SysFS directory that contains USB ports as subdirectories.
-    private static final String SYSFS_CLASS = "/sys/class/dual_role_usb";
-
-    // SysFS file that contains a USB port's supported modes.  (read-only)
-    // Contents: "", "ufp", "dfp", or "ufp dfp".
-    private static final String SYSFS_PORT_SUPPORTED_MODES = "supported_modes";
-
-    // SysFS file that contains a USB port's current mode.  (read-write if configurable)
-    // Contents: "", "ufp", or "dfp".
-    private static final String SYSFS_PORT_MODE = "mode";
-
-    // SysFS file that contains a USB port's current power role.  (read-write if configurable)
-    // Contents: "", "source", or "sink".
-    private static final String SYSFS_PORT_POWER_ROLE = "power_role";
-
-    // SysFS file that contains a USB port's current data role.  (read-write if configurable)
-    // Contents: "", "host", or "device".
-    private static final String SYSFS_PORT_DATA_ROLE = "data_role";
-
-    // Port modes: upstream facing port or downstream facing port.
-    private static final String PORT_MODE_DFP = "dfp";
-    private static final String PORT_MODE_UFP = "ufp";
-
-    // Port power roles: source or sink.
-    private static final String PORT_POWER_ROLE_SOURCE = "source";
-    private static final String PORT_POWER_ROLE_SINK = "sink";
-
-    // Port data roles: host or device.
-    private static final String PORT_DATA_ROLE_HOST = "host";
-    private static final String PORT_DATA_ROLE_DEVICE = "device";
-
-    private static final String USB_TYPEC_PROP_PREFIX = "sys.usb.typec.";
-    private static final String USB_TYPEC_STATE = "sys.usb.typec.state";
-
     // All non-trivial role combinations.
     private static final int COMBO_SOURCE_HOST =
             UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
@@ -110,8 +77,29 @@
     // The system context.
     private final Context mContext;
 
-    // True if we have kernel support.
-    private final boolean mHaveKernelSupport;
+    // Proxy object for the usb hal daemon.
+    // @GuardedBy("mLock")
+    private IUsb mProxy = null;
+
+    // Callback when the UsbPort status is changed by the kernel.
+    // Mostly due a command sent by the remote Usb device.
+    private HALCallback mHALCallback = new HALCallback(null, this);
+
+    // Notification object used to listen to the start of the usb daemon.
+    private final ServiceNotification mServiceNotification = new ServiceNotification();
+
+    // Cookie sent for usb hal death notification.
+    private static final int USB_HAL_DEATH_COOKIE = 1000;
+
+    // Usb hal service name.
+    private static String sSERVICENAME = "usb_hal";
+
+    // Used as the key while sending the bundle to Main thread.
+    private static final String PORT_INFO = "port_info";
+
+    // This is monitored to prevent updating the protInfo before the system
+    // is ready.
+    private boolean mSystemReady;
 
     // Mutex for all mutable shared state.
     private final Object mLock = new Object();
@@ -123,17 +111,37 @@
     private final ArrayMap<String, PortInfo> mPorts = new ArrayMap<String, PortInfo>();
 
     // List of all simulated ports, indexed by id.
-    private final ArrayMap<String, SimulatedPortInfo> mSimulatedPorts =
-            new ArrayMap<String, SimulatedPortInfo>();
+    private final ArrayMap<String, RawPortInfo> mSimulatedPorts =
+            new ArrayMap<String, RawPortInfo>();
 
     public UsbPortManager(Context context) {
         mContext = context;
-        mHaveKernelSupport = new File(SYSFS_CLASS).exists();
+        try {
+            boolean ret = IServiceManager.getService("manager")
+                    .registerForNotifications("android.hardware.usb@1.0::IUsb",
+                    "", mServiceNotification);
+            if (!ret) {
+                logAndPrint(Log.ERROR, null, "Failed to register service start notification");
+            }
+        } catch (RemoteException e) {
+            logAndPrint(Log.ERROR, null, "Failed to register service start notification");
+            Thread.dumpStack();
+            return;
+        }
+        connectToProxy(null);
     }
 
     public void systemReady() {
-        mUEventObserver.startObserving(UEVENT_FILTER);
-        scheduleUpdatePorts();
+        if (mProxy != null) {
+            try {
+                mProxy.queryPortStatus();
+            } catch (RemoteException e) {
+                logAndPrint(Log.ERROR, null,
+                        "ServiceStart: Failed to query port status");
+                Thread.dumpStack();
+            }
+        }
+        mSystemReady = true;
     }
 
     public UsbPort[] getPorts() {
@@ -223,20 +231,14 @@
                     + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
                     + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
 
-            SimulatedPortInfo sim = mSimulatedPorts.get(portId);
+            RawPortInfo sim = mSimulatedPorts.get(portId);
             if (sim != null) {
                 // Change simulated state.
                 sim.mCurrentMode = newMode;
                 sim.mCurrentPowerRole = newPowerRole;
                 sim.mCurrentDataRole = newDataRole;
-            } else if (mHaveKernelSupport) {
-                // Change actual state.
-                final File portDir = new File(SYSFS_CLASS, portId);
-                if (!portDir.exists()) {
-                    logAndPrint(Log.ERROR, pw, "USB port not found: portId=" + portId);
-                    return;
-                }
-
+                updatePortsLocked(pw, null);
+            } else if (mProxy != null) {
                 if (currentMode != newMode) {
                     // Changing the mode will have the side-effect of also changing
                     // the power and data roles but it might take some time to apply
@@ -244,38 +246,54 @@
                     // hardware, we have no way of knowing whether it will work apriori
                     // which is why we would prefer to set the power and data roles
                     // directly instead.
-                    if (!writeFile(portDir, SYSFS_PORT_MODE,
-                            newMode == UsbPort.MODE_DFP ? PORT_MODE_DFP : PORT_MODE_UFP)) {
-                        logAndPrint(Log.ERROR, pw, "Failed to set the USB port mode: "
+
+                    logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: "
                                 + "portId=" + portId
                                 + ", newMode=" + UsbPort.modeToString(newMode));
+                    PortRole newRole = new PortRole();
+                    newRole.type = PortRoleType.MODE;
+                    newRole.role = newMode;
+                    try {
+                        mProxy.switchRole(portId, newRole);
+                    } catch (RemoteException e) {
+                        logAndPrint(Log.ERROR, pw, "Failed to set the USB port mode: "
+                                + "portId=" + portId
+                                + ", newMode=" + UsbPort.modeToString(newRole.role));
+                        Thread.dumpStack();
                         return;
                     }
                 } else {
                     // Change power and data role independently as needed.
                     if (currentPowerRole != newPowerRole) {
-                        if (!writeFile(portDir, SYSFS_PORT_POWER_ROLE,
-                                newPowerRole == UsbPort.POWER_ROLE_SOURCE
-                                ? PORT_POWER_ROLE_SOURCE : PORT_POWER_ROLE_SINK)) {
+                        PortRole newRole = new PortRole();
+                        newRole.type = PortRoleType.POWER_ROLE;
+                        newRole.role = newPowerRole;
+                        try {
+                            mProxy.switchRole(portId, newRole);
+                        } catch (RemoteException e) {
                             logAndPrint(Log.ERROR, pw, "Failed to set the USB port power role: "
                                     + "portId=" + portId
-                                    + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole));
+                                    + ", newPowerRole=" + UsbPort.powerRoleToString(newRole.role));
+                            Thread.dumpStack();
                             return;
                         }
                     }
                     if (currentDataRole != newDataRole) {
-                        if (!writeFile(portDir, SYSFS_PORT_DATA_ROLE,
-                                newDataRole == UsbPort.DATA_ROLE_HOST
-                                ? PORT_DATA_ROLE_HOST : PORT_DATA_ROLE_DEVICE)) {
+                        PortRole newRole = new PortRole();
+                        newRole.type = PortRoleType.DATA_ROLE;
+                        newRole.role = newDataRole;
+                        try {
+                            mProxy.switchRole(portId, newRole);
+                        } catch (RemoteException e) {
                             logAndPrint(Log.ERROR, pw, "Failed to set the USB port data role: "
                                     + "portId=" + portId
-                                    + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
+                                    + ", newDataRole=" + UsbPort.dataRoleToString(newRole.role));
+                            Thread.dumpStack();
                             return;
                         }
                     }
                 }
             }
-            updatePortsLocked(pw);
         }
     }
 
@@ -289,8 +307,8 @@
             pw.println("Adding simulated port: portId=" + portId
                     + ", supportedModes=" + UsbPort.modeToString(supportedModes));
             mSimulatedPorts.put(portId,
-                    new SimulatedPortInfo(portId, supportedModes));
-            updatePortsLocked(pw);
+                    new RawPortInfo(portId, supportedModes));
+            updatePortsLocked(pw, null);
         }
     }
 
@@ -298,7 +316,7 @@
             int powerRole, boolean canChangePowerRole,
             int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) {
         synchronized (mLock) {
-            final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId);
+            final RawPortInfo portInfo = mSimulatedPorts.get(portId);
             if (portInfo == null) {
                 pw.println("Cannot connect simulated port which does not exist.");
                 return;
@@ -328,13 +346,13 @@
             portInfo.mCanChangePowerRole = canChangePowerRole;
             portInfo.mCurrentDataRole = dataRole;
             portInfo.mCanChangeDataRole = canChangeDataRole;
-            updatePortsLocked(pw);
+            updatePortsLocked(pw, null);
         }
     }
 
     public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) {
         synchronized (mLock) {
-            final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId);
+            final RawPortInfo portInfo = mSimulatedPorts.get(portId);
             if (portInfo == null) {
                 pw.println("Cannot disconnect simulated port which does not exist.");
                 return;
@@ -347,7 +365,7 @@
             portInfo.mCanChangePowerRole = false;
             portInfo.mCurrentDataRole = 0;
             portInfo.mCanChangeDataRole = false;
-            updatePortsLocked(pw);
+            updatePortsLocked(pw, null);
         }
     }
 
@@ -361,7 +379,7 @@
 
             pw.println("Disconnecting simulated port: portId=" + portId);
             mSimulatedPorts.removeAt(index);
-            updatePortsLocked(pw);
+            updatePortsLocked(pw, null);
         }
     }
 
@@ -370,7 +388,7 @@
             pw.println("Removing all simulated ports and ending simulation.");
             if (!mSimulatedPorts.isEmpty()) {
                 mSimulatedPorts.clear();
-                updatePortsLocked(pw);
+                updatePortsLocked(pw, null);
             }
         }
     }
@@ -393,9 +411,108 @@
         }
     }
 
-    private void updatePortsLocked(IndentingPrintWriter pw) {
-        // Assume all ports are gone unless informed otherwise.
-        // Kind of pessimistic but simple.
+    private static class HALCallback extends IUsbCallback.Stub {
+        public IndentingPrintWriter pw;
+        public UsbPortManager portManager;
+
+        HALCallback() {
+            super();
+        }
+
+        HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) {
+            this.pw = pw;
+            this.portManager = portManager;
+        }
+
+        public void notifyPortStatusChange(ArrayList<PortStatus> currentPortStatus, int retval) {
+            if (!portManager.mSystemReady) return;
+
+            if (retval != Status.SUCCESS) {
+                logAndPrint(Log.ERROR, pw, "port status enquiry failed");
+                return;
+            }
+
+            ArrayList<RawPortInfo> newPortInfo = new ArrayList<RawPortInfo>();
+
+            for (PortStatus current : currentPortStatus) {
+                RawPortInfo temp = new RawPortInfo(current.portName,
+                        current.supportedModes, current.currentMode,
+                        current.canChangeMode, current.currentPowerRole,
+                        current.canChangePowerRole,
+                        current.currentDataRole, current.canChangeDataRole);
+                newPortInfo.add(temp);
+                logAndPrint(Log.INFO, pw, "ClientCallback: " + current.portName);
+            }
+
+            Message message = portManager.mHandler.obtainMessage();
+            Bundle bundle = new Bundle();
+            bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
+            message.what = MSG_UPDATE_PORTS;
+            message.setData(bundle);
+            portManager.mHandler.sendMessage(message);
+            return;
+        }
+
+        public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
+            if (retval == Status.SUCCESS) {
+                logAndPrint(Log.INFO, pw, portName + "role switch successful");
+            } else {
+                logAndPrint(Log.ERROR, pw, portName + "role switch failed");
+            }
+        }
+    };
+
+    final class DeathRecipient implements HwBinder.DeathRecipient {
+        public IndentingPrintWriter pw;
+
+        DeathRecipient(IndentingPrintWriter pw) {
+            this.pw = pw;
+        }
+
+        @Override
+        public void serviceDied(long cookie) {
+            if (cookie == USB_HAL_DEATH_COOKIE) {
+                logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
+                synchronized (mLock) {
+                    mProxy = null;
+                }
+            }
+        }
+    }
+
+    final class ServiceNotification extends IServiceNotification.Stub {
+        @Override
+        public void onRegistration(String fqName, String name, boolean preexisting) {
+            logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
+            connectToProxy(null);
+        }
+    }
+
+    private void connectToProxy(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            if (mProxy != null) return;
+
+            try {
+                mProxy = IUsb.getService(sSERVICENAME);
+                mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
+                mProxy.setCallback(mHALCallback);
+                mProxy.queryPortStatus();
+            } catch (NoSuchElementException e) {
+                logAndPrint(Log.ERROR, pw, sSERVICENAME + "not found."
+                        + " Did the service failed to start ?");
+                Thread.dumpStack();
+            } catch (RemoteException e) {
+                logAndPrint(Log.ERROR, pw, sSERVICENAME + "connectToProxy: Service not responding");
+                Thread.dumpStack();
+            }
+        }
+    }
+
+    /**
+     * Simulated ports directly add the new roles to mSimulatedPorts before calling.
+     * USB hal callback populates and sends the newPortInfo.
+     */
+    private void updatePortsLocked(IndentingPrintWriter pw, ArrayList<RawPortInfo> newPortInfo) {
         for (int i = mPorts.size(); i-- > 0; ) {
             mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED;
         }
@@ -404,34 +521,18 @@
         if (!mSimulatedPorts.isEmpty()) {
             final int count = mSimulatedPorts.size();
             for (int i = 0; i < count; i++) {
-                final SimulatedPortInfo portInfo = mSimulatedPorts.valueAt(i);
+                final RawPortInfo portInfo = mSimulatedPorts.valueAt(i);
                 addOrUpdatePortLocked(portInfo.mPortId, portInfo.mSupportedModes,
                         portInfo.mCurrentMode, portInfo.mCanChangeMode,
                         portInfo.mCurrentPowerRole, portInfo.mCanChangePowerRole,
                         portInfo.mCurrentDataRole, portInfo.mCanChangeDataRole, pw);
             }
-        } else if (mHaveKernelSupport) {
-            final File[] portDirs = new File(SYSFS_CLASS).listFiles();
-            if (portDirs != null) {
-                for (File portDir : portDirs) {
-                    if (!portDir.isDirectory()) {
-                        continue;
-                    }
-
-                    // Parse the sysfs file contents.
-                    final String portId = portDir.getName();
-                    final int supportedModes = readSupportedModes(portDir);
-                    final int currentMode = readCurrentMode(portDir);
-                    final boolean canChangeMode = canChangeMode(portDir);
-                    final int currentPowerRole = readCurrentPowerRole(portDir);
-                    final boolean canChangePowerRole = canChangePowerRole(portDir);
-                    final int currentDataRole = readCurrentDataRole(portDir);
-                    final boolean canChangeDataRole = canChangeDataRole(portDir);
-                    addOrUpdatePortLocked(portId, supportedModes,
-                            currentMode, canChangeMode,
-                            currentPowerRole, canChangePowerRole,
-                            currentDataRole, canChangeDataRole, pw);
-                 }
+        } else {
+            for (RawPortInfo currentPortInfo : newPortInfo) {
+                addOrUpdatePortLocked(currentPortInfo.mPortId, currentPortInfo.mSupportedModes,
+                        currentPortInfo.mCurrentMode, currentPortInfo.mCanChangeMode,
+                        currentPortInfo.mCurrentPowerRole, currentPortInfo.mCanChangePowerRole,
+                        currentPortInfo.mCurrentDataRole, currentPortInfo.mCanChangeDataRole, pw);
             }
         }
 
@@ -457,20 +558,21 @@
         }
     }
 
+
     // Must only be called by updatePortsLocked.
     private void addOrUpdatePortLocked(String portId, int supportedModes,
-            int currentMode, boolean canChangeMode,
-            int currentPowerRole, boolean canChangePowerRole,
-            int currentDataRole, boolean canChangeDataRole,
-            IndentingPrintWriter pw) {
+                                       int currentMode, boolean canChangeMode,
+                                       int currentPowerRole, boolean canChangePowerRole,
+                                       int currentDataRole, boolean canChangeDataRole,
+                                       IndentingPrintWriter pw) {
         // Only allow mode switch capability for dual role ports.
         // Validate that the current mode matches the supported modes we expect.
         if (supportedModes != UsbPort.MODE_DUAL) {
             canChangeMode = false;
             if (currentMode != 0 && currentMode != supportedModes) {
                 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB "
-                        + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes)
-                        + ", currentMode=" + UsbPort.modeToString(currentMode));
+                            + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes)
+                            + ", currentMode=" + UsbPort.modeToString(currentMode));
                 currentMode = 0;
             }
         }
@@ -485,8 +587,8 @@
                 // Can change both power and data role independently.
                 // Assume all combinations are possible.
                 supportedRoleCombinations |=
-                        COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE
-                                | COMBO_SINK_HOST | COMBO_SINK_DEVICE;
+                    COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE
+                    | COMBO_SINK_HOST | COMBO_SINK_DEVICE;
             } else if (canChangePowerRole) {
                 // Can only change power role.
                 // Assume data role must remain at its current value.
@@ -514,24 +616,24 @@
         if (portInfo == null) {
             portInfo = new PortInfo(portId, supportedModes);
             portInfo.setStatus(currentMode, canChangeMode,
-                    currentPowerRole, canChangePowerRole,
-                    currentDataRole, canChangeDataRole,
-                    supportedRoleCombinations);
+                               currentPowerRole, canChangePowerRole,
+                               currentDataRole, canChangeDataRole,
+                               supportedRoleCombinations);
             mPorts.put(portId, portInfo);
         } else {
             // Sanity check that ports aren't changing definition out from under us.
             if (supportedModes != portInfo.mUsbPort.getSupportedModes()) {
                 logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from "
-                        + "USB port driver (should be immutable): "
-                        + "previous=" + UsbPort.modeToString(
-                                portInfo.mUsbPort.getSupportedModes())
-                        + ", current=" + UsbPort.modeToString(supportedModes));
+                            + "USB port driver (should be immutable): "
+                            + "previous=" + UsbPort.modeToString(
+                            portInfo.mUsbPort.getSupportedModes())
+                            + ", current=" + UsbPort.modeToString(supportedModes));
             }
 
             if (portInfo.setStatus(currentMode, canChangeMode,
-                    currentPowerRole, canChangePowerRole,
-                    currentDataRole, canChangeDataRole,
-                    supportedRoleCombinations)) {
+                                   currentPowerRole, canChangePowerRole,
+                                   currentDataRole, canChangeDataRole,
+                                   supportedRoleCombinations)) {
                 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
             } else {
                 portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -556,7 +658,9 @@
 
     private void sendPortChangedBroadcastLocked(PortInfo portInfo) {
         final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.addFlags(
+                Intent.FLAG_RECEIVER_FOREGROUND |
+                Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
         intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
 
@@ -570,120 +674,6 @@
         });
     }
 
-    private void scheduleUpdatePorts() {
-        if (!mHandler.hasMessages(MSG_UPDATE_PORTS)) {
-            mHandler.sendEmptyMessage(MSG_UPDATE_PORTS);
-        }
-    }
-
-    private static int readSupportedModes(File portDir) {
-        int modes = 0;
-        final String contents = readFile(portDir, SYSFS_PORT_SUPPORTED_MODES);
-        if (contents != null) {
-            if (contents.contains(PORT_MODE_DFP)) {
-                modes |= UsbPort.MODE_DFP;
-            }
-            if (contents.contains(PORT_MODE_UFP)) {
-                modes |= UsbPort.MODE_UFP;
-            }
-        }
-        return modes;
-    }
-
-    private static int readCurrentMode(File portDir) {
-        final String contents = readFile(portDir, SYSFS_PORT_MODE);
-        if (contents != null) {
-            if (contents.equals(PORT_MODE_DFP)) {
-                return UsbPort.MODE_DFP;
-            }
-            if (contents.equals(PORT_MODE_UFP)) {
-                return UsbPort.MODE_UFP;
-            }
-        }
-        return 0;
-    }
-
-    private static int readCurrentPowerRole(File portDir) {
-        final String contents = readFile(portDir, SYSFS_PORT_POWER_ROLE);
-        if (contents != null) {
-            if (contents.equals(PORT_POWER_ROLE_SOURCE)) {
-                return UsbPort.POWER_ROLE_SOURCE;
-            }
-            if (contents.equals(PORT_POWER_ROLE_SINK)) {
-                return UsbPort.POWER_ROLE_SINK;
-            }
-        }
-        return 0;
-    }
-
-    private static int readCurrentDataRole(File portDir) {
-        final String contents = readFile(portDir, SYSFS_PORT_DATA_ROLE);
-        if (contents != null) {
-            if (contents.equals(PORT_DATA_ROLE_HOST)) {
-                return UsbPort.DATA_ROLE_HOST;
-            }
-            if (contents.equals(PORT_DATA_ROLE_DEVICE)) {
-                return UsbPort.DATA_ROLE_DEVICE;
-            }
-        }
-        return 0;
-    }
-
-    private static boolean fileIsRootWritable(String path) {
-        try {
-            // If the file is user writable, then it is root writable.
-            return (Os.stat(path).st_mode & OsConstants.S_IWUSR) != 0;
-        } catch (ErrnoException e) {
-            return false;
-        }
-    }
-
-    private static boolean canChangeMode(File portDir) {
-        return fileIsRootWritable(new File(portDir, SYSFS_PORT_MODE).getPath());
-    }
-
-    private static boolean canChangePowerRole(File portDir) {
-        return fileIsRootWritable(new File(portDir, SYSFS_PORT_POWER_ROLE).getPath());
-    }
-
-    private static boolean canChangeDataRole(File portDir) {
-        return fileIsRootWritable(new File(portDir, SYSFS_PORT_DATA_ROLE).getPath());
-    }
-
-    private static String readFile(File dir, String filename) {
-        final File file = new File(dir, filename);
-        try {
-            return IoUtils.readFileAsString(file.getAbsolutePath()).trim();
-        } catch (IOException ex) {
-            return null;
-        }
-    }
-
-    private static boolean waitForState(String property, String state) {
-        // wait for the transition to complete.
-        // give up after 5 seconds.
-        // 5 seconds is probably too long, but we have seen hardware that takes
-        // over 3 seconds to change states.
-        String value = null;
-        for (int i = 0; i < 100; i++) {
-            // State transition is done when property is set to the new configuration
-            value = SystemProperties.get(property);
-            if (state.equals(value)) return true;
-            SystemClock.sleep(50);
-        }
-        Slog.e(TAG, "waitForState(" + state + ") for " + property + " FAILED: got " + value);
-        return false;
-    }
-
-    private static String propertyFromFilename(String filename) {
-        return USB_TYPEC_PROP_PREFIX + filename;
-    }
-
-    private static boolean writeFile(File dir, String filename, String contents) {
-        SystemProperties.set(propertyFromFilename(filename), contents);
-        return waitForState(USB_TYPEC_STATE, contents);
-    }
-
     private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
         Slog.println(priority, TAG, msg);
         if (pw != null) {
@@ -696,8 +686,10 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_UPDATE_PORTS: {
+                    Bundle b = msg.getData();
+                    ArrayList<RawPortInfo> PortInfo = b.getParcelableArrayList(PORT_INFO);
                     synchronized (mLock) {
-                        updatePortsLocked(null);
+                        updatePortsLocked(null, PortInfo);
                     }
                     break;
                 }
@@ -705,13 +697,6 @@
         }
     };
 
-    private final UEventObserver mUEventObserver = new UEventObserver() {
-        @Override
-        public void onUEvent(UEvent event) {
-            scheduleUpdatePorts();
-        }
-    };
-
     /**
      * Describes a USB port.
      */
@@ -762,10 +747,10 @@
     }
 
     /**
-     * Describes a simulated USB port.
-     * Roughly mirrors the information we would ordinarily get from the kernel.
+     * Used for storing the raw data from the kernel
+     * Values of the member variables mocked directly incase of emulation.
      */
-    private static final class SimulatedPortInfo {
+    private static final class RawPortInfo implements Parcelable {
         public final String mPortId;
         public final int mSupportedModes;
         public int mCurrentMode;
@@ -775,9 +760,63 @@
         public int mCurrentDataRole;
         public boolean mCanChangeDataRole;
 
-        public SimulatedPortInfo(String portId, int supportedModes) {
+        RawPortInfo(String portId, int supportedModes) {
             mPortId = portId;
             mSupportedModes = supportedModes;
         }
+
+        RawPortInfo(String portId, int supportedModes,
+                                 int currentMode, boolean canChangeMode,
+                                 int currentPowerRole, boolean canChangePowerRole,
+                                 int currentDataRole, boolean canChangeDataRole) {
+            mPortId = portId;
+            mSupportedModes = supportedModes;
+            mCurrentMode = currentMode;
+            mCanChangeMode = canChangeMode;
+            mCurrentPowerRole = currentPowerRole;
+            mCanChangePowerRole = canChangePowerRole;
+            mCurrentDataRole = currentDataRole;
+            mCanChangeDataRole = canChangeDataRole;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mPortId);
+            dest.writeInt(mSupportedModes);
+            dest.writeInt(mCurrentMode);
+            dest.writeByte((byte) (mCanChangeMode ? 1 : 0));
+            dest.writeInt(mCurrentPowerRole);
+            dest.writeByte((byte) (mCanChangePowerRole ? 1 : 0));
+            dest.writeInt(mCurrentDataRole);
+            dest.writeByte((byte) (mCanChangeDataRole ? 1 : 0));
+        }
+
+        public static final Parcelable.Creator<RawPortInfo> CREATOR =
+                new Parcelable.Creator<RawPortInfo>() {
+            @Override
+            public RawPortInfo createFromParcel(Parcel in) {
+                String id = in.readString();
+                int supportedModes = in.readInt();
+                int currentMode = in.readInt();
+                boolean canChangeMode = in.readByte() != 0;
+                int currentPowerRole = in.readInt();
+                boolean canChangePowerRole = in.readByte() != 0;
+                int currentDataRole = in.readInt();
+                boolean canChangeDataRole = in.readByte() != 0;
+                return new RawPortInfo(id, supportedModes, currentMode, canChangeMode,
+                                   currentPowerRole, canChangePowerRole,
+                                   currentDataRole, canChangeDataRole);
+            }
+
+            @Override
+            public RawPortInfo[] newArray(int size) {
+                return new RawPortInfo[size];
+            }
+        };
     }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index e03a14f..2e99b6e 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -971,7 +971,9 @@
     public void accessoryAttached(UsbAccessory accessory) {
         Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addFlags(
+                Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
 
         ArrayList<ResolveInfo> matches;
         String defaultPackage = null;
@@ -1364,7 +1366,9 @@
     private static Intent createDeviceAttachedIntent(UsbDevice device) {
         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addFlags(
+                Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         return intent;
     }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 24d5f09..7a55be4 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -182,6 +182,7 @@
         }
 
         Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
 
         if (DEBUG) {
@@ -204,6 +205,7 @@
         }
 
         Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 7d258a0..b7391b4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -377,8 +377,16 @@
      */
     public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6;
 
+    /**
+     * Set by the framework to indicate that the {@link Connection} originated from a self-managed
+     * {@link ConnectionService}.
+     * <p>
+     * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.
+     */
+    public static final int PROPERTY_SELF_MANAGED = 1<<7;
+
     //**********************************************************************************************
-    // Next PROPERTY value: 1<<7
+    // Next PROPERTY value: 1<<8
     //**********************************************************************************************
 
     /**
@@ -681,6 +689,10 @@
             builder.append("Properties:");
         }
 
+        if (can(properties, PROPERTY_SELF_MANAGED)) {
+            builder.append(isLong ? " PROPERTY_SELF_MANAGED" : " self_mng");
+        }
+
         if (can(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
             builder.append(isLong ? " PROPERTY_EMERGENCY_CALLBACK_MODE" : " ecbm");
         }
@@ -741,6 +753,7 @@
         public void onConnectionEvent(Connection c, String event, Bundle extras) {}
         /** @hide */
         public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
+        public void onAudioRouteChanged(Connection c, int audioRoute) {}
     }
 
     /**
@@ -2325,6 +2338,25 @@
     }
 
     /**
+     * Sets the audio route (speaker, bluetooth, etc...).  When this request is honored, there will
+     * be change to the {@link #getCallAudioState()}.
+     * <p>
+     * Used by self-managed {@link ConnectionService}s which wish to change the audio route for a
+     * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
+     * <p>
+     * See also {@link InCallService#setAudioRoute(int)}.
+     *
+     * @param route The audio route to use (one of {@link CallAudioState#ROUTE_BLUETOOTH},
+     *              {@link CallAudioState#ROUTE_EARPIECE}, {@link CallAudioState#ROUTE_SPEAKER}, or
+     *              {@link CallAudioState#ROUTE_WIRED_HEADSET}).
+     */
+    public final void setAudioRoute(int route) {
+        for (Listener l : mListeners) {
+            l.onAudioRouteChanged(this, route);
+        }
+    }
+
+    /**
      * Notifies this Connection that the {@link #getAudioState()} property has a new value.
      *
      * @param state The new connection audio state.
@@ -2479,6 +2511,21 @@
      */
     public void onExtrasChanged(Bundle extras) {}
 
+    /**
+     * Notifies this {@link Connection} that its {@link ConnectionService} is responsible for
+     * displaying its incoming call user interface for the {@link Connection}.
+     * <p>
+     * Will only be called for incoming calls added via a self-managed {@link ConnectionService}
+     * (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}), where the {@link ConnectionService}
+     * should show its own incoming call user interface.
+     * <p>
+     * Where there are ongoing calls in other self-managed {@link ConnectionService}s, or in a
+     * regular {@link ConnectionService}, the Telecom framework will display its own incoming call
+     * user interface to allow the user to choose whether to answer the new incoming call and
+     * disconnect other ongoing calls, or to reject the new incoming call.
+     */
+    public void onShowIncomingCallUi() {}
+
     static String toLogSafePhoneNumber(String number) {
         // For unknown number, log empty string.
         if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index aba38fe..2343462 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -33,6 +33,7 @@
     private final Bundle mExtras;
     private final int mVideoState;
     private final String mTelecomCallId;
+    private final boolean mShouldShowIncomingCallUi;
 
     /**
      * @param accountHandle The accountHandle which should be used to place the call.
@@ -43,7 +44,7 @@
             PhoneAccountHandle accountHandle,
             Uri handle,
             Bundle extras) {
-        this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null);
+        this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null, false);
     }
 
     /**
@@ -57,7 +58,7 @@
             Uri handle,
             Bundle extras,
             int videoState) {
-        this(accountHandle, handle, extras, videoState, null);
+        this(accountHandle, handle, extras, videoState, null, false);
     }
 
     /**
@@ -66,6 +67,10 @@
      * @param extras Application-specific extra data.
      * @param videoState Determines the video state for the connection.
      * @param telecomCallId The telecom call ID.
+     * @param shouldShowIncomingCallUi For a self-managed {@link ConnectionService}, will be
+     *                                 {@code true} if the {@link ConnectionService} should show its
+     *                                 own incoming call UI for an incoming call.  When
+     *                                 {@code false}, Telecom shows the incoming call UI.
      * @hide
      */
     public ConnectionRequest(
@@ -73,12 +78,14 @@
             Uri handle,
             Bundle extras,
             int videoState,
-            String telecomCallId) {
+            String telecomCallId,
+            boolean shouldShowIncomingCallUi) {
         mAccountHandle = accountHandle;
         mAddress = handle;
         mExtras = extras;
         mVideoState = videoState;
         mTelecomCallId = telecomCallId;
+        mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
     }
 
     private ConnectionRequest(Parcel in) {
@@ -87,6 +94,7 @@
         mExtras = in.readParcelable(getClass().getClassLoader());
         mVideoState = in.readInt();
         mTelecomCallId = in.readString();
+        mShouldShowIncomingCallUi = in.readInt() == 1;
     }
 
     /**
@@ -129,6 +137,18 @@
         return mTelecomCallId;
     }
 
+    /**
+     * For a self-managed {@link ConnectionService}, indicates for an incoming call whether the
+     * {@link ConnectionService} should show its own incoming call UI for an incoming call.
+     *
+     * @return {@code true} if the {@link ConnectionService} should show its own incoming call UI.
+     * When {@code false}, Telecom shows the incoming call UI for the call.
+     * @hide
+     */
+    public boolean shouldShowIncomingCallUi() {
+        return mShouldShowIncomingCallUi;
+    }
+
     @Override
     public String toString() {
         return String.format("ConnectionRequest %s %s",
@@ -165,5 +185,6 @@
         destination.writeParcelable(mExtras, 0);
         destination.writeInt(mVideoState);
         destination.writeString(mTelecomCallId);
+        destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0);
     }
 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index b119e16..d0ccd55 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -42,10 +42,15 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * An abstract service that should be implemented by any apps which can make phone calls (VoIP or
- * otherwise) and want those calls to be integrated into the built-in phone app.
- * Once implemented, the {@code ConnectionService} needs two additional steps before it will be
- * integrated into the phone app:
+ * An abstract service that should be implemented by any apps which either:
+ * <ol>
+ *     <li>Can make phone calls (VoIP or otherwise) and want those calls to be integrated into the
+ *     built-in phone app.  Referred to as a <b>system managed</b> {@link ConnectionService}.</li>
+ *     <li>Are a standalone calling app and don't want their calls to be integrated into the
+ *     built-in phone app.  Referred to as a <b>self managed</b> {@link ConnectionService}.</li>
+ * </ol>
+ * Once implemented, the {@link ConnectionService} needs to take the following steps so that Telecom
+ * will bind to it:
  * <p>
  * 1. <i>Registration in AndroidManifest.xml</i>
  * <br/>
@@ -63,16 +68,20 @@
  * <br/>
  * See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information.
  * <p>
- * Once registered and enabled by the user in the phone app settings, telecom will bind to a
- * {@code ConnectionService} implementation when it wants that {@code ConnectionService} to place
- * a call or the service has indicated that is has an incoming call through
- * {@link TelecomManager#addNewIncomingCall}. The {@code ConnectionService} can then expect a call
- * to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection} wherein it
- * should provide a new instance of a {@link Connection} object.  It is through this
- * {@link Connection} object that telecom receives state updates and the {@code ConnectionService}
+ * System managed {@link ConnectionService}s must be enabled by the user in the phone app settings
+ * before Telecom will bind to them.  Self-manged {@link ConnectionService}s must be granted the
+ * appropriate permission before Telecom will bind to them.
+ * <p>
+ * Once registered and enabled by the user in the phone app settings or granted permission, telecom
+ * will bind to a {@link ConnectionService} implementation when it wants that
+ * {@link ConnectionService} to place a call or the service has indicated that is has an incoming
+ * call through {@link TelecomManager#addNewIncomingCall}. The {@link ConnectionService} can then
+ * expect a call to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection}
+ * wherein it should provide a new instance of a {@link Connection} object.  It is through this
+ * {@link Connection} object that telecom receives state updates and the {@link ConnectionService}
  * receives call-commands such as answer, reject, hold and disconnect.
  * <p>
- * When there are no more live calls, telecom will unbind from the {@code ConnectionService}.
+ * When there are no more live calls, telecom will unbind from the {@link ConnectionService}.
  */
 public abstract class ConnectionService extends Service {
     /**
@@ -1054,6 +1063,7 @@
             }
         }
 
+        @Override
         public void onExtrasRemoved(Connection c, List<String> keys) {
             String id = mIdByConnection.get(c);
             if (id != null) {
@@ -1061,7 +1071,6 @@
             }
         }
 
-
         @Override
         public void onConnectionEvent(Connection connection, String event, Bundle extras) {
             String id = mIdByConnection.get(connection);
@@ -1069,6 +1078,14 @@
                 mAdapter.onConnectionEvent(id, event, extras);
             }
         }
+
+        @Override
+        public void onAudioRouteChanged(Connection c, int audioRoute) {
+            String id = mIdByConnection.get(c);
+            if (id != null) {
+                mAdapter.setAudioRoute(id, audioRoute);
+            }
+        }
     };
 
     /** {@inheritDoc} */
@@ -1146,6 +1163,13 @@
                         connection.getDisconnectCause(),
                         createIdList(connection.getConferenceables()),
                         connection.getExtras()));
+
+        if (isIncoming && request.shouldShowIncomingCallUi() &&
+                (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) ==
+                        Connection.PROPERTY_SELF_MANAGED) {
+            // Tell ConnectionService to show its incoming call UX.
+            connection.onShowIncomingCallUi();
+        }
         if (isUnknown) {
             triggerConferenceRecalculate();
         }
@@ -1587,6 +1611,38 @@
     }
 
     /**
+     * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+     * incoming {@link Connection} was denied.
+     * <p>
+     * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
+     * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time.
+     * The {@link ConnectionService} is responsible for silently rejecting the new incoming
+     * {@link Connection}.
+     * <p>
+     * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
+     *
+     * @param request The incoming connection request.
+     */
+    public void onCreateIncomingConnectionFailed(ConnectionRequest request) {
+    }
+
+    /**
+     * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+     * outgoing {@link Connection} was denied.
+     * <p>
+     * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
+     * {@link Connection}, but Telecom has determined that the call cannot be placed at this time.
+     * The {@link ConnectionService} is responisible for informing the user that the
+     * {@link Connection} cannot be made at this time.
+     * <p>
+     * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
+     *
+     * @param request The outgoing connection request.
+     */
+    public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {
+    }
+
+    /**
      * Trigger recalculate functinality for conference calls. This is used when a Telephony
      * Connection is part of a conference controller but is not yet added to Connection
      * Service and hence cannot be added to the conference call.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index f3fada9..9542b73 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -515,6 +515,23 @@
     }
 
     /**
+     * Sets the audio route associated with a {@link Connection}.
+     *
+     * @param callId The unique ID of the call.
+     * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
+     */
+    void setAudioRoute(String callId, int audioRoute) {
+        Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute));
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession());
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+
+    /**
      * Informs Telecom of a connection level event.
      *
      * @param callId The unique ID of the call.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index afe5e33..cc437f9 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -67,6 +67,7 @@
     private static final int MSG_ON_CONNECTION_EVENT = 26;
     private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
     private static final int MSG_SET_PULLING = 28;
+    private static final int MSG_SET_AUDIO_ROUTE = 29;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -289,6 +290,16 @@
                     }
                     break;
                 }
+                case MSG_SET_AUDIO_ROUTE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.setAudioRoute((String) args.arg1, args.argi1,
+                                (Session.Info) args.arg2);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
             }
         }
     };
@@ -507,6 +518,17 @@
         }
 
         @Override
+        public final void setAudioRoute(String connectionId, int audioRoute,
+                Session.Info sessionInfo) {
+
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = connectionId;
+            args.argi1 = audioRoute;
+            args.arg2 = sessionInfo;
+            mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, args).sendToTarget();
+        }
+
+        @Override
         public final void onConnectionEvent(String connectionId, String event, Bundle extras,
                 Session.Info sessionInfo) {
             SomeArgs args = SomeArgs.obtain();
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index ca54486..845a103 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -189,6 +189,21 @@
     public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 0x400;
 
     /**
+     * Flag indicating that this {@link PhoneAccount} is responsible for managing its own
+     * {@link Connection}s.  This type of {@link PhoneAccount} is ideal for use with standalone
+     * calling apps which do not wish to use the default phone app for {@link Connection} UX,
+     * but which want to leverage the call and audio routing capabilities of the Telecom framework.
+     * <p>
+     * When set, {@link Connection}s created by the self-managed {@link ConnectionService} will not
+     * be surfaced to implementations of the {@link InCallService} API.  Thus it is the
+     * responsibility of a self-managed {@link ConnectionService} to provide a user interface for
+     * its {@link Connection}s.
+     * <p>
+     * Self-managed {@link Connection}s will, however, be displayed on connected Bluetooth devices.
+     */
+    public static final int CAPABILITY_SELF_MANAGED = 0x800;
+
+    /**
      * URI scheme for telephone number URIs.
      */
     public static final String SCHEME_TEL = "tel";
@@ -692,6 +707,14 @@
         mIsEnabled = isEnabled;
     }
 
+    /**
+     * @return {@code true} if the {@link PhoneAccount} is self-managed, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isSelfManaged() {
+        return (mCapabilities & CAPABILITY_SELF_MANAGED) == CAPABILITY_SELF_MANAGED;
+    }
+
     //
     // Parcelable implementation
     //
@@ -815,6 +838,9 @@
      */
     private String capabilitiesToString() {
         StringBuilder sb = new StringBuilder();
+        if (hasCapabilities(CAPABILITY_SELF_MANAGED)) {
+            sb.append("SelfManaged ");
+        }
         if (hasCapabilities(CAPABILITY_SUPPORTS_VIDEO_CALLING)) {
             sb.append("SuppVideo ");
         }
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index d8a226a..0c7404a 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -389,6 +389,15 @@
         }
 
         @Override
+        public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) {
+            if (hasConnection(callId)) {
+                // TODO(3pcalls): handle this for remote connections.
+                // Likely we don't want to do anything since it doesn't make sense for self-managed
+                // connections to go through a connection mgr.
+            }
+        }
+
+        @Override
         public void onConnectionEvent(String callId, String event, Bundle extras,
                 Session.Info sessionInfo) {
             if (mConnectionById.containsKey(callId)) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f12886a..ba7b6a1 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -58,12 +58,15 @@
      * Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
      * {@link android.telecom.ConnectionService} that Telecom should bind to. Telecom will then
      * ask the connection service for more information about the call prior to showing any UI.
+     *
+     * @deprecated Use {@link #addNewIncomingCall} instead.
      */
     public static final String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
 
     /**
      * Similar to {@link #ACTION_INCOMING_CALL}, but is used only by Telephony to add a new
      * sim-initiated MO call for carrier testing.
+     * @deprecated Use {@link #addNewUnknownCall} instead.
      * @hide
      */
     public static final String ACTION_NEW_UNKNOWN_CALL = "android.telecom.action.NEW_UNKNOWN_CALL";
@@ -1202,17 +1205,25 @@
 
     /**
      * Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
-     * has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
-     * with {@link #registerPhoneAccount} and the user must have enabled the corresponding
-     * {@link PhoneAccount}. This can be checked using {@link #getPhoneAccount}. Once invoked, this
-     * method will cause the system to bind to the {@link ConnectionService} associated with the
-     * {@link PhoneAccountHandle} and request additional information about the call
-     * (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming
+     * has an incoming call. For managed {@link ConnectionService}s, the specified
+     * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
+     * the user must have enabled the corresponding {@link PhoneAccount}.  This can be checked using
+     * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have
+     * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call.
+     * <p>
+     * Once invoked, this method will cause the system to bind to the {@link ConnectionService}
+     * associated with the {@link PhoneAccountHandle} and request additional information about the
+     * call (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming
      * call UI.
      * <p>
-     * A {@link SecurityException} will be thrown if either the {@link PhoneAccountHandle} does not
-     * correspond to a registered {@link PhoneAccount} or the associated {@link PhoneAccount} is not
-     * currently enabled by the user.
+     * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either
+     * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or
+     * the associated {@link PhoneAccount} is not currently enabled by the user.
+     * <p>
+     * For a self-managed {@link ConnectionService}, a {@link SecurityException} will be thrown if
+     * the {@link PhoneAccount} has {@link PhoneAccount#CAPABILITY_SELF_MANAGED} and the calling app
+     * does not have {@link android.Manifest.permission#MANAGE_OWN_CALLS}.
+     *
      * @param phoneAccount A {@link PhoneAccountHandle} registered with
      *            {@link #registerPhoneAccount}.
      * @param extras A bundle that will be passed through to
@@ -1379,7 +1390,8 @@
      * method-caller is either the user selected default dialer app or preloaded system dialer
      * app, then emergency calls will also be allowed.
      *
-     * Requires permission: {@link android.Manifest.permission#CALL_PHONE}
+     * Placing a call via a managed {@link ConnectionService} requires permission:
+     * {@link android.Manifest.permission#CALL_PHONE}
      *
      * Usage example:
      * <pre>
@@ -1396,11 +1408,20 @@
      *   <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
      *   <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
      * </ul>
+     * <p>
+     * An app which implements the self-managed {@link ConnectionService} API uses
+     * {@link #placeCall(Uri, Bundle)} to inform Telecom of a new outgoing call.  A self-managed
+     * {@link ConnectionService} must include {@link #EXTRA_PHONE_ACCOUNT_HANDLE} to specify its
+     * associated {@link android.telecom.PhoneAccountHandle}.
+     *
+     * Self-managed {@link ConnectionService}s require permission
+     * {@link android.Manifest.permission#MANAGE_OWN_CALLS}.
      *
      * @param address The address to make the call to.
      * @param extras Bundle of extras to use with the call.
      */
-    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+    @RequiresPermission(anyOf = {android.Manifest.permission.CALL_PHONE,
+            android.Manifest.permission.MANAGE_OWN_CALLS})
     public void placeCall(Uri address, Bundle extras) {
         ITelecomService service = getTelecomService();
         if (service != null) {
@@ -1476,6 +1497,71 @@
         return result;
     }
 
+    /**
+     * Determines whether Telecom would permit an incoming call to be added via the
+     * {@link #addNewIncomingCall(PhoneAccountHandle, Bundle)} API for the specified
+     * {@link PhoneAccountHandle}.
+     * <p>
+     * A {@link ConnectionService} may not add a call for the specified {@link PhoneAccountHandle}
+     * in the following situations:
+     * <ul>
+     *     <li>{@link PhoneAccount} does not have property
+     *     {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set (i.e. it is a managed
+     *     {@link ConnectionService}), and the active or held call limit has
+     *     been reached.</li>
+     *     <li>There is an ongoing emergency call.</li>
+     * </ul>
+     *
+     * @param phoneAccountHandle The {@link PhoneAccountHandle} the call will be added for.
+     * @return {@code true} if telecom will permit an incoming call to be added, {@code false}
+     *      otherwise.
+     */
+    public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                return service.isIncomingCallPermitted(phoneAccountHandle);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error isIncomingCallPermitted", e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether Telecom would permit an outgoing call to be placed via the
+     * {@link #placeCall(Uri, Bundle)} API for the specified {@link PhoneAccountHandle}.
+     * <p>
+     * A {@link ConnectionService} may not place a call for the specified {@link PhoneAccountHandle}
+     * in the following situations:
+     * <ul>
+     *     <li>{@link PhoneAccount} does not have property
+     *     {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set (i.e. it is a managed
+     *     {@link ConnectionService}), and the active, held or ringing call limit has
+     *     been reached.</li>
+     *     <li>{@link PhoneAccount} has property {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set
+     *     (i.e. it is a self-managed {@link ConnectionService} and there is an ongoing call in
+     *     another {@link ConnectionService}.</li>
+     *     <li>There is an ongoing emergency call.</li>
+     * </ul>
+     *
+     * @param phoneAccountHandle The {@link PhoneAccountHandle} the call will be added for.
+     * @return {@code true} if telecom will permit an outgoing call to be placed, {@code false}
+     *      otherwise.
+     */
+    public boolean isOutgoingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                return service.isOutgoingCallPermitted(phoneAccountHandle);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error isOutgoingCallPermitted", e);
+            }
+        }
+        return false;
+    }
+
+
     private ITelecomService getTelecomService() {
         if (mTelecomServiceOverride != null) {
             return mTelecomServiceOverride;
diff --git a/telecomm/java/android/telecom/package-info.java b/telecomm/java/android/telecom/package-info.java
new file mode 100644
index 0000000..a4140e5
--- /dev/null
+++ b/telecomm/java/android/telecom/package-info.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+/**
+ * The Android Telecom framework is responsible for managing calls on an Android device.  This can
+ * include SIM-based calls using the {@code Telephony} framework, VOIP calls using SIP (e.g. the
+ * {@code SipConnectionService}), or via a third-party VOIP
+ * {@link android.telecom.ConnectionService}.  Telecom acts as a switchboard, routing calls and
+ * audio focus between {@link android.telecom.Connection}s provided by
+ * {@link android.telecom.ConnectionService} implementations, and
+ * {@link android.telecom.InCallService} implementations which provide a user interface for calls.
+ * <p>
+ * Android supports the following calling use cases (with increasing level of complexity):
+ * <ul>
+ *     <li>Implement the self-managed {@link android.telecom.ConnectionService} API - this is ideal
+ *     for developers of standalone calling apps which do not wish to show their calls within the
+ *     default phone app, and do not wish to have other calls shown in their user interface.  Using
+ *     a self-managed {@link android.telecom.ConnectionService} implementation within your
+ *     standalone calling app helps you ensure that your app will interoperate not only with native
+ *     telephony calling on the device, but also other standalone calling apps implementing this
+ *     API.  It also manages audio routing and focus for you.</li>
+ *     <li>Implement the managed {@link android.telecom.ConnectionService} API - facilitates
+ *     development of a calling solution that relies on the existing device phone application (see
+ *     {@link android.telecom.TelecomManager#getDefaultDialerPackage()}) to provide the user
+ *     interface for calls.  An example might be a third party implementation of SIP calling, or a
+ *     VOIP calling service.  A {@link android.telecom.ConnectionService} alone provides only the
+ *     means of connecting calls, but has no associated user interface.</li>
+ *     <li>Implement the {@link android.telecom.InCallService} API - facilitates development of a
+ *     replacement for the device's default Phone/Dialer app.  The
+ *     {@link android.telecom.InCallService} alone does not have any calling capability and consists
+ *     of the user-interface side of calling only.  An {@link android.telecom.InCallService} must
+ *     handle all Calls the Telecom framework is aware of.  It must not make assumptions about the
+ *     nature of the calls (e.g. assuming calls are SIM-based telephony calls), and should not
+ *     implement calling restrictions based on any one {@link android.telecom.ConnectionService}
+ *     (e.g. it should not enforce Telephony restrictions for video calls).</li>
+ *     <li>Implement both the {@link android.telecom.InCallService} and
+ *     {@link android.telecom.ConnectionService} API - ideal if you wish to create your own
+ *     {@link android.telecom.ConnectionService} based calling solution, complete with its own
+ *     full user interface, while showing all other Android calls in the same user interface.  Using
+ *     this approach, you must still ensure that your {@link android.telecom.InCallService} makes
+ *     no assumption about the source of the calls it displays.  You must also ensure that your
+ *     {@link android.telecom.ConnectionService} implementation can still function without the
+ *     default phone app being set to your custom {@link android.telecom.InCallService}.</li>
+ * </ul>
+ */
+package android.telecom;
\ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 002c3bb..b58f8bc 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -102,6 +102,8 @@
 
     void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
 
+    void setAudioRoute(String callId, int audioRoute, in Session.Info sessionInfo);
+
     void onConnectionEvent(String callId, String event, in Bundle extras,
     in Session.Info sessionInfo);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 5c412e7..6ca0bc5 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -249,4 +249,14 @@
     * @see TelecomServiceImpl#createManageBlockedNumbersIntent
     **/
     Intent createManageBlockedNumbersIntent();
+
+    /**
+     * @see TelecomServiceImpl#isIncomingCallPermitted
+     */
+    boolean isIncomingCallPermitted(in PhoneAccountHandle phoneAccountHandle);
+
+    /**
+     * @see TelecomServiceImpl#isOutgoingCallPermitted
+     */
+    boolean isOutgoingCallPermitted(in PhoneAccountHandle phoneAccountHandle);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 288c9ab..6c45233 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -259,6 +259,12 @@
             KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
 
     /**
+     * Override the device's configuration for the ImsService to use for this SIM card.
+     */
+    public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING =
+            "config_ims_package_override_string";
+
+    /**
      * Override the platform's notion of a network operator being considered roaming.
      * Value is string array of SIDs to be considered roaming for 3GPP2 RATs.
      */
@@ -598,6 +604,38 @@
         "vvm_cellular_data_required_bool";
 
     /**
+     * The default OMTP visual voicemail client prefix to use. Defaulted to "//VVM"
+     */
+    public static final String KEY_VVM_CLIENT_PREFIX_STRING =
+            "vvm_client_prefix_string";
+
+    /**
+     * Whether to use SSL to connect to the visual voicemail IMAP server. Defaulted to false.
+     */
+    public static final String KEY_VVM_SSL_ENABLED_BOOL = "vvm_ssl_enabled_bool";
+
+    /**
+     * A set of capabilities that should not be used even if it is reported by the visual voicemail
+     * IMAP CAPABILITY command.
+     */
+    public static final String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY =
+            "vvm_disabled_capabilities_string_array";
+
+    /**
+     * Whether legacy mode should be used when the visual voicemail client is disabled.
+     *
+     * <p>Legacy mode is a mode that on the carrier side visual voicemail is still activated, but on
+     * the client side all network operations are disabled. SMSs are still monitored so a new
+     * message SYNC SMS will be translated to show a message waiting indicator, like traditional
+     * voicemails.
+     *
+     * <p>This is for carriers that does not support VVM deactivation so voicemail can continue to
+     * function without the data cost.
+     */
+    public static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL =
+            "vvm_legacy_mode_enabled_bool";
+
+    /**
      * Whether to prefetch audio data on new voicemail arrival, defaulted to true.
      */
     public static final String KEY_VVM_PREFETCH_BOOL = "vvm_prefetch_bool";
@@ -605,10 +643,20 @@
     /**
      * The package name of the carrier's visual voicemail app to ensure that dialer visual voicemail
      * and carrier visual voicemail are not active at the same time.
+     *
+     * @deprecated use {@link #KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY}.
      */
+    @Deprecated
     public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
 
     /**
+     * A list of the carrier's visual voicemail app package names to ensure that dialer visual
+     * voicemail and carrier visual voicemail are not active at the same time.
+     */
+    public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY =
+            "carrier_vvm_package_name_string_array";
+
+    /**
      * Flag specifying whether ICCID is showed in SIM Status screen, default to false.
      */
     public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
@@ -878,39 +926,6 @@
      public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
 
     /**
-     * A list of component name of carrier signalling receivers which are interested in intent
-     * android.intent.action.CARRIER_SIGNAL_REDIRECTED.
-     * Example:
-     * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
-     * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
-     * @hide
-     */
-    public static final String KEY_SIGNAL_REDIRECTION_RECEIVER_STRING_ARRAY =
-            "signal_redirection_receiver_string_array";
-
-    /**
-     * A list of component name of carrier signalling receivers which are interested in intent
-     * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED.
-     * Example:
-     * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
-     * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
-     * @hide
-     */
-    public static final String KEY_SIGNAL_DCFAILURE_RECEIVER_STRING_ARRAY =
-            "signal_dcfailure_receiver_string_array";
-
-    /**
-     * A list of component name of carrier signalling receivers which are interested in intent
-     * android.intent.action.CARRIER_SIGNAL_PCO_VALUE.
-     * Example:
-     * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
-     * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
-     * @hide
-     */
-    public static final String KEY_SIGNAL_PCO_RECEIVER_STRING_ARRAY =
-            "signal_pco_receiver_string_array";
-
-    /**
      * Defines carrier-specific actions which act upon
      * android.intent.action.CARRIER_SIGNAL_REDIRECTED, used for customization of the
      * default carrier app
@@ -963,6 +978,42 @@
             "carrier_default_redirection_url_string_array";
 
     /**
+     * Each config includes the componentName of the carrier app, followed by a list of interesting
+     * signals(declared in the manifest) which could wake up the app.
+     * @see com.android.internal.telephony.TelephonyIntents
+     * Example:
+     * <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
+     * android.intent.action.CARRIER_SIGNAL_REDIRECTED,
+     * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+     * </item>
+     * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
+     * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+     * </item>
+     * @hide
+     */
+    public static final String KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY =
+            "carrier_app_wake_signal_config";
+
+    /**
+     * Each config includes the componentName of the carrier app, followed by a list of interesting
+     * signals for the app during run-time. The list of signals(intents) are targeting on run-time
+     * broadcast receivers only, aiming to avoid unnecessary wake-ups and should not be declared in
+     * the app's manifest.
+     * @see com.android.internal.telephony.TelephonyIntents
+     * Example:
+     * <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
+     * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+     * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+     * </item>
+     * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
+     * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+     * </item>
+     * @hide
+     */
+    public static final String KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY =
+            "carrier_app_no_wake_signal_config";
+
+    /**
      * Determines whether the carrier supports making non-emergency phone calls while the phone is
      * in emergency callback mode.  Default value is {@code true}, meaning that non-emergency calls
      * are allowed in emergency callback mode.
@@ -1305,8 +1356,13 @@
         sDefaults.putInt(KEY_VVM_PORT_NUMBER_INT, 0);
         sDefaults.putString(KEY_VVM_TYPE_STRING, "");
         sDefaults.putBoolean(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL, false);
+        sDefaults.putString(KEY_VVM_CLIENT_PREFIX_STRING,"//VVM");
+        sDefaults.putBoolean(KEY_VVM_SSL_ENABLED_BOOL,false);
+        sDefaults.putStringArray(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_VVM_LEGACY_MODE_ENABLED_BOOL,false);
         sDefaults.putBoolean(KEY_VVM_PREFETCH_BOOL, true);
         sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
+        sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, "");
@@ -1346,6 +1402,7 @@
                 });
         sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putString(KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
         sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null);
@@ -1413,10 +1470,14 @@
         sDefaults.putString(KEY_RCS_CONFIG_SERVER_URL_STRING, "");
 
         // Carrier Signalling Receivers
-        sDefaults.putStringArray(KEY_SIGNAL_REDIRECTION_RECEIVER_STRING_ARRAY, null);
-        sDefaults.putStringArray(KEY_SIGNAL_DCFAILURE_RECEIVER_STRING_ARRAY, null);
-        sDefaults.putStringArray(KEY_SIGNAL_PCO_RECEIVER_STRING_ARRAY, null);
         sDefaults.putString(KEY_CARRIER_SETUP_APP_STRING, "");
+        sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{
+                        "com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:" +
+                                "android.intent.action.CARRIER_SIGNAL_REDIRECTED"
+                });
+        sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
+
 
         // Default carrier app configurations
         sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY,
@@ -1504,12 +1565,12 @@
      * moment.
      * </p>
      * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
      * <p>
      * This method returns before the reload has completed, and
      * {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
      * arbitrary thread.
      * </p>
+     * @see #hasCarrierPrivileges
      */
     public void notifyConfigChangedForSubId(int subId) {
         try {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index eb838fa..f53c576 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -373,13 +373,46 @@
             "android.telephony.action.EMERGENCY_ASSISTANCE";
 
     /**
+     * A boolean meta-data value indicating whether the voicemail settings should be hidden in the
+     * call settings page launched by
+     * {@link android.telecom.TelecomManager#ACTION_SHOW_CALL_SETTINGS}.
+     * Dialer implementations (see {@link android.telecom.TelecomManager#getDefaultDialerPackage()})
+     * which would also like to manage voicemail settings should set this meta-data to {@code true}
+     * in the manifest registration of their application.
+     *
+     * @see android.telecom.TelecomManager#ACTION_SHOW_CALL_SETTINGS
+     * @see #ACTION_CONFIGURE_VOICEMAIL
+     * @see #EXTRA_HIDE_PUBLIC_SETTINGS
+     */
+    public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU =
+            "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
+
+    /**
      * Open the voicemail settings activity to make changes to voicemail configuration.
+     *
+     * <p>
+     * The {@link #EXTRA_HIDE_PUBLIC_SETTINGS} hides settings the dialer will modify through public
+     * API if set.
+     *
+     * @see #EXTRA_HIDE_PUBLIC_SETTINGS
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_CONFIGURE_VOICEMAIL =
             "android.telephony.action.CONFIGURE_VOICEMAIL";
 
     /**
+     * The boolean value indicating whether the voicemail settings activity launched by {@link
+     * #ACTION_CONFIGURE_VOICEMAIL} should hide settings accessible through public API. This is
+     * used by dialer implementations which provides their own voicemail settings UI, but still
+     * needs to expose device specific voicemail settings to the user.
+     *
+     * @see #ACTION_CONFIGURE_VOICEMAIL
+     * @see #METADATA_HIDE_VOICEMAIL_SETTINGS_MENU
+     */
+    public static final String EXTRA_HIDE_PUBLIC_SETTINGS =
+            "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
+
+    /**
      * @hide
      */
     public static final boolean EMERGENCY_ASSISTANCE_ENABLED = true;
@@ -388,13 +421,13 @@
      * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
      * for a String containing the new call state.
      *
-     * @see #EXTRA_STATE_IDLE
-     * @see #EXTRA_STATE_RINGING
-     * @see #EXTRA_STATE_OFFHOOK
-     *
      * <p class="note">
      * Retrieve with
      * {@link android.content.Intent#getStringExtra(String)}.
+     *
+     * @see #EXTRA_STATE_IDLE
+     * @see #EXTRA_STATE_RINGING
+     * @see #EXTRA_STATE_OFFHOOK
      */
     public static final String EXTRA_STATE = PhoneConstants.STATE_KEY;
 
@@ -1104,12 +1137,12 @@
     /**
      * Returns the neighboring cell information of the device.
      *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
+     *
      * @return List of NeighboringCellInfo or null if info unavailable.
      *
-     * <p>Requires Permission:
-     * (@link android.Manifest.permission#ACCESS_COARSE_UPDATES}
-     *
-     * @deprecated Use (@link getAllCellInfo} which returns a superset of the information
+     * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
      *             from NeighboringCellInfo.
      */
     @Deprecated
@@ -1655,6 +1688,11 @@
     /**
      * Returns a constant indicating the radio technology (network type)
      * currently in use on the device for data transmission.
+     *
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
      * @return the network type
      *
      * @see #NETWORK_TYPE_UNKNOWN
@@ -1673,10 +1711,6 @@
      * @see #NETWORK_TYPE_LTE
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
-     *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
     public int getDataNetworkType() {
         return getDataNetworkType(getSubId());
@@ -2657,6 +2691,28 @@
         return false;
     }
 
+
+    /**
+     * Returns the package responsible of processing visual voicemail for the phone account.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+     * READ_PHONE_STATE}
+     */
+    @Nullable
+    public String getVisualVoicemailPackageName(PhoneAccountHandle phoneAccountHandle) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony
+                        .getVisualVoicemailPackageName(mContext.getOpPackageName(),
+                                phoneAccountHandle);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
     /**
      * Enables the visual voicemail SMS filter for a phone account. When the filter is
      * enabled, Incoming SMS messages matching the OMTP VVM SMS interface will be redirected to the
@@ -4791,6 +4847,20 @@
         }
     }
 
+   /*
+    * @return true, if the device is currently on a technology (e.g. UMTS or LTE) which can support
+    * voice and data simultaneously. This can change based on location or network condition.
+    */
+    public boolean isConcurrentVoiceAndDataAllowed() {
+        try {
+            ITelephony telephony = getITelephony();
+            return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(mSubId));
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isConcurrentVoiceAndDataAllowed", e);
+        }
+        return false;
+    }
+
     /** @hide */
     @SystemApi
     public boolean handlePinMmi(String dialString) {
@@ -5809,10 +5879,17 @@
      * Set the allowed carrier list for slotId
      * Require system privileges. In the future we may add this to carrier APIs.
      *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     *
+     * <p>This method works only on devices with {@link
+     * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+     *
      * @return The number of carriers set successfully. Should be length of
      * carrierList on success; -1 on error.
      * @hide
      */
+    @SystemApi
     public int setAllowedCarriers(int slotId, List<CarrierIdentifier> carriers) {
         try {
             ITelephony service = getITelephony();
@@ -5821,6 +5898,8 @@
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
+        } catch (NullPointerException e) {
+            Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
         }
         return -1;
     }
@@ -5829,10 +5908,17 @@
      * Get the allowed carrier list for slotId.
      * Require system privileges. In the future we may add this to carrier APIs.
      *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * <p>This method returns valid data on devices with {@link
+     * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+     *
      * @return List of {@link android.telephony.CarrierIdentifier}; empty list
      * means all carriers are allowed.
      * @hide
      */
+    @SystemApi
     public List<CarrierIdentifier> getAllowedCarriers(int slotId) {
         try {
             ITelephony service = getITelephony();
@@ -5841,6 +5927,8 @@
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
+        } catch (NullPointerException e) {
+            Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
         }
         return new ArrayList<CarrierIdentifier>(0);
     }
diff --git a/telephony/java/android/telephony/ims/ImsServiceBase.java b/telephony/java/android/telephony/ims/ImsServiceBase.java
new file mode 100644
index 0000000..0b50eca
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsServiceBase.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Base ImsService Implementation, which is used by the ImsResolver to bind.
+ * @hide
+ */
+@SystemApi
+public class ImsServiceBase extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
new file mode 100644
index 0000000..0509d60
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ * @hide
+ */
+public class ImsFeature {
+
+    // Invalid feature value
+    public static final int INVALID = -1;
+    // ImsFeatures that are defined in the Manifests
+    public static final int EMERGENCY_MMTEL = 0;
+    public static final int MMTEL = 1;
+    public static final int RCS = 2;
+    // Total number of features defined
+    public static final int MAX = 3;
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
new file mode 100644
index 0000000..fa86a43
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+/**
+ * {@hide}
+ */
+interface IImsServiceController {
+    void createImsFeature(int slotId, int feature);
+    void removeImsFeature(int slotId, int feature);
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
new file mode 100644
index 0000000..0a36b6b
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+/**
+ * {@hide}
+ */
+oneway interface IImsServiceFeatureListener {
+    void imsFeatureCreated(int slotId, int feature);
+    void imsFeatureRemoved(int slotId, int feature);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c0d6768ae..ec419d7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -466,12 +466,21 @@
      */
     int getVoiceMessageCountForSubscriber(int subId);
 
+    /**
+      * Returns true if current state supports both voice and data
+      * simultaneously. This can change based on location or network condition.
+      */
+    boolean isConcurrentVoiceAndDataAllowed(int subId);
+
     oneway void setVisualVoicemailEnabled(String callingPackage,
             in PhoneAccountHandle accountHandle, boolean enabled);
 
     boolean isVisualVoicemailEnabled(String callingPackage,
             in PhoneAccountHandle accountHandle);
 
+    String getVisualVoicemailPackageName(String callingPackage,
+            in PhoneAccountHandle phoneAccountHandle);
+
     // Not oneway, caller needs to make sure the vaule is set before receiving a SMS
     void enableVisualVoicemailSmsFilter(String callingPackage, int subId,
             in VisualVoicemailSmsFilterSettings settings);
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 0e9a485..3c36e42 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := core-oj core-libart junit framework
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
 
 LOCAL_MODULE:= android.test.runner
 
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index bcc68b3..9ffd92d 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -19,6 +19,7 @@
 import android.annotation.SystemApi;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
+import android.app.Notification;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -513,6 +514,12 @@
     }
 
     @Override
+    public ComponentName startServiceInForeground(Intent service,
+            int id, Notification notification) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean stopService(Intent service) {
         throw new UnsupportedOperationException();
     }
@@ -525,6 +532,13 @@
 
     /** @hide */
     @Override
+    public ComponentName startServiceInForegroundAsUser(Intent service,
+            int id, Notification notification, UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
     public boolean stopServiceAsUser(Intent service, UserHandle user) {
         throw new UnsupportedOperationException();
     }
@@ -678,6 +692,13 @@
         return null;
     }
 
+    /** @hide */
+    @Override
+    public Context createContextForSplit(String splitName)
+            throws PackageManager.NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
     /** {@hide} */
     @Override
     public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index c46d4a5..fe4e330 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -16,6 +16,7 @@
 
 package android.test.mock;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PackageInstallObserver;
@@ -43,7 +44,9 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VersionedPackage;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
@@ -72,6 +75,12 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public PackageInfo getPackageInfo(VersionedPackage versionedPackage,
+            int flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
     /** @hide */
     @Override
     public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
@@ -211,6 +220,11 @@
     }
 
     @Override
+    public boolean canRequestPackageInstalls() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean isPermissionRevokedByPolicy(String permName, String pkgName) {
         throw new UnsupportedOperationException();
     }
@@ -715,8 +729,7 @@
      * @hide - to match hiding in superclass
      */
     @Override
-    public void deletePackage(
-            String packageName, IPackageDeleteObserver observer, int flags) {
+    public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
         throw new UnsupportedOperationException();
     }
 
@@ -724,8 +737,8 @@
      * @hide - to match hiding in superclass
      */
     @Override
-    public void deletePackageAsUser(
-            String packageName, IPackageDeleteObserver observer, int flags, int userId) {
+    public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer,
+            int flags, int userId) {
         throw new UnsupportedOperationException();
     }
 
@@ -818,6 +831,17 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public @NonNull List<SharedLibraryInfo> getSharedLibrariesAsUser(int flags, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
     /** @hide */
     @Override
     public @NonNull String getServicesSystemSharedLibraryPackageName() {
diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk
index d1efe7b..68fd662 100644
--- a/test-runner/tests/Android.mk
+++ b/test-runner/tests/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
index e6f6c39..9435893 100644
--- a/tests/AppLaunch/Android.mk
+++ b/tests/AppLaunch/Android.mk
@@ -11,7 +11,7 @@
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/BrowserPowerTest/Android.mk b/tests/BrowserPowerTest/Android.mk
index f2c07b3..59bc729 100644
--- a/tests/BrowserPowerTest/Android.mk
+++ b/tests/BrowserPowerTest/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
index 50926a6..1f14f03 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
@@ -25,13 +25,8 @@
 LOCAL_SRC_FILES += $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
-#LOCAL_STATIC_JAVA_LIBRARIES := filterframework-test-lib
-LOCAL_STATIC_JAVA_LIBRARIES += guava
+LOCAL_STATIC_JAVA_LIBRARIES := guava junit legacy-android-test
 
-#LOCAL_JAVA_LIBRARIES := filterframework-test-lib
-LOCAL_STATIC_JAVA_LIBRARIES := guava
-
-LOCAL_STATIC_JAVA_LIBRARIES +=
 LOCAL_PROGUARD_ENABLED := disabled
 
 LOCAL_INSTRUMENTATION_FOR := SmartCamera
diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk
index 642c9e9..90de503 100644
--- a/tests/CanvasCompare/Android.mk
+++ b/tests/CanvasCompare/Android.mk
@@ -24,5 +24,6 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 include $(BUILD_PACKAGE)
diff --git a/tests/Compatibility/Android.mk b/tests/Compatibility/Android.mk
index c2f89dd..99e84bd 100644
--- a/tests/Compatibility/Android.mk
+++ b/tests/Compatibility/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 # Include all test java files.
 LOCAL_SRC_FILES := \
 	$(call all-java-files-under, src)
diff --git a/tests/CoreTests/android/Android.mk b/tests/CoreTests/android/Android.mk
index 5f3d0d9..c9f1161 100644
--- a/tests/CoreTests/android/Android.mk
+++ b/tests/CoreTests/android/Android.mk
@@ -7,6 +7,7 @@
 	$(call all-subdir-java-files)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt org.apache.http.legacy
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 LOCAL_PACKAGE_NAME := CoreTests
 
diff --git a/tests/DataIdleTest/Android.mk b/tests/DataIdleTest/Android.mk
index acb46c5..4e15729 100644
--- a/tests/DataIdleTest/Android.mk
+++ b/tests/DataIdleTest/Android.mk
@@ -21,6 +21,7 @@
 
 LOCAL_PACKAGE_NAME := DataIdleTest
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 # We need to sign it to get access to the network usage history.
diff --git a/tests/FrameworkPerf/Android.mk b/tests/FrameworkPerf/Android.mk
index 2eb52f0..d2ec753 100644
--- a/tests/FrameworkPerf/Android.mk
+++ b/tests/FrameworkPerf/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_PACKAGE_NAME := FrameworkPerf
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 LOCAL_AAPT_FLAGS = -c 120dpi,240dpi,160dpi,161dpi,320dpi,nodpi
 
diff --git a/tests/HierarchyViewerTest/Android.mk b/tests/HierarchyViewerTest/Android.mk
index 07b90f0..f8c8656 100644
--- a/tests/HierarchyViewerTest/Android.mk
+++ b/tests/HierarchyViewerTest/Android.mk
@@ -8,5 +8,6 @@
 LOCAL_PACKAGE_NAME := HierarchyViewerTest
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 include $(BUILD_PACKAGE)
diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk
index 0f1924c..6042471 100644
--- a/tests/ImfTest/tests/Android.mk
+++ b/tests/ImfTest/tests/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 LOCAL_PACKAGE_NAME := ImfTestTests
 
diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk
index 0ab793b..578e628 100644
--- a/tests/MemoryUsage/Android.mk
+++ b/tests/MemoryUsage/Android.mk
@@ -10,8 +10,9 @@
 
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 include $(BUILD_PACKAGE)
 
 # Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
index a63162d..dd9ff11 100644
--- a/tests/NetworkSecurityConfigTest/Android.mk
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -6,6 +6,7 @@
 LOCAL_CERTIFICATE := platform
 
 LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
index e67134d..359484e 100644
--- a/tests/SoundTriggerTests/Android.mk
+++ b/tests/SoundTriggerTests/Android.mk
@@ -27,7 +27,7 @@
   LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
 endif
 
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target legacy-android-test
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_PACKAGE_NAME := SoundTriggerTests
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index da27ea9..56aad23 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -424,6 +424,17 @@
                     mNM.notify("secret", 7012, n);
                 }
             },
+            new Test("1 minute timeout") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("timeout in a minute")
+                            .setTimeout(System.currentTimeMillis() + (1000 * 60))
+                            .build();
+                    mNM.notify("timeout_min", 7013, n);
+                }
+            },
         new Test("Off") {
             public void run() {
                 PowerManager pm = (PowerManager)NotificationTestList.this.getSystemService(Context.POWER_SERVICE);
diff --git a/tests/TtsTests/Android.mk b/tests/TtsTests/Android.mk
index e049c90..ed63e12 100644
--- a/tests/TtsTests/Android.mk
+++ b/tests/TtsTests/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-LOCAL_STATIC_JAVA_LIBRARIES := littlemock
+LOCAL_STATIC_JAVA_LIBRARIES := littlemock junit legacy-android-test
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_PACKAGE_NAME := TtsTests
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index d62c30d..46b6403 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1103,6 +1103,29 @@
         UNAVAILABLE
     }
 
+    private static class CallbackInfo {
+        public final CallbackState state;
+        public final Network network;
+        public final Object arg;
+        public CallbackInfo(CallbackState s, Network n, Object o) {
+            state = s; network = n; arg = o;
+        }
+        public String toString() {
+            return String.format("%s (%s)", state, network);
+        }
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof CallbackInfo)) return false;
+            // Ignore timeMs, since it's unpredictable.
+            CallbackInfo other = (CallbackInfo) o;
+            return (state == other.state) && Objects.equals(network, other.network);
+        }
+        @Override
+        public int hashCode() {
+            return Objects.hash(state, network);
+        }
+    }
+
     /**
      * Utility NetworkCallback for testing. The caller must explicitly test for all the callbacks
      * this class receives, by calling expectCallback() exactly once each time a callback is
@@ -1114,21 +1137,6 @@
         // the linger timeout.
         private final static int TIMEOUT_MS = 50;
 
-        private class CallbackInfo {
-            public final CallbackState state;
-            public final Network network;
-            public Object arg;
-            public CallbackInfo(CallbackState s, Network n, Object o) {
-                state = s; network = n; arg = o;
-            }
-            public String toString() { return String.format("%s (%s)", state, network); }
-            public boolean equals(Object o) {
-                if (!(o instanceof CallbackInfo)) return false;
-                // Ignore timeMs, since it's unpredictable.
-                CallbackInfo other = (CallbackInfo) o;
-                return state == other.state && Objects.equals(network, other.network);
-            }
-        }
         private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
 
         protected void setLastCallback(CallbackState state, Network network, Object o) {
@@ -1155,17 +1163,24 @@
             setLastCallback(CallbackState.LOST, network, null);
         }
 
+        CallbackInfo nextCallback(int timeoutMs) {
+            CallbackInfo cb = null;
+            try {
+                cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+            }
+            if (cb == null) {
+                // LinkedBlockingQueue.poll() returns null if it timeouts.
+                fail("Did not receive callback after " + timeoutMs + "ms");
+            }
+            return cb;
+        }
+
         void expectCallback(CallbackState state, MockNetworkAgent mockAgent, int timeoutMs) {
             CallbackInfo expected = new CallbackInfo(
                     state, (mockAgent != null) ? mockAgent.getNetwork() : null, 0);
-            CallbackInfo actual;
-            try {
-                actual = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
-                assertEquals("Unexpected callback:", expected, actual);
-            } catch (InterruptedException e) {
-                fail("Did not receive expected " + expected + " after " + TIMEOUT_MS + "ms");
-                actual = null;  // Or the compiler can't tell it's never used uninitialized.
-            }
+            CallbackInfo actual = nextCallback(timeoutMs);
+            assertEquals("Unexpected callback:", expected, actual);
             if (state == CallbackState.LOSING) {
                 String msg = String.format(
                         "Invalid linger time value %d, must be between %d and %d",
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index b2a9a49..1e67769 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -16,10 +16,13 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.reset;
 
 import android.content.Context;
@@ -67,12 +70,13 @@
 
     @Test
     public void testDoesNothingBeforeStarted() {
-        UpstreamNetworkMonitor unm = new UpstreamNetworkMonitor(null, null, EVENT_UNM_UPDATE);
-        assertFalse(unm.mobileNetworkRequested());
-        // Given a null Context, and therefore a null ConnectivityManager,
-        // these would cause an exception, if they actually attempted anything.
-        unm.mobileUpstreamRequiresDun(true);
-        unm.mobileUpstreamRequiresDun(false);
+        assertTrue(mCM.hasNoCallbacks());
+        assertFalse(mUNM.mobileNetworkRequested());
+
+        mUNM.updateMobileRequiresDun(true);
+        assertTrue(mCM.hasNoCallbacks());
+        mUNM.updateMobileRequiresDun(false);
+        assertTrue(mCM.hasNoCallbacks());
     }
 
     @Test
@@ -83,7 +87,7 @@
         assertEquals(1, mCM.trackingDefault.size());
 
         mUNM.stop();
-        assertTrue(mCM.isEmpty());
+        assertTrue(mCM.hasNoCallbacks());
     }
 
     @Test
@@ -95,11 +99,11 @@
         assertTrue(mCM.isListeningForDun());
 
         mUNM.stop();
-        assertTrue(mCM.isEmpty());
+        assertTrue(mCM.hasNoCallbacks());
     }
 
     @Test
-    public void testCanRequestMobileNetwork() throws Exception {
+    public void testRequestsMobileNetwork() throws Exception {
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
@@ -107,22 +111,22 @@
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
-        mUNM.mobileUpstreamRequiresDun(false);
+        mUNM.updateMobileRequiresDun(false);
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
         mUNM.registerMobileNetworkRequest();
         assertTrue(mUNM.mobileNetworkRequested());
-        assertEquals(1, mCM.requested.size());
+        assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
         assertFalse(mCM.isDunRequested());
 
         mUNM.stop();
         assertFalse(mUNM.mobileNetworkRequested());
-        assertTrue(mCM.isEmpty());
+        assertTrue(mCM.hasNoCallbacks());
     }
 
     @Test
-    public void testCanRequestDunNetwork() throws Exception {
+    public void testRequestsDunNetwork() throws Exception {
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
@@ -130,33 +134,67 @@
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
-        mUNM.mobileUpstreamRequiresDun(true);
+        mUNM.updateMobileRequiresDun(true);
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
         mUNM.registerMobileNetworkRequest();
         assertTrue(mUNM.mobileNetworkRequested());
-        assertEquals(1, mCM.requested.size());
+        assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
         assertTrue(mCM.isDunRequested());
 
         mUNM.stop();
         assertFalse(mUNM.mobileNetworkRequested());
-        assertTrue(mCM.isEmpty());
+        assertTrue(mCM.hasNoCallbacks());
+    }
+
+    @Test
+    public void testUpdateMobileRequiredDun() throws Exception {
+        mUNM.start();
+
+        // Test going from no-DUN to DUN correctly re-registers callbacks.
+        mUNM.updateMobileRequiresDun(false);
+        mUNM.registerMobileNetworkRequest();
+        assertTrue(mUNM.mobileNetworkRequested());
+        assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
+        assertFalse(mCM.isDunRequested());
+        mUNM.updateMobileRequiresDun(true);
+        assertTrue(mUNM.mobileNetworkRequested());
+        assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
+        assertTrue(mCM.isDunRequested());
+
+        // Test going from DUN to no-DUN correctly re-registers callbacks.
+        mUNM.updateMobileRequiresDun(false);
+        assertTrue(mUNM.mobileNetworkRequested());
+        assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
+        assertFalse(mCM.isDunRequested());
+
+        mUNM.stop();
+        assertFalse(mUNM.mobileNetworkRequested());
+    }
+
+    private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
+        assertEquals(1, mCM.requested.size());
+        assertEquals(1, mCM.legacyTypeMap.size());
+        assertEquals(Integer.valueOf(upstreamType),
+                mCM.legacyTypeMap.values().iterator().next());
     }
 
     private static class TestConnectivityManager extends ConnectivityManager {
         public Set<NetworkCallback> trackingDefault = new HashSet<>();
         public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
         public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
+        public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
 
         public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
             super(ctx, svc);
         }
 
-        boolean isEmpty() {
+        boolean hasNoCallbacks() {
             return trackingDefault.isEmpty() &&
                    listening.isEmpty() &&
-                   requested.isEmpty();
+                   requested.isEmpty() &&
+                   legacyTypeMap.isEmpty();
         }
 
         boolean isListeningForDun() {
@@ -184,6 +222,17 @@
         }
 
         @Override
+        public void requestNetwork(NetworkRequest req, NetworkCallback cb,
+                int timeoutMs, int legacyType) {
+            assertFalse(requested.containsKey(cb));
+            requested.put(cb, req);
+            assertFalse(legacyTypeMap.containsKey(cb));
+            if (legacyType != ConnectivityManager.TYPE_NONE) {
+                legacyTypeMap.put(cb, legacyType);
+            }
+        }
+
+        @Override
         public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
             assertFalse(listening.containsKey(cb));
             listening.put(cb, req);
@@ -203,6 +252,9 @@
                 listening.remove(cb);
             } else if (requested.containsKey(cb)) {
                 requested.remove(cb);
+                legacyTypeMap.remove(cb);
+            } else {
+                fail("Unexpected callback removed");
             }
 
             assertFalse(trackingDefault.contains(cb));
diff --git a/tests/permission/Android.mk b/tests/permission/Android.mk
index 31a0daf..54688c8 100644
--- a/tests/permission/Android.mk
+++ b/tests/permission/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
 LOCAL_PACKAGE_NAME := FrameworkPermissionTests
 
 include $(BUILD_PACKAGE)
diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk
index 392d398..43d1e37 100644
--- a/tests/utils/testutils/Android.mk
+++ b/tests/utils/testutils/Android.mk
@@ -25,6 +25,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
+    legacy-android-test \
     mockito-target-minus-junit4
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
index 565d2f0..0aca45e 100644
--- a/tools/aapt/AaptConfig.cpp
+++ b/tools/aapt/AaptConfig.cpp
@@ -131,6 +131,22 @@
         part = parts[index].string();
     }
 
+    if (parseWideColorGamut(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseHdr(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
     if (parseOrientation(part, &config)) {
         index++;
         if (index == N) {
@@ -250,7 +266,9 @@
 
     uint16_t minSdk = 0;
     if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
-                == ResTable_config::UI_MODE_TYPE_VR_HEADSET) {
+                == ResTable_config::UI_MODE_TYPE_VR_HEADSET
+            || config->colorMode & ResTable_config::MASK_WIDE_COLOR_GAMUT
+            || config->colorMode & ResTable_config::MASK_HDR) {
         minSdk = SDK_O;
     } else if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
         minSdk = SDK_MNC;
@@ -431,6 +449,46 @@
     return false;
 }
 
+bool parseWideColorGamut(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->colorMode =
+                (out->colorMode&~ResTable_config::MASK_WIDE_COLOR_GAMUT)
+                | ResTable_config::WIDE_COLOR_GAMUT_ANY;
+        return true;
+    } else if (strcmp(name, "widecg") == 0) {
+        if (out) out->colorMode =
+                (out->colorMode&~ResTable_config::MASK_WIDE_COLOR_GAMUT)
+                | ResTable_config::WIDE_COLOR_GAMUT_YES;
+        return true;
+    } else if (strcmp(name, "nowidecg") == 0) {
+        if (out) out->colorMode =
+                (out->colorMode&~ResTable_config::MASK_WIDE_COLOR_GAMUT)
+                | ResTable_config::WIDE_COLOR_GAMUT_NO;
+        return true;
+    }
+    return false;
+}
+
+bool parseHdr(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->colorMode =
+                (out->colorMode&~ResTable_config::MASK_HDR)
+                | ResTable_config::HDR_ANY;
+        return true;
+    } else if (strcmp(name, "highdr") == 0) {
+        if (out) out->colorMode =
+                (out->colorMode&~ResTable_config::MASK_HDR)
+                | ResTable_config::HDR_YES;
+        return true;
+    } else if (strcmp(name, "lowdr") == 0) {
+        if (out) out->colorMode =
+                (out->colorMode&~ResTable_config::MASK_HDR)
+                | ResTable_config::HDR_NO;
+        return true;
+    }
+    return false;
+}
+
 bool parseOrientation(const char* name, ResTable_config* out) {
     if (strcmp(name, kWildcardName) == 0) {
         if (out) out->orientation = out->ORIENTATION_ANY;
diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h
index 04c763f..a6dd252 100644
--- a/tools/aapt/AaptConfig.h
+++ b/tools/aapt/AaptConfig.h
@@ -61,6 +61,8 @@
 bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL);
 bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL);
 bool parseScreenRound(const char* name, android::ResTable_config* out = NULL);
+bool parseWideColorGamut(const char* name, android::ResTable_config* out = NULL);
+bool parseHdr(const char* name, android::ResTable_config* out = NULL);
 bool parseOrientation(const char* str, android::ResTable_config* out = NULL);
 bool parseUiModeType(const char* str, android::ResTable_config* out = NULL);
 bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL);
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 653c1b4..a93ee2e 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -55,6 +55,7 @@
           mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL),
           mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
           mAutoAddOverlay(false), mGenDependencies(false), mNoVersionVectors(false),
+          mNoVersionTransitions(false),
           mCrunchedOutputDir(NULL), mProguardFile(NULL), mMainDexProguardFile(NULL),
           mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
           mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
@@ -219,6 +220,8 @@
     void setBuildAppAsSharedLibrary(bool val) { mBuildAppAsSharedLibrary = val; }
     void setNoVersionVectors(bool val) { mNoVersionVectors = val; }
     bool getNoVersionVectors() const { return mNoVersionVectors; }
+    void setNoVersionTransitions(bool val) { mNoVersionTransitions = val; }
+    bool getNoVersionTransitions() const { return mNoVersionTransitions; }
 
     /*
      * Set and get the file specification.
@@ -299,6 +302,7 @@
     bool        mAutoAddOverlay;
     bool        mGenDependencies;
     bool        mNoVersionVectors;
+    bool        mNoVersionTransitions;
     const char* mCrunchedOutputDir;
     const char* mProguardFile;
     const char* mMainDexProguardFile;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 984d98e..417b7ae 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -223,6 +223,8 @@
         "       localization\n"
         "   --no-version-vectors\n"
         "       Do not automatically generate versioned copies of vector XML resources.\n"
+        "   --no-version-transitions\n"
+        "       Do not automatically generate versioned copies of transition XML resources.\n"
         "   --private-symbols\n"
         "       Java package name to use when generating R.java for private resources.\n",
         gDefaultIgnoreAssets);
@@ -704,6 +706,8 @@
                     bundle.setPseudolocalize(PSEUDO_ACCENTED | PSEUDO_BIDI);
                 } else if (strcmp(cp, "-no-version-vectors") == 0) {
                     bundle.setNoVersionVectors(true);
+                } else if (strcmp(cp, "-no-version-transitions") == 0) {
+                    bundle.setNoVersionTransitions(true);
                 } else if (strcmp(cp, "-private-symbols") == 0) {
                     argc--;
                     argv++;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 75a3160..cf5badc 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -211,7 +211,7 @@
 bool isValidResourceType(const String8& type)
 {
     return type == "anim" || type == "animator" || type == "interpolator"
-        || type == "transition"
+        || type == "transition" || type == "font"
         || type == "drawable" || type == "layout"
         || type == "values" || type == "xml" || type == "raw"
         || type == "color" || type == "menu" || type == "mipmap";
@@ -1223,6 +1223,7 @@
     sp<ResourceTypeSet> colors;
     sp<ResourceTypeSet> menus;
     sp<ResourceTypeSet> mipmaps;
+    sp<ResourceTypeSet> fonts;
 
     ASSIGN_IT(drawable);
     ASSIGN_IT(layout);
@@ -1235,6 +1236,7 @@
     ASSIGN_IT(color);
     ASSIGN_IT(menu);
     ASSIGN_IT(mipmap);
+    ASSIGN_IT(font);
 
     assets->setResources(resources);
     // now go through any resource overlays and collect their files
@@ -1257,6 +1259,7 @@
             !applyFileOverlay(bundle, assets, &raws, "raw") ||
             !applyFileOverlay(bundle, assets, &colors, "color") ||
             !applyFileOverlay(bundle, assets, &menus, "menu") ||
+            !applyFileOverlay(bundle, assets, &fonts, "font") ||
             !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
         return UNKNOWN_ERROR;
     }
@@ -1291,6 +1294,13 @@
         }
     }
 
+    if (fonts != NULL) {
+        err = makeFileResources(bundle, assets, &table, fonts, "font");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
     if (layouts != NULL) {
         err = makeFileResources(bundle, assets, &table, layouts, "layout");
         if (err != NO_ERROR) {
@@ -1549,6 +1559,26 @@
         err = NO_ERROR;
     }
 
+    if (fonts != NULL) {
+        ResourceDirIterator it(fonts, String8("font"));
+        while ((err=it.next()) == NO_ERROR) {
+            // fonts can be resources other than xml.
+            if (it.getFile()->getPath().getPathExtension() == ".xml") {
+                String8 src = it.getFile()->getPrintableSource();
+                err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+                        it.getFile(), &table, xmlFlags);
+                if (err != NO_ERROR) {
+                    hasErrors = true;
+                }
+            }
+        }
+
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+        err = NO_ERROR;
+    }
+
     // Now compile any generated resources.
     std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue();
     while (!workQueue.empty()) {
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 661409e..63498f7 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -4730,6 +4730,32 @@
     return NO_ERROR;
 }
 
+const String16 kTransitionElements[] = {
+    String16("fade"),
+    String16("changeBounds"),
+    String16("slide"),
+    String16("explode"),
+    String16("changeImageTransform"),
+    String16("changeTransform"),
+    String16("changeClipBounds"),
+    String16("autoTransition"),
+    String16("recolor"),
+    String16("changeScroll"),
+    String16("transitionSet"),
+    String16("transition"),
+    String16("transitionManager"),
+};
+
+static bool IsTransitionElement(const String16& name) {
+    for (int i = 0, size = sizeof(kTransitionElements) / sizeof(kTransitionElements[0]);
+         i < size; ++i) {
+        if (name == kTransitionElements[i]) {
+            return true;
+        }
+    }
+    return false;
+}
+
 status_t ResourceTable::modifyForCompat(const Bundle* bundle,
                                         const String16& resourceName,
                                         const sp<AaptFile>& target,
@@ -4766,6 +4792,11 @@
             continue;
         }
 
+        if (bundle->getNoVersionTransitions() && (IsTransitionElement(node->getElementName()))) {
+            // We were told not to version transition tags, so skip the children here.
+            continue;
+        }
+
         const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes();
         for (size_t i = 0; i < attrs.size(); i++) {
             const XMLNode::attribute_entry& attr = attrs[i];
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
index 8bb38ba..4f22fa5 100644
--- a/tools/aapt/tests/AaptConfig_test.cpp
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -98,3 +98,33 @@
     EXPECT_EQ(SDK_MNC, config.sdkVersion);
     EXPECT_EQ(String8("notround-v23"), config.toString());
 }
+
+TEST(AaptConfigTest, WideColorGamutQualifier) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("widecg", &config));
+    EXPECT_EQ(android::ResTable_config::WIDE_COLOR_GAMUT_YES,
+              config.colorMode & android::ResTable_config::MASK_WIDE_COLOR_GAMUT);
+    EXPECT_EQ(SDK_O, config.sdkVersion);
+    EXPECT_EQ(String8("widecg-v26"), config.toString());
+
+    EXPECT_TRUE(TestParse("nowidecg", &config));
+    EXPECT_EQ(android::ResTable_config::WIDE_COLOR_GAMUT_NO,
+              config.colorMode & android::ResTable_config::MASK_WIDE_COLOR_GAMUT);
+    EXPECT_EQ(SDK_O, config.sdkVersion);
+    EXPECT_EQ(String8("nowidecg-v26"), config.toString());
+}
+
+TEST(AaptConfigTest, HdrQualifier) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("highdr", &config));
+    EXPECT_EQ(android::ResTable_config::HDR_YES,
+              config.colorMode & android::ResTable_config::MASK_HDR);
+    EXPECT_EQ(SDK_O, config.sdkVersion);
+    EXPECT_EQ(String8("highdr-v26"), config.toString());
+
+    EXPECT_TRUE(TestParse("lowdr", &config));
+    EXPECT_EQ(android::ResTable_config::HDR_NO,
+              config.colorMode & android::ResTable_config::MASK_HDR);
+    EXPECT_EQ(SDK_O, config.sdkVersion);
+    EXPECT_EQ(String8("lowdr-v26"), config.toString());
+}
\ No newline at end of file
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index b1bd401..46098cb 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -20,15 +20,16 @@
 #include <vector>
 
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
 
 #include "Locale.h"
 #include "SdkConstants.h"
-#include "util/StringPiece.h"
 #include "util/Util.h"
 
-namespace aapt {
-
 using android::ResTable_config;
+using android::StringPiece;
+
+namespace aapt {
 
 static const char* kWildcardName = "any";
 
@@ -205,6 +206,52 @@
   return false;
 }
 
+static bool parseWideColorGamut(const char* name, ResTable_config* out) {
+  if (strcmp(name, kWildcardName) == 0) {
+    if (out)
+      out->colorMode =
+          (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) |
+          ResTable_config::WIDE_COLOR_GAMUT_ANY;
+    return true;
+  } else if (strcmp(name, "widecg") == 0) {
+    if (out)
+      out->colorMode =
+          (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) |
+          ResTable_config::WIDE_COLOR_GAMUT_YES;
+    return true;
+  } else if (strcmp(name, "nowidecg") == 0) {
+    if (out)
+      out->colorMode =
+          (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) |
+          ResTable_config::WIDE_COLOR_GAMUT_NO;
+    return true;
+  }
+  return false;
+}
+
+static bool parseHdr(const char* name, ResTable_config* out) {
+  if (strcmp(name, kWildcardName) == 0) {
+    if (out)
+      out->colorMode =
+          (out->colorMode & ~ResTable_config::MASK_HDR) |
+          ResTable_config::HDR_ANY;
+    return true;
+  } else if (strcmp(name, "highdr") == 0) {
+    if (out)
+      out->colorMode =
+          (out->colorMode & ~ResTable_config::MASK_HDR) |
+          ResTable_config::HDR_YES;
+    return true;
+  } else if (strcmp(name, "lowdr") == 0) {
+    if (out)
+      out->colorMode =
+          (out->colorMode & ~ResTable_config::MASK_HDR) |
+          ResTable_config::HDR_NO;
+    return true;
+  }
+  return false;
+}
+
 static bool parseOrientation(const char* name, ResTable_config* out) {
   if (strcmp(name, kWildcardName) == 0) {
     if (out) out->orientation = out->ORIENTATION_ANY;
@@ -686,6 +733,20 @@
     }
   }
 
+  if (parseWideColorGamut(part_iter->c_str(), &config)) {
+    ++part_iter;
+    if (part_iter == parts_end) {
+      goto success;
+    }
+  }
+
+  if (parseHdr(part_iter->c_str(), &config)) {
+    ++part_iter;
+    if (part_iter == parts_end) {
+      goto success;
+    }
+  }
+
   if (parseOrientation(part_iter->c_str(), &config)) {
     ++part_iter;
     if (part_iter == parts_end) {
@@ -778,7 +839,9 @@
     ConfigDescription* config) {
   uint16_t min_sdk = 0;
   if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
-                == ResTable_config::UI_MODE_TYPE_VR_HEADSET) {
+                == ResTable_config::UI_MODE_TYPE_VR_HEADSET ||
+            config->colorMode & ResTable_config::MASK_WIDE_COLOR_GAMUT ||
+            config->colorMode & ResTable_config::MASK_HDR) {
         min_sdk = SDK_O;
   } else if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
     min_sdk = SDK_MARSHMALLOW;
@@ -849,6 +912,12 @@
   if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
     return !(o.screenLayout2 & MASK_SCREENROUND);
   }
+  if ((colorMode | o.colorMode) & MASK_HDR) {
+    return !(o.colorMode & MASK_HDR);
+  }
+  if ((colorMode | o.colorMode) & MASK_WIDE_COLOR_GAMUT) {
+    return !(o.colorMode & MASK_WIDE_COLOR_GAMUT);
+  }
   if (orientation || o.orientation) return (!o.orientation);
   if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
     return !(o.uiMode & MASK_UI_MODE_TYPE);
@@ -895,6 +964,9 @@
          !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT) ||
          !pred(screenLayout2 & MASK_SCREENROUND,
                o.screenLayout2 & MASK_SCREENROUND) ||
+         !pred(colorMode & MASK_HDR, o.colorMode & MASK_HDR) ||
+         !pred(colorMode & MASK_WIDE_COLOR_GAMUT,
+               o.colorMode & MASK_WIDE_COLOR_GAMUT) ||
          !pred(orientation, o.orientation) ||
          !pred(touchscreen, o.touchscreen) ||
          !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN) ||
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 97d0f38..65c9617 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -20,8 +20,7 @@
 #include <ostream>
 
 #include "androidfw/ResourceTypes.h"
-
-#include "util/StringPiece.h"
+#include "androidfw/StringPiece.h"
 
 namespace aapt {
 
@@ -42,7 +41,7 @@
    * The resulting configuration has the appropriate sdkVersion defined
    * for backwards compatibility.
    */
-  static bool Parse(const StringPiece& str, ConfigDescription* out = nullptr);
+  static bool Parse(const android::StringPiece& str, ConfigDescription* out = nullptr);
 
   /**
    * If the configuration uses an axis that was added after
diff --git a/tools/aapt2/ConfigDescription_test.cpp b/tools/aapt2/ConfigDescription_test.cpp
index 1d22ce0..14a5656 100644
--- a/tools/aapt2/ConfigDescription_test.cpp
+++ b/tools/aapt2/ConfigDescription_test.cpp
@@ -18,9 +18,12 @@
 
 #include <string>
 
+#include "androidfw/StringPiece.h"
+
 #include "SdkConstants.h"
 #include "test/Test.h"
-#include "util/StringPiece.h"
+
+using android::StringPiece;
 
 namespace aapt {
 
@@ -99,6 +102,36 @@
   EXPECT_EQ(std::string("notround-v23"), config.toString().string());
 }
 
+TEST(ConfigDescriptionTest, TestWideColorGamutQualifier) {
+  ConfigDescription config;
+  EXPECT_TRUE(TestParse("widecg", &config));
+  EXPECT_EQ(android::ResTable_config::WIDE_COLOR_GAMUT_YES,
+            config.colorMode & android::ResTable_config::MASK_WIDE_COLOR_GAMUT);
+  EXPECT_EQ(SDK_O, config.sdkVersion);
+  EXPECT_EQ(std::string("widecg-v26"), config.toString().string());
+
+  EXPECT_TRUE(TestParse("nowidecg", &config));
+  EXPECT_EQ(android::ResTable_config::WIDE_COLOR_GAMUT_NO,
+            config.colorMode & android::ResTable_config::MASK_WIDE_COLOR_GAMUT);
+  EXPECT_EQ(SDK_O, config.sdkVersion);
+  EXPECT_EQ(std::string("nowidecg-v26"), config.toString().string());
+}
+
+TEST(ConfigDescriptionTest, TestHdrQualifier) {
+  ConfigDescription config;
+  EXPECT_TRUE(TestParse("highdr", &config));
+  EXPECT_EQ(android::ResTable_config::HDR_YES,
+            config.colorMode & android::ResTable_config::MASK_HDR);
+  EXPECT_EQ(SDK_O, config.sdkVersion);
+  EXPECT_EQ(std::string("highdr-v26"), config.toString().string());
+
+  EXPECT_TRUE(TestParse("lowdr", &config));
+  EXPECT_EQ(android::ResTable_config::HDR_NO,
+            config.colorMode & android::ResTable_config::MASK_HDR);
+  EXPECT_EQ(SDK_O, config.sdkVersion);
+  EXPECT_EQ(std::string("lowdr-v26"), config.toString().string());
+}
+
 TEST(ConfigDescriptionTest, ParseVrAttribute) {
   ConfigDescription config;
   EXPECT_TRUE(TestParse("vrheadset", &config));
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h
index 5bc86a9..50e8b33 100644
--- a/tools/aapt2/Diagnostics.h
+++ b/tools/aapt2/Diagnostics.h
@@ -22,9 +22,9 @@
 #include <string>
 
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 
 #include "Source.h"
-#include "util/StringPiece.h"
 #include "util/Util.h"
 
 namespace aapt {
@@ -38,7 +38,7 @@
  public:
   DiagMessage() = default;
 
-  explicit DiagMessage(const StringPiece& src) : source_(src) {}
+  explicit DiagMessage(const android::StringPiece& src) : source_(src) {}
 
   explicit DiagMessage(const Source& src) : source_(src) {}
 
@@ -59,6 +59,12 @@
   std::stringstream message_;
 };
 
+template <>
+inline DiagMessage& DiagMessage::operator<<(const ::std::u16string& value) {
+  message_ << android::StringPiece16(value);
+  return *this;
+}
+
 struct IDiagnostics {
   virtual ~IDiagnostics() = default;
 
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
index c98cd37..84977ab 100644
--- a/tools/aapt2/Flags.cpp
+++ b/tools/aapt2/Flags.cpp
@@ -21,20 +21,22 @@
 #include <string>
 #include <vector>
 
-#include "util/StringPiece.h"
+#include "androidfw/StringPiece.h"
+
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 Flags& Flags::RequiredFlag(const StringPiece& name,
                            const StringPiece& description, std::string* value) {
   auto func = [value](const StringPiece& arg) -> bool {
-    *value = arg.ToString();
+    *value = arg.to_string();
     return true;
   };
 
-  flags_.push_back(
-      Flag{name.ToString(), description.ToString(), func, true, 1, false});
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
   return *this;
 }
 
@@ -42,12 +44,11 @@
                                const StringPiece& description,
                                std::vector<std::string>* value) {
   auto func = [value](const StringPiece& arg) -> bool {
-    value->push_back(arg.ToString());
+    value->push_back(arg.to_string());
     return true;
   };
 
-  flags_.push_back(
-      Flag{name.ToString(), description.ToString(), func, true, 1, false});
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
   return *this;
 }
 
@@ -55,12 +56,11 @@
                            const StringPiece& description,
                            Maybe<std::string>* value) {
   auto func = [value](const StringPiece& arg) -> bool {
-    *value = arg.ToString();
+    *value = arg.to_string();
     return true;
   };
 
-  flags_.push_back(
-      Flag{name.ToString(), description.ToString(), func, false, 1, false});
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
   return *this;
 }
 
@@ -68,12 +68,11 @@
                                const StringPiece& description,
                                std::vector<std::string>* value) {
   auto func = [value](const StringPiece& arg) -> bool {
-    value->push_back(arg.ToString());
+    value->push_back(arg.to_string());
     return true;
   };
 
-  flags_.push_back(
-      Flag{name.ToString(), description.ToString(), func, false, 1, false});
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
   return *this;
 }
 
@@ -81,12 +80,11 @@
                                const StringPiece& description,
                                std::unordered_set<std::string>* value) {
   auto func = [value](const StringPiece& arg) -> bool {
-    value->insert(arg.ToString());
+    value->insert(arg.to_string());
     return true;
   };
 
-  flags_.push_back(
-      Flag{name.ToString(), description.ToString(), func, false, 1, false});
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
   return *this;
 }
 
@@ -97,8 +95,7 @@
     return true;
   };
 
-  flags_.push_back(
-      Flag{name.ToString(), description.ToString(), func, false, 0, false});
+  flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
   return *this;
 }
 
@@ -141,7 +138,7 @@
   for (size_t i = 0; i < args.size(); i++) {
     StringPiece arg = args[i];
     if (*(arg.data()) != '-') {
-      args_.push_back(arg.ToString());
+      args_.push_back(arg.to_string());
       continue;
     }
 
diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h
index 9feff6b..3b3ae71 100644
--- a/tools/aapt2/Flags.h
+++ b/tools/aapt2/Flags.h
@@ -23,32 +23,30 @@
 #include <unordered_set>
 #include <vector>
 
+#include "androidfw/StringPiece.h"
+
 #include "util/Maybe.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 
 class Flags {
  public:
-  Flags& RequiredFlag(const StringPiece& name, const StringPiece& description,
+  Flags& RequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
                       std::string* value);
-  Flags& RequiredFlagList(const StringPiece& name,
-                          const StringPiece& description,
+  Flags& RequiredFlagList(const android::StringPiece& name, const android::StringPiece& description,
                           std::vector<std::string>* value);
-  Flags& OptionalFlag(const StringPiece& name, const StringPiece& description,
+  Flags& OptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
                       Maybe<std::string>* value);
-  Flags& OptionalFlagList(const StringPiece& name,
-                          const StringPiece& description,
+  Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
                           std::vector<std::string>* value);
-  Flags& OptionalFlagList(const StringPiece& name,
-                          const StringPiece& description,
+  Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
                           std::unordered_set<std::string>* value);
-  Flags& OptionalSwitch(const StringPiece& name, const StringPiece& description,
+  Flags& OptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
                         bool* value);
 
-  void Usage(const StringPiece& command, std::ostream* out);
+  void Usage(const android::StringPiece& command, std::ostream* out);
 
-  bool Parse(const StringPiece& command, const std::vector<StringPiece>& args,
+  bool Parse(const android::StringPiece& command, const std::vector<android::StringPiece>& args,
              std::ostream* outError);
 
   const std::vector<std::string>& GetArgs();
@@ -57,7 +55,7 @@
   struct Flag {
     std::string name;
     std::string description;
-    std::function<bool(const StringPiece& value)> action;
+    std::function<bool(const android::StringPiece& value)> action;
     bool required;
     size_t num_args;
 
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index 78f56c7a..7664fac 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -72,7 +72,7 @@
   return std::all_of(std::begin(str), std::end(str), ::isdigit);
 }
 
-bool LocaleValue::InitFromFilterString(const StringPiece& str) {
+bool LocaleValue::InitFromFilterString(const android::StringPiece& str) {
   // A locale (as specified in the filter) is an underscore separated name such
   // as "en_US", "en_Latn_US", or "en_US_POSIX".
   std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h
index fc6c448..3d73b2e 100644
--- a/tools/aapt2/Locale.h
+++ b/tools/aapt2/Locale.h
@@ -21,8 +21,7 @@
 #include <vector>
 
 #include "androidfw/ResourceTypes.h"
-
-#include "util/StringPiece.h"
+#include "androidfw/StringPiece.h"
 
 namespace aapt {
 
@@ -40,7 +39,7 @@
   /**
    * Initialize this LocaleValue from a config string.
    */
-  bool InitFromFilterString(const StringPiece& config);
+  bool InitFromFilterString(const android::StringPiece& config);
 
   /**
    * Initialize this LocaleValue from parts of a vector.
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index a2b216d..15d7e2e 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -17,7 +17,7 @@
 #include <iostream>
 #include <vector>
 
-#include "util/StringPiece.h"
+#include "androidfw/StringPiece.h"
 
 namespace aapt {
 
@@ -25,7 +25,7 @@
 static const char* sMajorVersion = "2";
 
 // Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "4";
+static const char* sMinorVersion = "5";
 
 int PrintVersion() {
   std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
@@ -33,10 +33,10 @@
   return 0;
 }
 
-extern int Compile(const std::vector<StringPiece>& args);
-extern int Link(const std::vector<StringPiece>& args);
-extern int Dump(const std::vector<StringPiece>& args);
-extern int Diff(const std::vector<StringPiece>& args);
+extern int Compile(const std::vector<android::StringPiece>& args);
+extern int Link(const std::vector<android::StringPiece>& args);
+extern int Dump(const std::vector<android::StringPiece>& args);
+extern int Diff(const std::vector<android::StringPiece>& args);
 
 }  // namespace aapt
 
@@ -45,12 +45,12 @@
     argv += 1;
     argc -= 1;
 
-    std::vector<aapt::StringPiece> args;
+    std::vector<android::StringPiece> args;
     for (int i = 1; i < argc; i++) {
       args.push_back(argv[i]);
     }
 
-    aapt::StringPiece command(argv[0]);
+    android::StringPiece command(argv[0]);
     if (command == "compile" || command == "c") {
       return aapt::Compile(args);
     } else if (command == "link" || command == "l") {
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 3eef7aa7..fdabce1 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -19,6 +19,8 @@
 #include <map>
 #include <string>
 
+using android::StringPiece;
+
 namespace aapt {
 
 StringPiece ToString(ResourceType type) {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 13330b5..1950ea3 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -24,11 +24,11 @@
 #include <tuple>
 #include <vector>
 
+#include "androidfw/StringPiece.h"
 #include "utils/JenkinsHash.h"
 
 #include "ConfigDescription.h"
 #include "Source.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 
@@ -63,13 +63,13 @@
   kXml,
 };
 
-StringPiece ToString(ResourceType type);
+android::StringPiece ToString(ResourceType type);
 
 /**
  * Returns a pointer to a valid ResourceType, or nullptr if
  * the string was invalid.
  */
-const ResourceType* ParseResourceType(const StringPiece& str);
+const ResourceType* ParseResourceType(const android::StringPiece& str);
 
 /**
  * A resource's name. This can uniquely identify
@@ -81,7 +81,7 @@
   std::string entry;
 
   ResourceName() = default;
-  ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e);
+  ResourceName(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
 
   int compare(const ResourceName& other) const;
 
@@ -96,15 +96,15 @@
  * of the original string.
  */
 struct ResourceNameRef {
-  StringPiece package;
+  android::StringPiece package;
   ResourceType type = ResourceType::kRaw;
-  StringPiece entry;
+  android::StringPiece entry;
 
   ResourceNameRef() = default;
   ResourceNameRef(const ResourceNameRef&) = default;
   ResourceNameRef(ResourceNameRef&&) = default;
   ResourceNameRef(const ResourceName& rhs);  // NOLINT(implicit)
-  ResourceNameRef(const StringPiece& p, ResourceType t, const StringPiece& e);
+  ResourceNameRef(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
   ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
   ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
   ResourceNameRef& operator=(const ResourceName& rhs);
@@ -258,9 +258,9 @@
 // ResourceName implementation.
 //
 
-inline ResourceName::ResourceName(const StringPiece& p, ResourceType t,
-                                  const StringPiece& e)
-    : package(p.ToString()), type(t), entry(e.ToString()) {}
+inline ResourceName::ResourceName(const android::StringPiece& p, ResourceType t,
+                                  const android::StringPiece& e)
+    : package(p.to_string()), type(t), entry(e.to_string()) {}
 
 inline int ResourceName::compare(const ResourceName& other) const {
   int cmp = package.compare(other.package);
@@ -311,8 +311,8 @@
 inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs)
     : package(rhs.package), type(rhs.type), entry(rhs.entry) {}
 
-inline ResourceNameRef::ResourceNameRef(const StringPiece& p, ResourceType t,
-                                        const StringPiece& e)
+inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p, ResourceType t,
+                                        const android::StringPiece& e)
     : package(p), type(t), entry(e) {}
 
 inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index b16def4..79379fe 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -29,6 +29,8 @@
 #include "util/Util.h"
 #include "xml/XmlPullParser.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 constexpr const char* sXliffNamespaceUri =
@@ -101,7 +103,7 @@
   StringPiece trimmed_comment = util::TrimWhitespace(res->comment);
   if (trimmed_comment.size() != res->comment.size()) {
     // Only if there was a change do we re-assign.
-    res->comment = trimmed_comment.ToString();
+    res->comment = trimmed_comment.to_string();
   }
 
   if (res->symbol_state) {
@@ -297,7 +299,7 @@
     // Extract the product name if it exists.
     if (Maybe<StringPiece> maybe_product =
             xml::FindNonEmptyAttribute(parser, "product")) {
-      parsed_resource.product = maybe_product.value().ToString();
+      parsed_resource.product = maybe_product.value().to_string();
     }
 
     // Parse the resource regardless of product.
@@ -383,7 +385,7 @@
     // Items have their type encoded in the type attribute.
     if (Maybe<StringPiece> maybe_type =
             xml::FindNonEmptyAttribute(parser, "type")) {
-      resource_type = maybe_type.value().ToString();
+      resource_type = maybe_type.value().to_string();
     } else {
       diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
                    << "<item> must have a 'type' attribute");
@@ -419,7 +421,7 @@
     }
 
     out_resource->name.type = ResourceType::kId;
-    out_resource->name.entry = maybe_name.value().ToString();
+    out_resource->name.entry = maybe_name.value().to_string();
     out_resource->value = util::make_unique<Id>();
     return true;
   }
@@ -436,7 +438,7 @@
     }
 
     out_resource->name.type = item_iter->second.type;
-    out_resource->name.entry = maybe_name.value().ToString();
+    out_resource->name.entry = maybe_name.value().to_string();
 
     // Only use the implicit format for this type if it wasn't overridden.
     if (!resource_format) {
@@ -461,7 +463,7 @@
         return false;
       }
 
-      out_resource->name.entry = maybe_name.value().ToString();
+      out_resource->name.entry = maybe_name.value().to_string();
     }
 
     // Call the associated parse method. The type will be filled in by the
@@ -484,7 +486,7 @@
     }
 
     out_resource->name.type = *parsed_type;
-    out_resource->name.entry = maybe_name.value().ToString();
+    out_resource->name.entry = maybe_name.value().to_string();
     out_resource->value =
         ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
     if (!out_resource->value) {
@@ -718,7 +720,7 @@
   const size_t depth = parser->depth();
   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
     if (parser->event() == xml::XmlPullParser::Event::kComment) {
-      comment = util::TrimWhitespace(parser->comment()).ToString();
+      comment = util::TrimWhitespace(parser->comment()).to_string();
       continue;
     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
       // Skip text.
@@ -754,7 +756,7 @@
 
       ParsedResource child_resource;
       child_resource.name.type = *parsed_type;
-      child_resource.name.entry = maybe_name.value().ToString();
+      child_resource.name.entry = maybe_name.value().to_string();
       child_resource.id = next_id;
       child_resource.comment = std::move(comment);
       child_resource.source = item_source;
@@ -899,7 +901,7 @@
   const size_t depth = parser->depth();
   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
     if (parser->event() == xml::XmlPullParser::Event::kComment) {
-      comment = util::TrimWhitespace(parser->comment()).ToString();
+      comment = util::TrimWhitespace(parser->comment()).to_string();
       continue;
     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
       // Skip text.
@@ -1288,7 +1290,7 @@
   const size_t depth = parser->depth();
   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
     if (parser->event() == xml::XmlPullParser::Event::kComment) {
-      comment = util::TrimWhitespace(parser->comment()).ToString();
+      comment = util::TrimWhitespace(parser->comment()).to_string();
       continue;
     } else if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
       // Ignore text.
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 11b1e5b..c12dacf 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -20,6 +20,7 @@
 #include <memory>
 
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 
 #include "ConfigDescription.h"
 #include "Diagnostics.h"
@@ -27,7 +28,6 @@
 #include "ResourceValues.h"
 #include "StringPool.h"
 #include "util/Maybe.h"
-#include "util/StringPiece.h"
 #include "xml/XmlPullParser.h"
 
 namespace aapt {
@@ -101,7 +101,7 @@
   bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
                      bool weak);
   Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
-                                               const StringPiece& tag);
+                                               const android::StringPiece& tag);
   bool ParseStyle(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
   bool ParseDeclareStyleable(xml::XmlPullParser* parser,
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 2463911..5762fb0 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -25,6 +25,8 @@
 #include "test/Test.h"
 #include "xml/XmlPullParser.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 constexpr const char* kXmlPreamble =
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 4e6a50a..dd78750 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -28,6 +28,8 @@
 #include <string>
 #include <tuple>
 
+using android::StringPiece;
+
 namespace aapt {
 
 static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs,
@@ -87,7 +89,7 @@
 
   std::unique_ptr<ResourceTablePackage> new_package =
       util::make_unique<ResourceTablePackage>();
-  new_package->name = name.ToString();
+  new_package->name = name.to_string();
   return packages.emplace(iter, std::move(new_package))->get();
 }
 
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index a0c3217..0fe966c 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -25,7 +25,9 @@
 #include "StringPool.h"
 #include "io/File.h"
 
-#include <android-base/macros.h>
+#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+
 #include <functional>
 #include <map>
 #include <memory>
@@ -68,9 +70,8 @@
    */
   std::unique_ptr<Value> value;
 
-  ResourceConfigValue(const ConfigDescription& config,
-                      const StringPiece& product)
-      : config(config), product(product.ToString()) {}
+  ResourceConfigValue(const ConfigDescription& config, const android::StringPiece& product)
+      : config(config), product(product.to_string()) {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
@@ -105,13 +106,13 @@
    */
   std::vector<std::unique_ptr<ResourceConfigValue>> values;
 
-  explicit ResourceEntry(const StringPiece& name) : name(name.ToString()) {}
+  explicit ResourceEntry(const android::StringPiece& name) : name(name.to_string()) {}
 
   ResourceConfigValue* FindValue(const ConfigDescription& config);
   ResourceConfigValue* FindValue(const ConfigDescription& config,
-                                 const StringPiece& product);
+                                 const android::StringPiece& product);
   ResourceConfigValue* FindOrCreateValue(const ConfigDescription& config,
-                                         const StringPiece& product);
+                                         const android::StringPiece& product);
   std::vector<ResourceConfigValue*> findAllValues(
       const ConfigDescription& config);
   std::vector<ResourceConfigValue*> FindValuesIf(
@@ -150,8 +151,8 @@
 
   explicit ResourceTableType(const ResourceType type) : type(type) {}
 
-  ResourceEntry* FindEntry(const StringPiece& name);
-  ResourceEntry* FindOrCreateEntry(const StringPiece& name);
+  ResourceEntry* FindEntry(const android::StringPiece& name);
+  ResourceEntry* FindOrCreateEntry(const android::StringPiece& name);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
@@ -195,22 +196,19 @@
                                                Value* incoming);
 
   bool AddResource(const ResourceNameRef& name, const ConfigDescription& config,
-                   const StringPiece& product, std::unique_ptr<Value> value,
+                   const android::StringPiece& product, std::unique_ptr<Value> value,
                    IDiagnostics* diag);
 
   bool AddResource(const ResourceNameRef& name, const ResourceId& res_id,
-                   const ConfigDescription& config, const StringPiece& product,
+                   const ConfigDescription& config, const android::StringPiece& product,
                    std::unique_ptr<Value> value, IDiagnostics* diag);
 
-  bool AddFileReference(const ResourceNameRef& name,
-                        const ConfigDescription& config, const Source& source,
-                        const StringPiece& path, IDiagnostics* diag);
+  bool AddFileReference(const ResourceNameRef& name, const ConfigDescription& config,
+                        const Source& source, const android::StringPiece& path, IDiagnostics* diag);
 
-  bool AddFileReferenceAllowMangled(const ResourceNameRef& name,
-                                    const ConfigDescription& config,
-                                    const Source& source,
-                                    const StringPiece& path, io::IFile* file,
-                                    IDiagnostics* diag);
+  bool AddFileReferenceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config,
+                                    const Source& source, const android::StringPiece& path,
+                                    io::IFile* file, IDiagnostics* diag);
 
   /**
    * Same as AddResource, but doesn't verify the validity of the name. This is
@@ -219,18 +217,13 @@
    * mangled
    * names.
    */
-  bool AddResourceAllowMangled(const ResourceNameRef& name,
-                               const ConfigDescription& config,
-                               const StringPiece& product,
-                               std::unique_ptr<Value> value,
+  bool AddResourceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config,
+                               const android::StringPiece& product, std::unique_ptr<Value> value,
                                IDiagnostics* diag);
 
-  bool AddResourceAllowMangled(const ResourceNameRef& name,
-                               const ResourceId& id,
-                               const ConfigDescription& config,
-                               const StringPiece& product,
-                               std::unique_ptr<Value> value,
-                               IDiagnostics* diag);
+  bool AddResourceAllowMangled(const ResourceNameRef& name, const ResourceId& id,
+                               const ConfigDescription& config, const android::StringPiece& product,
+                               std::unique_ptr<Value> value, IDiagnostics* diag);
 
   bool SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
                       const Symbol& symbol, IDiagnostics* diag);
@@ -273,28 +266,23 @@
    * represent the
    * 'current' package before it is known to the ResourceTable.
    */
-  ResourceTablePackage* FindPackage(const StringPiece& name);
+  ResourceTablePackage* FindPackage(const android::StringPiece& name);
 
   ResourceTablePackage* FindPackageById(uint8_t id);
 
-  ResourceTablePackage* CreatePackage(const StringPiece& name,
-                                      Maybe<uint8_t> id = {});
+  ResourceTablePackage* CreatePackage(const android::StringPiece& name, Maybe<uint8_t> id = {});
 
  private:
-  ResourceTablePackage* FindOrCreatePackage(const StringPiece& name);
+  ResourceTablePackage* FindOrCreatePackage(const android::StringPiece& name);
 
   bool AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
-                       const ConfigDescription& config,
-                       const StringPiece& product, std::unique_ptr<Value> value,
-                       const char* valid_chars,
-                       const CollisionResolverFunc& conflict_resolver,
-                       IDiagnostics* diag);
+                       const ConfigDescription& config, const android::StringPiece& product,
+                       std::unique_ptr<Value> value, const char* valid_chars,
+                       const CollisionResolverFunc& conflict_resolver, IDiagnostics* diag);
 
-  bool AddFileReferenceImpl(const ResourceNameRef& name,
-                            const ConfigDescription& config,
-                            const Source& source, const StringPiece& path,
-                            io::IFile* file, const char* valid_chars,
-                            IDiagnostics* diag);
+  bool AddFileReferenceImpl(const ResourceNameRef& name, const ConfigDescription& config,
+                            const Source& source, const android::StringPiece& path, io::IFile* file,
+                            const char* valid_chars, IDiagnostics* diag);
 
   bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
                           const Symbol& symbol, const char* valid_chars,
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index fce9b33..1123967 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -26,6 +26,9 @@
 #include "util/Files.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+using android::StringPiece16;
+
 namespace aapt {
 namespace ResourceUtils {
 
@@ -59,7 +62,7 @@
     name_out.entry =
         util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
   } else if (name_in.name8) {
-    name_out.entry = StringPiece(name_in.name8, name_in.nameLen).ToString();
+    name_out.entry.assign(name_in.name8, name_in.nameLen);
   } else {
     return {};
   }
@@ -303,9 +306,7 @@
     p++;
   }
 
-  ref.name =
-      ResourceName(package.ToString(), ResourceType::kAttr,
-                   name.empty() ? trimmed_str.ToString() : name.ToString());
+  ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
   return Maybe<Reference>(std::move(ref));
 }
 
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 9766f6a..bd3a8e3 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -20,10 +20,11 @@
 #include <functional>
 #include <memory>
 
+#include "androidfw/StringPiece.h"
+
 #include "NameMangler.h"
 #include "Resource.h"
 #include "ResourceValues.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 namespace ResourceUtils {
@@ -37,8 +38,8 @@
  * individual extracted piece to verify that the pieces are valid.
  * Returns false if there was no package but a ':' was present.
  */
-bool ExtractResourceName(const StringPiece& str, StringPiece* out_package,
-                         StringPiece* out_type, StringPiece* out_entry);
+bool ExtractResourceName(const android::StringPiece& str, android::StringPiece* out_package,
+                         android::StringPiece* out_type, android::StringPiece* out_entry);
 
 /**
  * Returns true if the string was parsed as a resource name
@@ -46,7 +47,7 @@
  * `out_resource` set to the parsed resource name and `out_private` set to true
  * if a '*' prefix was present.
  */
-bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_resource,
+bool ParseResourceName(const android::StringPiece& str, ResourceNameRef* out_resource,
                        bool* out_private = nullptr);
 
 /*
@@ -57,28 +58,27 @@
  * If '+' was present in the reference, `out_create` is set to true.
  * If '*' was present in the reference, `out_private` is set to true.
  */
-bool ParseReference(const StringPiece& str, ResourceNameRef* out_reference,
+bool ParseReference(const android::StringPiece& str, ResourceNameRef* out_reference,
                     bool* out_create = nullptr, bool* out_private = nullptr);
 
 /*
  * Returns true if the string is in the form of a resource reference
  * (@[+][package:]type/name).
  */
-bool IsReference(const StringPiece& str);
+bool IsReference(const android::StringPiece& str);
 
 /*
  * Returns true if the string was parsed as an attribute reference
  * (?[package:][type/]name),
  * with `out_reference` set to the parsed reference.
  */
-bool ParseAttributeReference(const StringPiece& str,
-                             ResourceNameRef* out_reference);
+bool ParseAttributeReference(const android::StringPiece& str, ResourceNameRef* out_reference);
 
 /**
  * Returns true if the string is in the form of an attribute
  * reference(?[package:][type/]name).
  */
-bool IsAttributeReference(const StringPiece& str);
+bool IsAttributeReference(const android::StringPiece& str);
 
 /**
  * Convert an android::ResTable::resource_name to an aapt::ResourceName struct.
@@ -90,22 +90,22 @@
  * Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
  * false, or False.
  */
-Maybe<bool> ParseBool(const StringPiece& str);
+Maybe<bool> ParseBool(const android::StringPiece& str);
 
 /**
  * Returns a uint32_t if the string is an integer.
  */
-Maybe<uint32_t> ParseInt(const StringPiece& str);
+Maybe<uint32_t> ParseInt(const android::StringPiece& str);
 
 /**
  * Returns an ID if it the string represented a valid ID.
  */
-Maybe<ResourceId> ParseResourceId(const StringPiece& str);
+Maybe<ResourceId> ParseResourceId(const android::StringPiece& str);
 
 /**
  * Parses an SDK version, which can be an integer, or a letter from A-Z.
  */
-Maybe<int> ParseSdkVersion(const StringPiece& str);
+Maybe<int> ParseSdkVersion(const android::StringPiece& str);
 
 /*
  * Returns a Reference, or None Maybe instance if the string `str` was parsed as
@@ -118,8 +118,7 @@
  * ?[package:]style/<entry> or
  * <package>:[style/]<entry>
  */
-Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
-                                           std::string* out_error);
+Maybe<Reference> ParseStyleParentReference(const android::StringPiece& str, std::string* out_error);
 
 /*
  * Returns a Reference if the string `str` was parsed as a valid XML attribute
@@ -128,7 +127,7 @@
  *
  * package:entry
  */
-Maybe<Reference> ParseXmlAttributeName(const StringPiece& str);
+Maybe<Reference> ParseXmlAttributeName(const android::StringPiece& str);
 
 /*
  * Returns a Reference object if the string was parsed as a resource or
@@ -137,52 +136,52 @@
  * if
  * the '+' was present in the string.
  */
-std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
+std::unique_ptr<Reference> TryParseReference(const android::StringPiece& str,
                                              bool* out_create = nullptr);
 
 /*
  * Returns a BinaryPrimitve object representing @null or @empty if the string
  * was parsed as one.
  */
-std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const android::StringPiece& str);
 
 /*
  * Returns a BinaryPrimitve object representing a color if the string was parsed
  * as one.
  */
-std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseColor(const android::StringPiece& str);
 
 /*
  * Returns a BinaryPrimitve object representing a boolean if the string was
  * parsed as one.
  */
-std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseBool(const android::StringPiece& str);
 
 /*
  * Returns a BinaryPrimitve object representing an integer if the string was
  * parsed as one.
  */
-std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseInt(const android::StringPiece& str);
 
 /*
  * Returns a BinaryPrimitve object representing a floating point number
  * (float, dimension, etc) if the string was parsed as one.
  */
-std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str);
+std::unique_ptr<BinaryPrimitive> TryParseFloat(const android::StringPiece& str);
 
 /*
  * Returns a BinaryPrimitve object representing an enum symbol if the string was
  * parsed as one.
  */
 std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
-                                                    const StringPiece& str);
+                                                    const android::StringPiece& str);
 
 /*
  * Returns a BinaryPrimitve object representing a flag symbol if the string was
  * parsed as one.
  */
 std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* enum_attr,
-                                                    const StringPiece& str);
+                                                    const android::StringPiece& str);
 /*
  * Try to convert a string to an Item for the given attribute. The attribute
  * will
@@ -191,11 +190,11 @@
  * reference to an ID that must be created (@+id/foo).
  */
 std::unique_ptr<Item> TryParseItemForAttribute(
-    const StringPiece& value, const Attribute* attr,
+    const android::StringPiece& value, const Attribute* attr,
     const std::function<void(const ResourceName&)>& on_create_reference = {});
 
 std::unique_ptr<Item> TryParseItemForAttribute(
-    const StringPiece& value, uint32_t type_mask,
+    const android::StringPiece& value, uint32_t type_mask,
     const std::function<void(const ResourceName&)>& on_create_reference = {});
 
 uint32_t AndroidTypeToAttributeTypeMask(uint16_t type);
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index f9c500b..048c692 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -48,8 +48,7 @@
   EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
   EXPECT_TRUE(actual_priv);
 
-  EXPECT_FALSE(
-      ResourceUtils::ParseResourceName(StringPiece(), &actual, &actual_priv));
+  EXPECT_FALSE(ResourceUtils::ParseResourceName(android::StringPiece(), &actual, &actual_priv));
 }
 
 TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index ea73615..d380f8d 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
 
 #include "Diagnostics.h"
 #include "Resource.h"
@@ -73,7 +74,7 @@
    */
   const std::string& GetComment() const { return comment_; }
 
-  void SetComment(const StringPiece& str) { comment_ = str.ToString(); }
+  void SetComment(const android::StringPiece& str) { comment_ = str.to_string(); }
 
   void SetComment(std::string&& str) { comment_ = std::move(str); }
 
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index c7f920a..e806714 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -21,6 +21,8 @@
 #include <unordered_map>
 #include <vector>
 
+using android::StringPiece;
+
 namespace aapt {
 
 static const char* sDevelopmentSdkCodeName = "O";
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 5352b53..98ba94b 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -19,6 +19,8 @@
 
 #include <utility>
 
+#include "androidfw/StringPiece.h"
+
 #include "Resource.h"
 
 namespace aapt {
@@ -52,7 +54,7 @@
 
 size_t FindAttributeSdkLevel(const ResourceId& id);
 size_t FindAttributeSdkLevel(const ResourceName& name);
-std::pair<StringPiece, int> GetDevelopmentSdkCodeNameAndVersion();
+std::pair<android::StringPiece, int> GetDevelopmentSdkCodeNameAndVersion();
 
 }  // namespace aapt
 
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 459a8e6..d7f2a66 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -20,8 +20,9 @@
 #include <ostream>
 #include <string>
 
+#include "androidfw/StringPiece.h"
+
 #include "util/Maybe.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 
@@ -35,12 +36,11 @@
 
   Source() = default;
 
-  inline Source(const StringPiece& path)
-      : path(path.ToString()) {  // NOLINT(implicit)
+  inline Source(const android::StringPiece& path) : path(path.to_string()) {  // NOLINT(implicit)
   }
 
-  inline Source(const StringPiece& path, size_t line)
-      : path(path.ToString()), line(line) {}
+  inline Source(const android::StringPiece& path, size_t line)
+      : path(path.to_string()), line(line) {}
 
   inline Source WithLine(size_t line) const { return Source(path, line); }
 };
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index 3032829..d968d73 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -22,11 +22,13 @@
 
 #include "android-base/logging.h"
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
 
 #include "util/BigBuffer.h"
-#include "util/StringPiece.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 StringPool::Ref::Ref() : entry_(nullptr) {}
@@ -140,7 +142,7 @@
   }
 
   Entry* entry = new Entry();
-  entry->value = str.ToString();
+  entry->value = str.to_string();
   entry->context = context;
   entry->index = strings_.size();
   entry->ref_ = 0;
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index a4f556c..d0ce489 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -23,9 +23,10 @@
 #include <unordered_map>
 #include <vector>
 
+#include "androidfw/StringPiece.h"
+
 #include "ConfigDescription.h"
 #include "util/BigBuffer.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 
@@ -149,14 +150,14 @@
    * Adds a string to the pool, unless it already exists. Returns
    * a reference to the string in the pool.
    */
-  Ref MakeRef(const StringPiece& str);
+  Ref MakeRef(const android::StringPiece& str);
 
   /**
    * Adds a string to the pool, unless it already exists, with a context
    * object that can be used when sorting the string pool. Returns
    * a reference to the string in the pool.
    */
-  Ref MakeRef(const StringPiece& str, const Context& context);
+  Ref MakeRef(const android::StringPiece& str, const Context& context);
 
   /**
    * Adds a style to the string pool and returns a reference to it.
@@ -208,11 +209,11 @@
 
   static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8);
 
-  Ref MakeRefImpl(const StringPiece& str, const Context& context, bool unique);
+  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
 
   std::vector<std::unique_ptr<Entry>> strings_;
   std::vector<std::unique_ptr<StyleEntry>> styles_;
-  std::unordered_multimap<StringPiece, Entry*> indexed_strings_;
+  std::unordered_multimap<android::StringPiece, Entry*> indexed_strings_;
 };
 
 //
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index e1394fc..f64a8cf 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -18,9 +18,14 @@
 
 #include <string>
 
+#include "androidfw/StringPiece.h"
+
 #include "test/Test.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+using android::StringPiece16;
+
 namespace aapt {
 
 TEST(StringPoolTest, InsertOneString) {
@@ -236,8 +241,7 @@
     EXPECT_EQ(StringPiece16(u"goodbye"), util::GetString16(test, 1));
 
     EXPECT_EQ(StringPiece(sLongString), util::GetString(test, 2));
-    EXPECT_EQ(util::Utf8ToUtf16(sLongString),
-              util::GetString16(test, 2).ToString());
+    EXPECT_EQ(util::Utf8ToUtf16(sLongString), util::GetString16(test, 2).to_string());
 
     size_t len;
     EXPECT_TRUE(test.stringAt(3, &len) != nullptr ||
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index f0b18e6..8027f42 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -19,6 +19,12 @@
 #include <fstream>
 #include <string>
 
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "androidfw/StringPiece.h"
+#include "google/protobuf/io/coded_stream.h"
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+
 #include "ConfigDescription.h"
 #include "Diagnostics.h"
 #include "Flags.h"
@@ -38,11 +44,7 @@
 #include "xml/XmlDom.h"
 #include "xml/XmlPullParser.h"
 
-#include "android-base/errors.h"
-#include "android-base/file.h"
-#include "google/protobuf/io/coded_stream.h"
-#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
-
+using android::StringPiece;
 using google::protobuf::io::CopyingOutputStreamAdaptor;
 using google::protobuf::io::ZeroCopyOutputStream;
 
@@ -103,9 +105,8 @@
     name = name.substr(0, dot_pos);
   }
 
-  return ResourcePathData{Source(path),          dir_str.ToString(),
-                          name.ToString(),       extension.ToString(),
-                          config_str.ToString(), config};
+  return ResourcePathData{Source(path),          dir_str.to_string(),    name.to_string(),
+                          extension.to_string(), config_str.to_string(), config};
 }
 
 struct CompileOptions {
diff --git a/tools/aapt2/compile/IdAssigner.h b/tools/aapt2/compile/IdAssigner.h
index 371ec01..9640eb8 100644
--- a/tools/aapt2/compile/IdAssigner.h
+++ b/tools/aapt2/compile/IdAssigner.h
@@ -19,11 +19,11 @@
 
 #include <unordered_map>
 
+#include "android-base/macros.h"
+
 #include "Resource.h"
 #include "process/IResourceTableConsumer.h"
 
-#include "android-base/macros.h"
-
 namespace aapt {
 
 /**
@@ -40,8 +40,7 @@
   bool Consume(IAaptContext* context, ResourceTable* table) override;
 
  private:
-  const std::unordered_map<ResourceName, ResourceId>* assigned_id_map_ =
-      nullptr;
+  const std::unordered_map<ResourceName, ResourceId>* assigned_id_map_ = nullptr;
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch.cpp b/tools/aapt2/compile/NinePatch.cpp
index eab5c97..c931da4 100644
--- a/tools/aapt2/compile/NinePatch.cpp
+++ b/tools/aapt2/compile/NinePatch.cpp
@@ -21,10 +21,12 @@
 #include <vector>
 
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
 
-#include "util/StringPiece.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 // Colors in the format 0xAARRGGBB (the way 9-patch expects it).
diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp
index 7ab05b5..5e15c88 100644
--- a/tools/aapt2/compile/Png.cpp
+++ b/tools/aapt2/compile/Png.cpp
@@ -15,18 +15,21 @@
  */
 
 #include "Png.h"
-#include "Source.h"
-#include "util/BigBuffer.h"
-#include "util/Util.h"
 
-#include <androidfw/ResourceTypes.h>
 #include <png.h>
 #include <zlib.h>
+
 #include <iostream>
 #include <sstream>
 #include <string>
 #include <vector>
 
+#include "androidfw/ResourceTypes.h"
+
+#include "Source.h"
+#include "util/BigBuffer.h"
+#include "util/Util.h"
+
 namespace aapt {
 
 constexpr bool kDebug = false;
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
index aff1da3..a820051 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -56,7 +56,7 @@
  */
 class PngChunkFilter : public io::InputStream {
  public:
-  explicit PngChunkFilter(const StringPiece& data);
+  explicit PngChunkFilter(const android::StringPiece& data);
 
   bool Next(const void** buffer, int* len) override;
   void BackUp(int count) override;
@@ -71,7 +71,7 @@
  private:
   bool ConsumeWindow(const void** buffer, int* len);
 
-  StringPiece data_;
+  android::StringPiece data_;
   size_t window_start_ = 0;
   size_t window_end_ = 0;
   bool error_ = false;
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
index 4cbefb9..edec123 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -16,8 +16,11 @@
 
 #include "compile/Png.h"
 
+#include "androidfw/StringPiece.h"
+
 #include "io/Io.h"
-#include "util/StringPiece.h"
+
+using android::StringPiece;
 
 namespace aapt {
 
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 055a725..5035f81 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -23,6 +23,8 @@
 #include "ValueVisitor.h"
 #include "compile/Pseudolocalizer.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 std::unique_ptr<StyledString> PseudolocalizeStyledString(
diff --git a/tools/aapt2/compile/Pseudolocalizer.cpp b/tools/aapt2/compile/Pseudolocalizer.cpp
index f89288f..15a3d8c 100644
--- a/tools/aapt2/compile/Pseudolocalizer.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer.cpp
@@ -18,6 +18,8 @@
 
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 // String basis to generate expansion
@@ -40,10 +42,8 @@
 
 class PseudoMethodNone : public PseudoMethodImpl {
  public:
-  std::string Text(const StringPiece& text) override { return text.ToString(); }
-  std::string Placeholder(const StringPiece& text) override {
-    return text.ToString();
-  }
+  std::string Text(const StringPiece& text) override { return text.to_string(); }
+  std::string Placeholder(const StringPiece& text) override { return text.to_string(); }
 };
 
 class PseudoMethodBidi : public PseudoMethodImpl {
@@ -116,7 +116,7 @@
       }
       size_t size = nextpos - lastpos;
       if (size) {
-        std::string chunk = text.substr(lastpos, size).ToString();
+        std::string chunk = text.substr(lastpos, size).to_string();
         if (pseudo) {
           chunk = impl_->Text(chunk);
         } else if (str[lastpos] == kArgStart && str[nextpos - 1] == kArgEnd) {
@@ -437,7 +437,7 @@
 
 std::string PseudoMethodAccent::Placeholder(const StringPiece& source) {
   // Surround a placeholder with brackets
-  return kPlaceholderOpen + source.ToString() + kPlaceholderClose;
+  return kPlaceholderOpen + source.to_string() + kPlaceholderClose;
 }
 
 std::string PseudoMethodBidi::Text(const StringPiece& source) {
@@ -467,7 +467,7 @@
 
 std::string PseudoMethodBidi::Placeholder(const StringPiece& source) {
   // Surround a placeholder with directionality change sequence
-  return kRlm + kRlo + source.ToString() + kPdf + kRlm;
+  return kRlm + kRlo + source.to_string() + kPdf + kRlm;
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/compile/Pseudolocalizer.h b/tools/aapt2/compile/Pseudolocalizer.h
index a6d2ad0..6cf003b 100644
--- a/tools/aapt2/compile/Pseudolocalizer.h
+++ b/tools/aapt2/compile/Pseudolocalizer.h
@@ -20,10 +20,10 @@
 #include <memory>
 
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 
 #include "ResourceValues.h"
 #include "StringPool.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 
@@ -32,8 +32,8 @@
   virtual ~PseudoMethodImpl() {}
   virtual std::string Start() { return {}; }
   virtual std::string End() { return {}; }
-  virtual std::string Text(const StringPiece& text) = 0;
-  virtual std::string Placeholder(const StringPiece& text) = 0;
+  virtual std::string Text(const android::StringPiece& text) = 0;
+  virtual std::string Placeholder(const android::StringPiece& text) = 0;
 };
 
 class Pseudolocalizer {
@@ -48,7 +48,7 @@
   void SetMethod(Method method);
   std::string Start() { return impl_->Start(); }
   std::string End() { return impl_->End(); }
-  std::string Text(const StringPiece& text);
+  std::string Text(const android::StringPiece& text);
 
  private:
   std::unique_ptr<PseudoMethodImpl> impl_;
diff --git a/tools/aapt2/compile/Pseudolocalizer_test.cpp b/tools/aapt2/compile/Pseudolocalizer_test.cpp
index 92eb3b5..d3b7b02 100644
--- a/tools/aapt2/compile/Pseudolocalizer_test.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer_test.cpp
@@ -19,6 +19,8 @@
 #include "test/Test.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 // In this context, 'Axis' represents a particular field in the configuration,
diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp
index 593e7ab..de0fe40 100644
--- a/tools/aapt2/diff/Diff.cpp
+++ b/tools/aapt2/diff/Diff.cpp
@@ -24,6 +24,8 @@
 #include "process/SymbolTable.h"
 #include "unflatten/BinaryResourceParser.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 class DiffContext : public IAaptContext {
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
index 2920c2a..bcede72 100644
--- a/tools/aapt2/dump/Dump.cpp
+++ b/tools/aapt2/dump/Dump.cpp
@@ -16,6 +16,8 @@
 
 #include <vector>
 
+#include "androidfw/StringPiece.h"
+
 #include "Debug.h"
 #include "Diagnostics.h"
 #include "Flags.h"
@@ -24,7 +26,8 @@
 #include "proto/ProtoSerialize.h"
 #include "unflatten/BinaryResourceParser.h"
 #include "util/Files.h"
-#include "util/StringPiece.h"
+
+using android::StringPiece;
 
 namespace aapt {
 
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/flatten/Archive.cpp
index 47de0a3..5c96a4d 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/flatten/Archive.cpp
@@ -22,10 +22,12 @@
 #include <vector>
 
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 #include "ziparchive/zip_writer.h"
 
 #include "util/Files.h"
-#include "util/StringPiece.h"
+
+using android::StringPiece;
 
 namespace aapt {
 
@@ -36,7 +38,7 @@
   DirectoryWriter() = default;
 
   bool Open(IDiagnostics* diag, const StringPiece& out_dir) {
-    dir_ = out_dir.ToString();
+    dir_ = out_dir.to_string();
     file::FileType type = file::GetFileType(dir_);
     if (type == file::FileType::kNonexistant) {
       diag->Error(DiagMessage() << "directory " << dir_ << " does not exist");
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/flatten/Archive.h
index 4fcb3ff..f0681bd 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/flatten/Archive.h
@@ -22,12 +22,12 @@
 #include <string>
 #include <vector>
 
+#include "androidfw/StringPiece.h"
 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
 
 #include "Diagnostics.h"
 #include "util/BigBuffer.h"
 #include "util/Files.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 
@@ -46,7 +46,7 @@
  public:
   virtual ~IArchiveWriter() = default;
 
-  virtual bool StartEntry(const StringPiece& path, uint32_t flags) = 0;
+  virtual bool StartEntry(const android::StringPiece& path, uint32_t flags) = 0;
   virtual bool WriteEntry(const BigBuffer& buffer) = 0;
   virtual bool WriteEntry(const void* data, size_t len) = 0;
   virtual bool FinishEntry() = 0;
@@ -57,11 +57,11 @@
   }
 };
 
-std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(
-    IDiagnostics* diag, const StringPiece& path);
+std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(IDiagnostics* diag,
+                                                             const android::StringPiece& path);
 
-std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(
-    IDiagnostics* diag, const StringPiece& path);
+std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
+                                                           const android::StringPiece& path);
 
 }  // namespace aapt
 
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index 2c83bb3..ffc2de1 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -23,6 +23,8 @@
 #include "util/BigBuffer.h"
 #include "util/Util.h"
 
+using android::StringPiece16;
+
 namespace aapt {
 
 class XmlFlattenerTest : public ::testing::Test {
diff --git a/tools/aapt2/integration-tests/AppOne/Android.mk b/tools/aapt2/integration-tests/AppOne/Android.mk
index bc40a62..a6f32d4 100644
--- a/tools/aapt2/integration-tests/AppOne/Android.mk
+++ b/tools/aapt2/integration-tests/AppOne/Android.mk
@@ -24,5 +24,5 @@
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     AaptTestStaticLibOne \
     AaptTestStaticLibTwo
-LOCAL_AAPT_FLAGS := --no-version-vectors
+LOCAL_AAPT_FLAGS := --no-version-vectors --no-version-transitions
 include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml b/tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml
similarity index 63%
rename from packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
rename to tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml
index 85573e0..e10e6c2 100644
--- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_root.xml
+++ b/tools/aapt2/integration-tests/AppOne/res/transition/transition_set.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2015 The Android Open Source Project
+     Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,11 +14,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="350"
-        android:propertyName="alpha"
-        android:valueFrom="0.3"
-        android:valueTo="1.0"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+    android:transitionOrdering="sequential">
+    <fade android:fadingMode="fade_out" />
+    <changeBounds />
+    <fade android:fadingMode="fade_in" />
+</transitionSet>
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index 644f59f..3d5b5b1 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -110,7 +110,7 @@
  public:
   virtual ~IFileCollection() = default;
 
-  virtual IFile* FindFile(const StringPiece& path) = 0;
+  virtual IFile* FindFile(const android::StringPiece& path) = 0;
   virtual std::unique_ptr<IFileCollectionIterator> Iterator() = 0;
 };
 
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 828f34e..027cbd0 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -16,14 +16,16 @@
 
 #include "io/FileSystem.h"
 
+#include "androidfw/StringPiece.h"
 #include "utils/FileMap.h"
 
 #include "Source.h"
 #include "util/Files.h"
 #include "util/Maybe.h"
-#include "util/StringPiece.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 namespace io {
 
@@ -54,13 +56,11 @@
 }
 
 IFile* FileCollection::InsertFile(const StringPiece& path) {
-  return (files_[path.ToString()] =
-              util::make_unique<RegularFile>(Source(path)))
-      .get();
+  return (files_[path.to_string()] = util::make_unique<RegularFile>(Source(path))).get();
 }
 
 IFile* FileCollection::FindFile(const StringPiece& path) {
-  auto iter = files_.find(path.ToString());
+  auto iter = files_.find(path.to_string());
   if (iter != files_.end()) {
     return iter->second.get();
   }
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index 84f851f..dfd3717 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -59,8 +59,8 @@
   /**
    * Adds a file located at path. Returns the IFile representation of that file.
    */
-  IFile* InsertFile(const StringPiece& path);
-  IFile* FindFile(const StringPiece& path) override;
+  IFile* InsertFile(const android::StringPiece& path);
+  IFile* FindFile(const android::StringPiece& path) override;
   std::unique_ptr<IFileCollectionIterator> Iterator() override;
 
  private:
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index f4a128e..62b436f 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -22,6 +22,8 @@
 #include "Source.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 namespace io {
 
@@ -107,7 +109,7 @@
     std::string zip_entry_path =
         std::string(reinterpret_cast<const char*>(zip_entry_name.name),
                     zip_entry_name.name_length);
-    std::string nested_path = path.ToString() + "@" + zip_entry_path;
+    std::string nested_path = path.to_string() + "@" + zip_entry_path;
     collection->files_[zip_entry_path] = util::make_unique<ZipFile>(
         collection->handle_, zip_data, Source(nested_path));
   }
@@ -120,7 +122,7 @@
 }
 
 IFile* ZipFileCollection::FindFile(const StringPiece& path) {
-  auto iter = files_.find(path.ToString());
+  auto iter = files_.find(path.to_string());
   if (iter != files_.end()) {
     return iter->second.get();
   }
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 85ca1ae..634adad 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -21,8 +21,9 @@
 
 #include <map>
 
+#include "androidfw/StringPiece.h"
+
 #include "io/File.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 namespace io {
@@ -64,10 +65,10 @@
  */
 class ZipFileCollection : public IFileCollection {
  public:
-  static std::unique_ptr<ZipFileCollection> Create(const StringPiece& path,
+  static std::unique_ptr<ZipFileCollection> Create(const android::StringPiece& path,
                                                    std::string* outError);
 
-  io::IFile* FindFile(const StringPiece& path) override;
+  io::IFile* FindFile(const android::StringPiece& path) override;
   std::unique_ptr<IFileCollectionIterator> Iterator() override;
 
   ~ZipFileCollection() override;
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 2951e5c..a0ef00b 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -20,6 +20,8 @@
 
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 void AnnotationProcessor::AppendCommentLine(std::string& comment) {
@@ -54,7 +56,7 @@
   for (StringPiece line : util::Tokenize(comment, '\n')) {
     line = util::TrimWhitespace(line);
     if (!line.empty()) {
-      std::string lineCopy = line.ToString();
+      std::string lineCopy = line.to_string();
       AppendCommentLine(lineCopy);
     }
   }
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index 666a7f3..99cd44f 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -20,7 +20,7 @@
 #include <sstream>
 #include <string>
 
-#include "util/StringPiece.h"
+#include "androidfw/StringPiece.h"
 
 namespace aapt {
 
@@ -58,7 +58,7 @@
    * configurations,
    * we need to collect all the comments.
    */
-  void AppendComment(const StringPiece& comment);
+  void AppendComment(const android::StringPiece& comment);
 
   void AppendNewLine();
 
@@ -66,7 +66,7 @@
    * Writes the comments and annotations to the stream, with the given prefix
    * before each line.
    */
-  void WriteToStream(std::ostream* out, const StringPiece& prefix) const;
+  void WriteToStream(std::ostream* out, const android::StringPiece& prefix) const;
 
  private:
   enum : uint32_t {
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
index f1f1f92..53d6ea1 100644
--- a/tools/aapt2/java/ClassDefinition.cpp
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -16,7 +16,9 @@
 
 #include "java/ClassDefinition.h"
 
-#include "util/StringPiece.h"
+#include "androidfw/StringPiece.h"
+
+using android::StringPiece;
 
 namespace aapt {
 
@@ -43,7 +45,7 @@
   }
   *out << "final class " << name_ << " {\n";
 
-  std::string new_prefix = prefix.ToString();
+  std::string new_prefix = prefix.to_string();
   new_prefix.append(kIndent);
 
   for (const std::unique_ptr<ClassMember>& member : members_) {
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index d8b61d9..64e4b29 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -21,10 +21,10 @@
 #include <string>
 
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 
 #include "Resource.h"
 #include "java/AnnotationProcessor.h"
-#include "util/StringPiece.h"
 #include "util/Util.h"
 
 namespace aapt {
@@ -41,7 +41,7 @@
 
   virtual bool empty() const = 0;
 
-  virtual void WriteToStream(const StringPiece& prefix, bool final,
+  virtual void WriteToStream(const android::StringPiece& prefix, bool final,
                              std::ostream* out) const {
     processor_.WriteToStream(out, prefix);
   }
@@ -53,12 +53,12 @@
 template <typename T>
 class PrimitiveMember : public ClassMember {
  public:
-  PrimitiveMember(const StringPiece& name, const T& val)
-      : name_(name.ToString()), val_(val) {}
+  PrimitiveMember(const android::StringPiece& name, const T& val)
+      : name_(name.to_string()), val_(val) {}
 
   bool empty() const override { return false; }
 
-  void WriteToStream(const StringPiece& prefix, bool final,
+  void WriteToStream(const android::StringPiece& prefix, bool final,
                      std::ostream* out) const override {
     ClassMember::WriteToStream(prefix, final, out);
 
@@ -79,12 +79,12 @@
 template <>
 class PrimitiveMember<std::string> : public ClassMember {
  public:
-  PrimitiveMember(const StringPiece& name, const std::string& val)
-      : name_(name.ToString()), val_(val) {}
+  PrimitiveMember(const android::StringPiece& name, const std::string& val)
+      : name_(name.to_string()), val_(val) {}
 
   bool empty() const override { return false; }
 
-  void WriteToStream(const StringPiece& prefix, bool final,
+  void WriteToStream(const android::StringPiece& prefix, bool final,
                      std::ostream* out) const override {
     ClassMember::WriteToStream(prefix, final, out);
 
@@ -106,14 +106,13 @@
 template <typename T>
 class PrimitiveArrayMember : public ClassMember {
  public:
-  explicit PrimitiveArrayMember(const StringPiece& name)
-      : name_(name.ToString()) {}
+  explicit PrimitiveArrayMember(const android::StringPiece& name) : name_(name.to_string()) {}
 
   void AddElement(const T& val) { elements_.push_back(val); }
 
   bool empty() const override { return false; }
 
-  void WriteToStream(const StringPiece& prefix, bool final,
+  void WriteToStream(const android::StringPiece& prefix, bool final,
                      std::ostream* out) const override {
     ClassMember::WriteToStream(prefix, final, out);
 
@@ -147,22 +146,18 @@
 
 class ClassDefinition : public ClassMember {
  public:
-  static bool WriteJavaFile(const ClassDefinition* def,
-                            const StringPiece& package, bool final,
-                            std::ostream* out);
+  static bool WriteJavaFile(const ClassDefinition* def, const android::StringPiece& package,
+                            bool final, std::ostream* out);
 
-  ClassDefinition(const StringPiece& name, ClassQualifier qualifier,
-                  bool createIfEmpty)
-      : name_(name.ToString()),
-        qualifier_(qualifier),
-        create_if_empty_(createIfEmpty) {}
+  ClassDefinition(const android::StringPiece& name, ClassQualifier qualifier, bool createIfEmpty)
+      : name_(name.to_string()), qualifier_(qualifier), create_if_empty_(createIfEmpty) {}
 
   void AddMember(std::unique_ptr<ClassMember> member) {
     members_.push_back(std::move(member));
   }
 
   bool empty() const override;
-  void WriteToStream(const StringPiece& prefix, bool final,
+  void WriteToStream(const android::StringPiece& prefix, bool final,
                      std::ostream* out) const override;
 
  private:
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 6e7c7078..b71dc48 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -23,6 +23,7 @@
 #include <tuple>
 
 #include "android-base/logging.h"
+#include "androidfw/StringPiece.h"
 
 #include "NameMangler.h"
 #include "Resource.h"
@@ -32,7 +33,8 @@
 #include "java/AnnotationProcessor.h"
 #include "java/ClassDefinition.h"
 #include "process/SymbolTable.h"
-#include "util/StringPiece.h"
+
+using android::StringPiece;
 
 namespace aapt {
 
@@ -58,7 +60,7 @@
  * Replace those with '_'.
  */
 static std::string Transform(const StringPiece& symbol) {
-  std::string output = symbol.ToString();
+  std::string output = symbol.to_string();
   for (char& c : output) {
     if (c == '.' || c == '-') {
       c = '_';
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 190e73b..5cf556e 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -20,10 +20,11 @@
 #include <ostream>
 #include <string>
 
+#include "androidfw/StringPiece.h"
+
 #include "ResourceTable.h"
 #include "ResourceValues.h"
 #include "process/IResourceTableConsumer.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 
@@ -69,22 +70,20 @@
    * We need to generate these symbols in a separate file.
    * Returns true on success.
    */
-  bool Generate(const StringPiece& packageNameToGenerate, std::ostream* out);
+  bool Generate(const android::StringPiece& packageNameToGenerate, std::ostream* out);
 
-  bool Generate(const StringPiece& packageNameToGenerate,
-                const StringPiece& outputPackageName, std::ostream* out);
+  bool Generate(const android::StringPiece& packageNameToGenerate,
+                const android::StringPiece& outputPackageName, std::ostream* out);
 
   const std::string& getError() const;
 
  private:
-  bool AddMembersToTypeClass(const StringPiece& packageNameToGenerate,
-                             const ResourceTablePackage* package,
-                             const ResourceTableType* type,
+  bool AddMembersToTypeClass(const android::StringPiece& packageNameToGenerate,
+                             const ResourceTablePackage* package, const ResourceTableType* type,
                              ClassDefinition* outTypeClassDef);
 
-  void AddMembersToStyleableClass(const StringPiece& packageNameToGenerate,
-                                  const std::string& entryName,
-                                  const Styleable* styleable,
+  void AddMembersToStyleableClass(const android::StringPiece& packageNameToGenerate,
+                                  const std::string& entryName, const Styleable* styleable,
                                   ClassDefinition* outStyleableClassDef);
 
   bool SkipSymbol(SymbolState state);
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 3d3d24e..55c5cb2 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -22,6 +22,8 @@
 #include "test/Test.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index db84f29..de8e59a 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -24,6 +24,8 @@
 #include "util/Maybe.h"
 #include "xml/XmlDom.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag,
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
index 5518fe2..b029b20 100644
--- a/tools/aapt2/jni/aapt2_jni.cpp
+++ b/tools/aapt2/jni/aapt2_jni.cpp
@@ -26,6 +26,8 @@
 
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 extern int Compile(const std::vector<StringPiece> &args);
 extern int Link(const std::vector<StringPiece> &args);
@@ -65,9 +67,8 @@
  * The returned pieces can only be used while the original ones have not been
  * destroyed.
  */
-static std::vector<aapt::StringPiece> extract_pieces(
-    const std::vector<ScopedUtfChars> &strings) {
-  std::vector<aapt::StringPiece> pieces;
+static std::vector<StringPiece> extract_pieces(const std::vector<ScopedUtfChars> &strings) {
+  std::vector<StringPiece> pieces;
 
   std::for_each(
       strings.begin(), strings.end(),
@@ -80,8 +81,7 @@
     JNIEnv *env, jclass aapt_obj, jobject arguments_obj) {
   std::vector<ScopedUtfChars> compile_args_jni =
       list_to_utfchars(env, arguments_obj);
-  std::vector<aapt::StringPiece> compile_args =
-      extract_pieces(compile_args_jni);
+  std::vector<StringPiece> compile_args = extract_pieces(compile_args_jni);
   aapt::Compile(compile_args);
 }
 
@@ -89,7 +89,7 @@
     JNIEnv *env, jclass aapt_obj, jobject arguments_obj) {
   std::vector<ScopedUtfChars> link_args_jni =
       list_to_utfchars(env, arguments_obj);
-  std::vector<aapt::StringPiece> link_args = extract_pieces(link_args_jni);
+  std::vector<StringPiece> link_args = extract_pieces(link_args_jni);
   aapt::Link(link_args);
 }
 
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index b525758..f7e0f8f 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -23,6 +23,7 @@
 
 #include "android-base/errors.h"
 #include "android-base/file.h"
+#include "androidfw/StringPiece.h"
 #include "google/protobuf/io/coded_stream.h"
 
 #include "AppInfo.h"
@@ -51,9 +52,9 @@
 #include "split/TableSplitter.h"
 #include "unflatten/BinaryResourceParser.h"
 #include "util/Files.h"
-#include "util/StringPiece.h"
 #include "xml/XmlDom.h"
 
+using android::StringPiece;
 using ::google::protobuf::io::CopyingOutputStreamAdaptor;
 
 namespace aapt {
@@ -79,6 +80,7 @@
   // Optimizations/features.
   bool no_auto_version = false;
   bool no_version_vectors = false;
+  bool no_version_transitions = false;
   bool no_resource_deduping = false;
   bool no_xml_namespaces = false;
   bool do_not_compress_anything = false;
@@ -121,7 +123,7 @@
   }
 
   void SetCompilationPackage(const StringPiece& package_name) {
-    compilation_package_ = package_name.ToString();
+    compilation_package_ = package_name.to_string();
   }
 
   uint8_t GetPackageId() override { return package_id_; }
@@ -249,6 +251,7 @@
 struct ResourceFileFlattenerOptions {
   bool no_auto_version = false;
   bool no_version_vectors = false;
+  bool no_version_transitions = false;
   bool no_xml_namespaces = false;
   bool keep_raw_values = false;
   bool do_not_compress_anything = false;
@@ -305,6 +308,23 @@
   return ArchiveEntry::kCompress;
 }
 
+static bool IsTransitionElement(const std::string& name) {
+  return
+    name == "fade" ||
+    name == "changeBounds" ||
+    name == "slide" ||
+    name == "explode" ||
+    name == "changeImageTransform" ||
+    name == "changeTransform" ||
+    name == "changeClipBounds" ||
+    name == "autoTransition" ||
+    name == "recolor" ||
+    name == "changeScroll" ||
+    name == "transitionSet" ||
+    name == "transition" ||
+    name == "transitionManager";
+}
+
 bool ResourceFileFlattener::LinkAndVersionXmlFile(
     ResourceTable* table, FileOperation* file_op,
     std::queue<FileOperation>* out_file_op_queue) {
@@ -344,6 +364,17 @@
         }
       }
     }
+    if (options_.no_version_transitions) {
+      // Skip this if it is a transition resource.
+      xml::Element* el = xml::FindRootElement(doc);
+      if (el && el->namespace_uri.empty()) {
+        if (IsTransitionElement(el->name)) {
+          // We are NOT going to version this file.
+          file_op->skip_version = true;
+          return true;
+        }
+      }
+    }
 
     const ConfigDescription& config = file_op->config;
 
@@ -1383,6 +1414,7 @@
         options_.extensions_to_not_compress;
     file_flattener_options.no_auto_version = options_.no_auto_version;
     file_flattener_options.no_version_vectors = options_.no_version_vectors;
+    file_flattener_options.no_version_transitions = options_.no_version_transitions;
     file_flattener_options.no_xml_namespaces = options_.no_xml_namespaces;
     file_flattener_options.update_proguard_spec =
         static_cast<bool>(options_.generate_proguard_rules_path);
@@ -1862,6 +1894,11 @@
                           "Use this only\n"
                           "when building with vector drawable support library",
                           &options.no_version_vectors)
+          .OptionalSwitch("--no-version-transitions",
+                          "Disables automatic versioning of transition resources. "
+                          "Use this only\n"
+                          "when building with transition support library",
+                          &options.no_version_transitions)
           .OptionalSwitch("--no-resource-deduping",
                           "Disables automatic deduping of resources with\n"
                           "identical values across compatible configurations.",
@@ -2011,14 +2048,14 @@
   for (std::string& extra_package : extra_java_packages) {
     // A given package can actually be a colon separated list of packages.
     for (StringPiece package : util::Split(extra_package, ':')) {
-      options.extra_java_packages.insert(package.ToString());
+      options.extra_java_packages.insert(package.to_string());
     }
   }
 
   if (product_list) {
     for (StringPiece product : util::Tokenize(product_list.value(), ',')) {
       if (product != "" && product != "default") {
-        options.products.insert(product.ToString());
+        options.products.insert(product.to_string());
       }
     }
   }
@@ -2103,6 +2140,7 @@
   if (options.static_lib) {
     options.no_auto_version = true;
     options.no_version_vectors = true;
+    options.no_version_transitions = true;
   }
 
   LinkCommand cmd(&context, options);
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 4185937..e5eaf2f 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -25,6 +25,8 @@
 #include "xml/XmlActionExecutor.h"
 #include "xml/XmlDom.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 /**
@@ -293,7 +295,7 @@
   CHECK(attr != nullptr);
 
   std::string original_package = std::move(attr->value);
-  attr->value = package_override.ToString();
+  attr->value = package_override.to_string();
 
   FullyQualifiedClassNameVisitor visitor(original_package);
   manifest_el->Accept(&visitor);
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index fc6970c..12a304a 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -18,6 +18,8 @@
 
 #include "test/Test.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 struct ManifestFixerTest : public ::testing::Test {
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index be787b2..ea68b61 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -30,6 +30,8 @@
 #include "util/Util.h"
 #include "xml/XmlUtil.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 namespace {
@@ -192,8 +194,7 @@
       const StringPiece& alias,
       const StringPiece& local_package) const override {
     if (alias.empty()) {
-      return xml::ExtractedPackage{local_package.ToString(),
-                                   true /* private */};
+      return xml::ExtractedPackage{local_package.to_string(), true /* private */};
     }
     return {};
   }
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index d808da3..7e7b9fb 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -24,6 +24,8 @@
 #include "ValueVisitor.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
@@ -351,9 +353,8 @@
     const std::string& package, const FileReference& file_ref) {
   StringPiece prefix, entry, suffix;
   if (util::ExtractResFilePathParts(*file_ref.path, &prefix, &entry, &suffix)) {
-    std::string mangled_entry =
-        NameMangler::MangleEntry(package, entry.ToString());
-    std::string newPath = prefix.ToString() + mangled_entry + suffix.ToString();
+    std::string mangled_entry = NameMangler::MangleEntry(package, entry.to_string());
+    std::string newPath = prefix.to_string() + mangled_entry + suffix.to_string();
     std::unique_ptr<FileReference> new_file_ref =
         util::make_unique<FileReference>(
             master_table_->string_pool.MakeRef(newPath));
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index 4ab83c3..c96b1b0 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -96,8 +96,8 @@
    * An io::IFileCollection is needed in order to find the referenced Files and
    * process them.
    */
-  bool MergeAndMangle(const Source& src, const StringPiece& package,
-                      ResourceTable* table, io::IFileCollection* collection);
+  bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table,
+                      io::IFileCollection* collection);
 
   /**
    * Merges a compiled file that belongs to this same or empty package. This is
diff --git a/tools/aapt2/link/VersionCollapser_test.cpp b/tools/aapt2/link/VersionCollapser_test.cpp
index 1b5592f..44babb2 100644
--- a/tools/aapt2/link/VersionCollapser_test.cpp
+++ b/tools/aapt2/link/VersionCollapser_test.cpp
@@ -18,6 +18,8 @@
 
 #include "test/Test.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 static std::unique_ptr<ResourceTable> BuildTableWithConfigs(
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 767384d..1a3da73 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -25,6 +25,8 @@
 #include "ValueVisitor.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 void SymbolTable::AppendSource(std::unique_ptr<ISymbolSource> source) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 25f7565..cf597bb 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -154,7 +154,7 @@
  public:
   AssetManagerSymbolSource() = default;
 
-  bool AddAssetPath(const StringPiece& path);
+  bool AddAssetPath(const android::StringPiece& path);
 
   std::unique_ptr<SymbolTable::Symbol> FindByName(
       const ResourceName& name) override;
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index 0d0e46d..7230b55 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -22,7 +22,7 @@
 #include "proto/ProtoSerialize.h"
 #include "util/BigBuffer.h"
 
-#include <android-base/logging.h>
+#include "android-base/logging.h"
 
 using google::protobuf::io::CodedOutputStream;
 using google::protobuf::io::CodedInputStream;
@@ -239,7 +239,7 @@
       if (type->id) {
         pb_type->set_id(type->id.value());
       }
-      pb_type->set_name(ToString(type->type).ToString());
+      pb_type->set_name(ToString(type->type).to_string());
 
       for (auto& entry : type->entries) {
         pb::Entry* pb_entry = pb_type->add_entries();
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 8001033..e2a752e 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,10 @@
 # Android Asset Packaging Tool 2.0 (AAPT2) release notes
 
+## Version 2.5
+### `aapt2 link ...`
+- Transition XML versioning: Adds a new flag `--no-version-transitions` to disable automatic
+  versioning of Transition XML resources.
+
 ## Version 2.4
 ### `aapt2 link ...`
 - Supports `<meta-data>` tags in `<manifest>`.
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 7aad86f..38cfd2e 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -21,6 +21,7 @@
 #include <set>
 #include <unordered_map>
 #include <vector>
+
 #include "android-base/logging.h"
 
 #include "ConfigDescription.h"
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 9377306..6888cf3 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -37,95 +37,86 @@
 
   StringPool* string_pool() { return &table_->string_pool; }
 
-  ResourceTableBuilder& SetPackageId(const StringPiece& package_name,
-                                     uint8_t id) {
+  ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id) {
     ResourceTablePackage* package = table_->CreatePackage(package_name, id);
     CHECK(package != nullptr);
     return *this;
   }
 
-  ResourceTableBuilder& AddSimple(const StringPiece& name,
-                                  const ResourceId& id = {}) {
+  ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {}) {
     return AddValue(name, id, util::make_unique<Id>());
   }
 
-  ResourceTableBuilder& AddSimple(const StringPiece& name,
-                                  const ConfigDescription& config,
+  ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ConfigDescription& config,
                                   const ResourceId& id = {}) {
     return AddValue(name, config, id, util::make_unique<Id>());
   }
 
-  ResourceTableBuilder& AddReference(const StringPiece& name,
-                                     const StringPiece& ref) {
+  ResourceTableBuilder& AddReference(const android::StringPiece& name,
+                                     const android::StringPiece& ref) {
     return AddReference(name, {}, ref);
   }
 
-  ResourceTableBuilder& AddReference(const StringPiece& name,
-                                     const ResourceId& id,
-                                     const StringPiece& ref) {
+  ResourceTableBuilder& AddReference(const android::StringPiece& name, const ResourceId& id,
+                                     const android::StringPiece& ref) {
     return AddValue(name, id,
                     util::make_unique<Reference>(ParseNameOrDie(ref)));
   }
 
-  ResourceTableBuilder& AddString(const StringPiece& name,
-                                  const StringPiece& str) {
+  ResourceTableBuilder& AddString(const android::StringPiece& name,
+                                  const android::StringPiece& str) {
     return AddString(name, {}, str);
   }
 
-  ResourceTableBuilder& AddString(const StringPiece& name, const ResourceId& id,
-                                  const StringPiece& str) {
+  ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
+                                  const android::StringPiece& str) {
     return AddValue(
         name, id, util::make_unique<String>(table_->string_pool.MakeRef(str)));
   }
 
-  ResourceTableBuilder& AddString(const StringPiece& name, const ResourceId& id,
+  ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id,
                                   const ConfigDescription& config,
-                                  const StringPiece& str) {
+                                  const android::StringPiece& str) {
     return AddValue(name, config, id, util::make_unique<String>(
                                           table_->string_pool.MakeRef(str)));
   }
 
-  ResourceTableBuilder& AddFileReference(const StringPiece& name,
-                                         const StringPiece& path) {
+  ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
+                                         const android::StringPiece& path) {
     return AddFileReference(name, {}, path);
   }
 
-  ResourceTableBuilder& AddFileReference(const StringPiece& name,
-                                         const ResourceId& id,
-                                         const StringPiece& path) {
+  ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const ResourceId& id,
+                                         const android::StringPiece& path) {
     return AddValue(name, id, util::make_unique<FileReference>(
                                   table_->string_pool.MakeRef(path)));
   }
 
-  ResourceTableBuilder& AddFileReference(const StringPiece& name,
-                                         const StringPiece& path,
+  ResourceTableBuilder& AddFileReference(const android::StringPiece& name,
+                                         const android::StringPiece& path,
                                          const ConfigDescription& config) {
     return AddValue(name, config, {}, util::make_unique<FileReference>(
                                           table_->string_pool.MakeRef(path)));
   }
 
-  ResourceTableBuilder& AddValue(const StringPiece& name,
-                                 std::unique_ptr<Value> value) {
+  ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value) {
     return AddValue(name, {}, std::move(value));
   }
 
-  ResourceTableBuilder& AddValue(const StringPiece& name, const ResourceId& id,
+  ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id,
                                  std::unique_ptr<Value> value) {
     return AddValue(name, {}, id, std::move(value));
   }
 
-  ResourceTableBuilder& AddValue(const StringPiece& name,
-                                 const ConfigDescription& config,
-                                 const ResourceId& id,
-                                 std::unique_ptr<Value> value) {
+  ResourceTableBuilder& AddValue(const android::StringPiece& name, const ConfigDescription& config,
+                                 const ResourceId& id, std::unique_ptr<Value> value) {
     ResourceName res_name = ParseNameOrDie(name);
     CHECK(table_->AddResourceAllowMangled(res_name, id, config, {},
                                           std::move(value), &diagnostics_));
     return *this;
   }
 
-  ResourceTableBuilder& SetSymbolState(const StringPiece& name,
-                                       const ResourceId& id,
+  ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
                                        SymbolState state) {
     ResourceName res_name = ParseNameOrDie(name);
     Symbol symbol;
@@ -144,8 +135,8 @@
   std::unique_ptr<ResourceTable> table_ = util::make_unique<ResourceTable>();
 };
 
-inline std::unique_ptr<Reference> BuildReference(
-    const StringPiece& ref, const Maybe<ResourceId>& id = {}) {
+inline std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
+                                                 const Maybe<ResourceId>& id = {}) {
   std::unique_ptr<Reference> reference =
       util::make_unique<Reference>(ParseNameOrDie(ref));
   reference->id = id;
@@ -174,7 +165,7 @@
     return *this;
   }
 
-  ValueBuilder& SetComment(const StringPiece& str) {
+  ValueBuilder& SetComment(const android::StringPiece& str) {
     value_->SetComment(str);
     return *this;
   }
@@ -199,7 +190,7 @@
     return *this;
   }
 
-  AttributeBuilder& AddItem(const StringPiece& name, uint32_t value) {
+  AttributeBuilder& AddItem(const android::StringPiece& name, uint32_t value) {
     attr_->symbols.push_back(Attribute::Symbol{
         Reference(ResourceName({}, ResourceType::kId, name)), value});
     return *this;
@@ -217,18 +208,18 @@
  public:
   StyleBuilder() = default;
 
-  StyleBuilder& SetParent(const StringPiece& str) {
+  StyleBuilder& SetParent(const android::StringPiece& str) {
     style_->parent = Reference(ParseNameOrDie(str));
     return *this;
   }
 
-  StyleBuilder& AddItem(const StringPiece& str, std::unique_ptr<Item> value) {
+  StyleBuilder& AddItem(const android::StringPiece& str, std::unique_ptr<Item> value) {
     style_->entries.push_back(
         Style::Entry{Reference(ParseNameOrDie(str)), std::move(value)});
     return *this;
   }
 
-  StyleBuilder& AddItem(const StringPiece& str, const ResourceId& id,
+  StyleBuilder& AddItem(const android::StringPiece& str, const ResourceId& id,
                         std::unique_ptr<Item> value) {
     AddItem(str, std::move(value));
     style_->entries.back().key.id = id;
@@ -247,8 +238,7 @@
  public:
   StyleableBuilder() = default;
 
-  StyleableBuilder& AddItem(const StringPiece& str,
-                            const Maybe<ResourceId>& id = {}) {
+  StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {}) {
     styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
     styleable_->entries.back().id = id;
     return *this;
@@ -262,7 +252,7 @@
   std::unique_ptr<Styleable> styleable_ = util::make_unique<Styleable>();
 };
 
-inline std::unique_ptr<xml::XmlResource> BuildXmlDom(const StringPiece& str) {
+inline std::unique_ptr<xml::XmlResource> BuildXmlDom(const android::StringPiece& str) {
   std::stringstream in;
   in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
   StdErrDiagnostics diag;
@@ -273,7 +263,7 @@
 }
 
 inline std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(
-    IAaptContext* context, const StringPiece& str) {
+    IAaptContext* context, const android::StringPiece& str) {
   std::unique_ptr<xml::XmlResource> doc = BuildXmlDom(str);
   doc->file.name.package = context->GetCompilationPackage();
   return doc;
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 3689201..248921f 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -21,6 +21,7 @@
 
 #include "android-base/logging.h"
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 #include "gtest/gtest.h"
 
 #include "ConfigDescription.h"
@@ -30,7 +31,6 @@
 #include "ValueVisitor.h"
 #include "io/File.h"
 #include "process/IResourceTableConsumer.h"
-#include "util/StringPiece.h"
 
 //
 // GTEST 1.7 doesn't explicitly cast to bool, which causes explicit operators to
@@ -68,23 +68,22 @@
   return &diag;
 }
 
-inline ResourceName ParseNameOrDie(const StringPiece& str) {
+inline ResourceName ParseNameOrDie(const android::StringPiece& str) {
   ResourceNameRef ref;
   CHECK(ResourceUtils::ParseResourceName(str, &ref)) << "invalid resource name";
   return ref.ToResourceName();
 }
 
-inline ConfigDescription ParseConfigOrDie(const StringPiece& str) {
+inline ConfigDescription ParseConfigOrDie(const android::StringPiece& str) {
   ConfigDescription config;
   CHECK(ConfigDescription::Parse(str, &config)) << "invalid configuration";
   return config;
 }
 
 template <typename T>
-T* GetValueForConfigAndProduct(ResourceTable* table,
-                               const StringPiece& res_name,
+T* GetValueForConfigAndProduct(ResourceTable* table, const android::StringPiece& res_name,
                                const ConfigDescription& config,
-                               const StringPiece& product) {
+                               const android::StringPiece& product) {
   Maybe<ResourceTable::SearchResult> result =
       table->FindResource(ParseNameOrDie(res_name));
   if (result) {
@@ -98,19 +97,19 @@
 }
 
 template <typename T>
-T* GetValueForConfig(ResourceTable* table, const StringPiece& res_name,
+T* GetValueForConfig(ResourceTable* table, const android::StringPiece& res_name,
                      const ConfigDescription& config) {
   return GetValueForConfigAndProduct<T>(table, res_name, config, {});
 }
 
 template <typename T>
-T* GetValue(ResourceTable* table, const StringPiece& res_name) {
+T* GetValue(ResourceTable* table, const android::StringPiece& res_name) {
   return GetValueForConfig<T>(table, res_name, {});
 }
 
 class TestFile : public io::IFile {
  public:
-  explicit TestFile(const StringPiece& path) : source_(path) {}
+  explicit TestFile(const android::StringPiece& path) : source_(path) {}
 
   std::unique_ptr<io::IData> OpenAsData() override { return {}; }
 
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 7986329..63e5f16 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -70,8 +70,8 @@
 
 class ContextBuilder {
  public:
-  ContextBuilder& SetCompilationPackage(const StringPiece& package) {
-    context_->compilation_package_ = package.ToString();
+  ContextBuilder& SetCompilationPackage(const android::StringPiece& package) {
+    context_->compilation_package_ = package.to_string();
     return *this;
   }
 
@@ -103,9 +103,8 @@
 
 class StaticSymbolSourceBuilder {
  public:
-  StaticSymbolSourceBuilder& AddPublicSymbol(
-      const StringPiece& name, ResourceId id,
-      std::unique_ptr<Attribute> attr = {}) {
+  StaticSymbolSourceBuilder& AddPublicSymbol(const android::StringPiece& name, ResourceId id,
+                                             std::unique_ptr<Attribute> attr = {}) {
     std::unique_ptr<SymbolTable::Symbol> symbol =
         util::make_unique<SymbolTable::Symbol>(id, std::move(attr), true);
     symbol_source_->name_map_[ParseNameOrDie(name)] = symbol.get();
@@ -114,7 +113,7 @@
     return *this;
   }
 
-  StaticSymbolSourceBuilder& AddSymbol(const StringPiece& name, ResourceId id,
+  StaticSymbolSourceBuilder& AddSymbol(const android::StringPiece& name, ResourceId id,
                                        std::unique_ptr<Attribute> attr = {}) {
     std::unique_ptr<SymbolTable::Symbol> symbol =
         util::make_unique<SymbolTable::Symbol>(id, std::move(attr), false);
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index f034607..aa840e2 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -35,6 +35,8 @@
 #include <direct.h>
 #endif
 
+using android::StringPiece;
+
 namespace aapt {
 namespace file {
 
@@ -72,10 +74,9 @@
 
 inline static int MkdirImpl(const StringPiece& path) {
 #ifdef _WIN32
-  return _mkdir(path.ToString().c_str());
+  return _mkdir(path.to_string().c_str());
 #else
-  return mkdir(path.ToString().c_str(),
-               S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
+  return mkdir(path.to_string().c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP);
 #endif
 }
 
@@ -184,7 +185,7 @@
                         std::vector<std::string>* out_arglist,
                         std::string* out_error) {
   std::string contents;
-  if (!android::base::ReadFileToString(path.ToString(), &contents)) {
+  if (!android::base::ReadFileToString(path.to_string(), &contents)) {
     if (out_error) *out_error = "failed to read argument-list file";
     return false;
   }
@@ -192,7 +193,7 @@
   for (StringPiece line : util::Tokenize(contents, ' ')) {
     line = util::TrimWhitespace(line);
     if (!line.empty()) {
-      out_arglist->push_back(line.ToString());
+      out_arglist->push_back(line.to_string());
     }
   }
   return true;
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index a157dbd..95c492f 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -22,12 +22,12 @@
 #include <vector>
 
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 #include "utils/FileMap.h"
 
 #include "Diagnostics.h"
 #include "Maybe.h"
 #include "Source.h"
-#include "util/StringPiece.h"
 
 namespace aapt {
 namespace file {
@@ -50,51 +50,49 @@
   kSocket,
 };
 
-FileType GetFileType(const StringPiece& path);
+FileType GetFileType(const android::StringPiece& path);
 
 /*
  * Appends a path to `base`, separated by the directory separator.
  */
-void AppendPath(std::string* base, StringPiece part);
+void AppendPath(std::string* base, android::StringPiece part);
 
 /*
  * Makes all the directories in `path`. The last element in the path
  * is interpreted as a directory.
  */
-bool mkdirs(const StringPiece& path);
+bool mkdirs(const android::StringPiece& path);
 
 /**
  * Returns all but the last part of the path.
  */
-StringPiece GetStem(const StringPiece& path);
+android::StringPiece GetStem(const android::StringPiece& path);
 
 /**
  * Returns the last part of the path with extension.
  */
-StringPiece GetFilename(const StringPiece& path);
+android::StringPiece GetFilename(const android::StringPiece& path);
 
 /**
  * Returns the extension of the path. This is the entire string after
  * the first '.' of the last part of the path.
  */
-StringPiece GetExtension(const StringPiece& path);
+android::StringPiece GetExtension(const android::StringPiece& path);
 
 /**
  * Converts a package name (com.android.app) to a path: com/android/app
  */
-std::string PackageToPath(const StringPiece& package);
+std::string PackageToPath(const android::StringPiece& package);
 
 /**
  * Creates a FileMap for the file at path.
  */
-Maybe<android::FileMap> MmapPath(const StringPiece& path,
-                                 std::string* out_error);
+Maybe<android::FileMap> MmapPath(const android::StringPiece& path, std::string* out_error);
 
 /**
  * Reads the file at path and appends each line to the outArgList vector.
  */
-bool AppendArgsFromFile(const StringPiece& path,
-                        std::vector<std::string>* out_arglist,
+bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist,
                         std::string* out_error);
 
 /*
@@ -120,7 +118,7 @@
    * - Otherwise the full string is matched.
    * - match is not case-sensitive.
    */
-  bool SetPattern(const StringPiece& pattern);
+  bool SetPattern(const android::StringPiece& pattern);
 
   /**
    * Applies the filter, returning true for pass, false for fail.
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index d5c0c8a..cf22322 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -15,9 +15,6 @@
  */
 
 #include "util/Util.h"
-#include "util/BigBuffer.h"
-#include "util/Maybe.h"
-#include "util/StringPiece.h"
 
 #include <utils/Unicode.h>
 #include <algorithm>
@@ -25,6 +22,14 @@
 #include <string>
 #include <vector>
 
+#include "androidfw/StringPiece.h"
+
+#include "util/BigBuffer.h"
+#include "util/Maybe.h"
+
+using android::StringPiece;
+using android::StringPiece16;
+
 namespace aapt {
 namespace util {
 
@@ -36,7 +41,7 @@
   StringPiece::const_iterator current;
   do {
     current = std::find(start, end, sep);
-    parts.emplace_back(str.substr(start, current).ToString());
+    parts.emplace_back(str.substr(start, current).to_string());
     if (f) {
       std::string& part = parts.back();
       std::transform(part.begin(), part.end(), part.begin(), f);
@@ -162,7 +167,7 @@
   }
 
   if (util::IsJavaClassName(classname)) {
-    return classname.ToString();
+    return classname.to_string();
   }
 
   if (package.empty()) {
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 05e9cc5..f8fa80e 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -24,11 +24,11 @@
 #include <vector>
 
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
 #include "utils/ByteOrder.h"
 
 #include "util/BigBuffer.h"
 #include "util/Maybe.h"
-#include "util/StringPiece.h"
 
 #ifdef _WIN32
 // TODO(adamlesinski): remove once http://b/32447322 is resolved.
@@ -44,26 +44,24 @@
 namespace aapt {
 namespace util {
 
-std::vector<std::string> Split(const StringPiece& str, char sep);
-std::vector<std::string> SplitAndLowercase(const StringPiece& str, char sep);
+std::vector<std::string> Split(const android::StringPiece& str, char sep);
+std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
 
 /**
  * Returns true if the string starts with prefix.
  */
-bool StartsWith(const StringPiece& str, const StringPiece& prefix);
+bool StartsWith(const android::StringPiece& str, const android::StringPiece& prefix);
 
 /**
  * Returns true if the string ends with suffix.
  */
-bool EndsWith(const StringPiece& str, const StringPiece& suffix);
+bool EndsWith(const android::StringPiece& str, const android::StringPiece& suffix);
 
 /**
  * Creates a new StringPiece16 that points to a substring
  * of the original string without leading or trailing whitespace.
  */
-StringPiece TrimWhitespace(const StringPiece& str);
-
-StringPiece TrimWhitespace(const StringPiece& str);
+android::StringPiece TrimWhitespace(const android::StringPiece& str);
 
 /**
  * UTF-16 isspace(). It basically checks for lower range characters that are
@@ -75,18 +73,18 @@
  * Returns an iterator to the first character that is not alpha-numeric and that
  * is not in the allowedChars set.
  */
-StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
-    const StringPiece& str, const StringPiece& allowed_chars);
+android::StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
+    const android::StringPiece& str, const android::StringPiece& allowed_chars);
 
 /**
  * Tests that the string is a valid Java class name.
  */
-bool IsJavaClassName(const StringPiece& str);
+bool IsJavaClassName(const android::StringPiece& str);
 
 /**
  * Tests that the string is a valid Java package name.
  */
-bool IsJavaPackageName(const StringPiece& str);
+bool IsJavaPackageName(const android::StringPiece& str);
 
 /**
  * Converts the class name to a fully qualified class name from the given
@@ -97,8 +95,8 @@
  * .a.b         --> package.a.b
  * asdf.adsf    --> asdf.adsf
  */
-Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
-                                              const StringPiece& class_name);
+Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
+                                              const android::StringPiece& class_name);
 
 /**
  * Makes a std::unique_ptr<> with the template parameter inferred by the
@@ -138,7 +136,7 @@
  * stored as UTF-8,
  * the conversion to UTF-16 happens within ResStringPool.
  */
-StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx);
+android::StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx);
 
 /**
  * Helper method to extract a UTF-8 string from a StringPool. If the string is
@@ -159,11 +157,11 @@
  * which will
  * break the string interpolation.
  */
-bool VerifyJavaStringFormat(const StringPiece& str);
+bool VerifyJavaStringFormat(const android::StringPiece& str);
 
 class StringBuilder {
  public:
-  StringBuilder& Append(const StringPiece& str);
+  StringBuilder& Append(const android::StringPiece& str);
   const std::string& ToString() const;
   const std::string& Error() const;
 
@@ -194,8 +192,8 @@
 /**
  * Converts a UTF8 string to a UTF16 string.
  */
-std::u16string Utf8ToUtf16(const StringPiece& utf8);
-std::string Utf16ToUtf8(const StringPiece16& utf16);
+std::u16string Utf8ToUtf16(const android::StringPiece& utf8);
+std::string Utf16ToUtf8(const android::StringPiece16& utf16);
 
 /**
  * Writes the entire BigBuffer to the output stream.
@@ -220,22 +218,22 @@
 
     iterator& operator++();
 
-    StringPiece operator*() { return token_; }
+    android::StringPiece operator*() { return token_; }
     bool operator==(const iterator& rhs) const;
     bool operator!=(const iterator& rhs) const;
 
    private:
     friend class Tokenizer;
 
-    iterator(StringPiece s, char sep, StringPiece tok, bool end);
+    iterator(android::StringPiece s, char sep, android::StringPiece tok, bool end);
 
-    StringPiece str_;
+    android::StringPiece str_;
     char separator_;
-    StringPiece token_;
+    android::StringPiece token_;
     bool end_;
   };
 
-  Tokenizer(StringPiece str, char sep);
+  Tokenizer(android::StringPiece str, char sep);
 
   iterator begin() { return begin_; }
 
@@ -246,9 +244,7 @@
   const iterator end_;
 };
 
-inline Tokenizer Tokenize(const StringPiece& str, char sep) {
-  return Tokenizer(str, sep);
-}
+inline Tokenizer Tokenize(const android::StringPiece& str, char sep) { return Tokenizer(str, sep); }
 
 inline uint16_t HostToDevice16(uint16_t value) { return htods(value); }
 
@@ -267,8 +263,8 @@
  *
  * Returns true if successful.
  */
-bool ExtractResFilePathParts(const StringPiece& path, StringPiece* out_prefix,
-                             StringPiece* out_entry, StringPiece* out_suffix);
+bool ExtractResFilePathParts(const android::StringPiece& path, android::StringPiece* out_prefix,
+                             android::StringPiece* out_entry, android::StringPiece* out_suffix);
 
 }  // namespace util
 
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index cac3de4..e49aee5 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -20,6 +20,8 @@
 
 #include "test/Test.h"
 
+using android::StringPiece;
+
 namespace aapt {
 
 TEST(UtilTest, TrimOnlyWhitespace) {
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 960d361..fab2f19 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -29,6 +29,9 @@
 #include "XmlPullParser.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+using android::StringPiece16;
+
 namespace aapt {
 namespace xml {
 
@@ -52,10 +55,10 @@
 
   if (*p == 0) {
     out_ns->clear();
-    *out_name = StringPiece(name).ToString();
+    out_name->assign(name);
   } else {
-    *out_ns = StringPiece(name, (p - name)).ToString();
-    *out_name = StringPiece(p + 1).ToString();
+    out_ns->assign(name, (p - name));
+    out_name->assign(p + 1);
   }
 }
 
@@ -83,11 +86,11 @@
 
   std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
   if (prefix) {
-    ns->namespace_prefix = StringPiece(prefix).ToString();
+    ns->namespace_prefix = prefix;
   }
 
   if (uri) {
-    ns->namespace_uri = StringPiece(uri).ToString();
+    ns->namespace_uri = uri;
   }
 
   AddToStack(stack, parser, std::move(ns));
@@ -117,7 +120,7 @@
   while (*attrs) {
     Attribute attribute;
     SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
-    attribute.value = StringPiece(*attrs++).ToString();
+    attribute.value = *attrs++;
 
     // Insert in sorted order.
     auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(),
@@ -153,14 +156,14 @@
     if (!currentParent->children.empty()) {
       Node* last_child = currentParent->children.back().get();
       if (Text* text = NodeCast<Text>(last_child)) {
-        text->text += StringPiece(s, len).ToString();
+        text->text.append(s, len);
         return;
       }
     }
   }
 
   std::unique_ptr<Text> text = util::make_unique<Text>();
-  text->text = StringPiece(s, len).ToString();
+  text->text.assign(s, len);
   AddToStack(stack, parser, std::move(text));
 }
 
@@ -495,15 +498,14 @@
 Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
     const StringPiece& alias, const StringPiece& local_package) const {
   if (alias.empty()) {
-    return ExtractedPackage{local_package.ToString(), false /* private */};
+    return ExtractedPackage{local_package.to_string(), false /* private */};
   }
 
   const auto rend = package_decls_.rend();
   for (auto iter = package_decls_.rbegin(); iter != rend; ++iter) {
     if (alias == iter->prefix) {
       if (iter->package.package.empty()) {
-        return ExtractedPackage{local_package.ToString(),
-                                iter->package.private_namespace};
+        return ExtractedPackage{local_package.to_string(), iter->package.private_namespace};
       }
       return iter->package;
     }
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 720fe35..90cdfb6 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -22,10 +22,11 @@
 #include <string>
 #include <vector>
 
+#include "androidfw/StringPiece.h"
+
 #include "Diagnostics.h"
 #include "Resource.h"
 #include "ResourceValues.h"
-#include "util/StringPiece.h"
 #include "util/Util.h"
 #include "xml/XmlUtil.h"
 
@@ -100,13 +101,13 @@
   std::string name;
   std::vector<Attribute> attributes;
 
-  Attribute* FindAttribute(const StringPiece& ns, const StringPiece& name);
-  xml::Element* FindChild(const StringPiece& ns, const StringPiece& name);
-  xml::Element* FindChildWithAttribute(const StringPiece& ns,
-                                       const StringPiece& name,
-                                       const StringPiece& attr_ns,
-                                       const StringPiece& attr_name,
-                                       const StringPiece& attr_value);
+  Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name);
+  xml::Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
+  xml::Element* FindChildWithAttribute(const android::StringPiece& ns,
+                                       const android::StringPiece& name,
+                                       const android::StringPiece& attr_ns,
+                                       const android::StringPiece& attr_name,
+                                       const android::StringPiece& attr_value);
   std::vector<xml::Element*> GetChildElements();
   std::unique_ptr<Node> Clone() override;
 };
@@ -190,8 +191,7 @@
 
   void Visit(Namespace* ns) override;
   Maybe<ExtractedPackage> TransformPackageAlias(
-      const StringPiece& alias,
-      const StringPiece& local_package) const override;
+      const android::StringPiece& alias, const android::StringPiece& local_package) const override;
 
  private:
   struct PackageDecl {
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index e59fa86..c2a9c82 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -22,6 +22,8 @@
 #include "xml/XmlPullParser.h"
 #include "xml/XmlUtil.h"
 
+using android::StringPiece;
+
 namespace aapt {
 namespace xml {
 
@@ -136,15 +138,14 @@
 Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(
     const StringPiece& alias, const StringPiece& local_package) const {
   if (alias.empty()) {
-    return ExtractedPackage{local_package.ToString(), false /* private */};
+    return ExtractedPackage{local_package.to_string(), false /* private */};
   }
 
   const auto end_iter = package_aliases_.rend();
   for (auto iter = package_aliases_.rbegin(); iter != end_iter; ++iter) {
     if (alias == iter->prefix) {
       if (iter->package.package.empty()) {
-        return ExtractedPackage{local_package.ToString(),
-                                iter->package.private_namespace};
+        return ExtractedPackage{local_package.to_string(), iter->package.private_namespace};
       }
       return iter->package;
     }
@@ -188,19 +189,18 @@
 /**
  * Extracts the namespace and name of an expanded element or attribute name.
  */
-static void SplitName(const char* name, std::string& out_ns,
-                      std::string& out_name) {
+static void SplitName(const char* name, std::string* out_ns, std::string* out_name) {
   const char* p = name;
   while (*p != 0 && *p != kXmlNamespaceSep) {
     p++;
   }
 
   if (*p == 0) {
-    out_ns = std::string();
-    out_name = name;
+    out_ns->clear();
+    out_name->assign(name);
   } else {
-    out_ns = StringPiece(name, (p - name)).ToString();
-    out_name = p + 1;
+    out_ns->assign(name, (p - name));
+    out_name->assign(p + 1);
   }
 }
 
@@ -224,11 +224,11 @@
   EventData data = {Event::kStartElement,
                     XML_GetCurrentLineNumber(parser->parser_),
                     parser->depth_++};
-  SplitName(name, data.data1, data.data2);
+  SplitName(name, &data.data1, &data.data2);
 
   while (*attrs) {
     Attribute attribute;
-    SplitName(*attrs++, attribute.namespace_uri, attribute.name);
+    SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
     attribute.value = *attrs++;
 
     // Insert in sorted order.
@@ -245,9 +245,8 @@
                                                  int len) {
   XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
 
-  parser->event_queue_.push(
-      EventData{Event::kText, XML_GetCurrentLineNumber(parser->parser_),
-                parser->depth_, StringPiece(s, len).ToString()});
+  parser->event_queue_.push(EventData{Event::kText, XML_GetCurrentLineNumber(parser->parser_),
+                                      parser->depth_, std::string(s, len)});
 }
 
 void XMLCALL XmlPullParser::EndElementHandler(void* user_data,
@@ -257,7 +256,7 @@
   EventData data = {Event::kEndElement,
                     XML_GetCurrentLineNumber(parser->parser_),
                     --(parser->depth_)};
-  SplitName(name, data.data1, data.data2);
+  SplitName(name, &data.data1, &data.data2);
 
   // Move the data into the queue (no copy).
   parser->event_queue_.push(std::move(data));
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index ff58d60..cdeeefd 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -28,11 +28,11 @@
 #include <vector>
 
 #include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
 
 #include "Resource.h"
 #include "process/IResourceTableConsumer.h"
 #include "util/Maybe.h"
-#include "util/StringPiece.h"
 #include "xml/XmlUtil.h"
 
 namespace aapt {
@@ -119,8 +119,7 @@
    * 'package' will be set to 'defaultPackage'.
    */
   Maybe<ExtractedPackage> TransformPackageAlias(
-      const StringPiece& alias,
-      const StringPiece& local_package) const override;
+      const android::StringPiece& alias, const android::StringPiece& local_package) const override;
 
   //
   // Remaining methods are for retrieving information about attributes
@@ -146,8 +145,7 @@
   const_iterator begin_attributes() const;
   const_iterator end_attributes() const;
   size_t attribute_count() const;
-  const_iterator FindAttribute(StringPiece namespace_uri,
-                               StringPiece name) const;
+  const_iterator FindAttribute(android::StringPiece namespace_uri, android::StringPiece name) const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(XmlPullParser);
@@ -190,16 +188,16 @@
 /**
  * Finds the attribute in the current element within the global namespace.
  */
-Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
-                                 const StringPiece& name);
+Maybe<android::StringPiece> FindAttribute(const XmlPullParser* parser,
+                                          const android::StringPiece& name);
 
 /**
  * Finds the attribute in the current element within the global namespace. The
  * attribute's value
  * must not be the empty string.
  */
-Maybe<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
-                                         const StringPiece& name);
+Maybe<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+                                                  const android::StringPiece& name);
 
 //
 // Implementation
@@ -299,13 +297,13 @@
 }
 
 inline XmlPullParser::const_iterator XmlPullParser::FindAttribute(
-    StringPiece namespace_uri, StringPiece name) const {
+    android::StringPiece namespace_uri, android::StringPiece name) const {
   const auto end_iter = end_attributes();
   const auto iter = std::lower_bound(
       begin_attributes(), end_iter,
-      std::pair<StringPiece, StringPiece>(namespace_uri, name),
+      std::pair<android::StringPiece, android::StringPiece>(namespace_uri, name),
       [](const Attribute& attr,
-         const std::pair<StringPiece, StringPiece>& rhs) -> bool {
+         const std::pair<android::StringPiece, android::StringPiece>& rhs) -> bool {
         int cmp = attr.namespace_uri.compare(
             0, attr.namespace_uri.size(), rhs.first.data(), rhs.first.size());
         if (cmp < 0) return true;
diff --git a/tools/aapt2/xml/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp
index 4f18cd2..1cce485 100644
--- a/tools/aapt2/xml/XmlPullParser_test.cpp
+++ b/tools/aapt2/xml/XmlPullParser_test.cpp
@@ -18,8 +18,11 @@
 
 #include <sstream>
 
+#include "androidfw/StringPiece.h"
+
 #include "test/Test.h"
-#include "util/StringPiece.h"
+
+using android::StringPiece;
 
 namespace aapt {
 
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index d00f7f2..fb8cee8 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -21,6 +21,8 @@
 #include "util/Maybe.h"
 #include "util/Util.h"
 
+using android::StringPiece;
+
 namespace aapt {
 namespace xml {
 
@@ -42,7 +44,7 @@
     if (package.empty()) {
       return {};
     }
-    return ExtractedPackage{package.ToString(), false /* is_private */};
+    return ExtractedPackage{package.to_string(), false /* is_private */};
 
   } else if (util::StartsWith(namespace_uri, kSchemaPrivatePrefix)) {
     StringPiece schema_prefix = kSchemaPrivatePrefix;
@@ -52,7 +54,7 @@
     if (package.empty()) {
       return {};
     }
-    return ExtractedPackage{package.ToString(), true /* is_private */};
+    return ExtractedPackage{package.to_string(), true /* is_private */};
 
   } else if (namespace_uri == kSchemaAuto) {
     return ExtractedPackage{std::string(), true /* is_private */};
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 5365401..1650ac2 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -74,7 +74,7 @@
  *
  * http://schemas.android.com/apk/prv/res/<package>
  */
-std::string BuildPackageNamespace(const StringPiece& package,
+std::string BuildPackageNamespace(const android::StringPiece& package,
                                   bool private_reference = false);
 
 /**
@@ -90,7 +90,7 @@
    * package declaration.
    */
   virtual Maybe<ExtractedPackage> TransformPackageAlias(
-      const StringPiece& alias, const StringPiece& local_package) const = 0;
+      const android::StringPiece& alias, const android::StringPiece& local_package) const = 0;
 };
 
 /**
@@ -100,8 +100,7 @@
  * the namespace of the package declaration was private.
  */
 void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
-                                     const StringPiece& local_package,
-                                     Reference* in_ref);
+                                     const android::StringPiece& local_package, Reference* in_ref);
 
 }  // namespace xml
 }  // namespace aapt
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index d326935..147ed99 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.text.FontConfig;
 import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
@@ -292,7 +293,7 @@
 
     @LayoutlibDelegate
     /*package*/ static boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
-            int ttcIndex, List<FontListParser.Axis> listOfAxis,
+            int ttcIndex, List<FontConfig.Axis> listOfAxis,
             int weight, boolean isItalic) {
         assert false : "The only client of this method has been overriden.";
         return false;
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index a554b6d..f6c463f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.text.FontConfig;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
@@ -208,12 +209,12 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static FontFamily makeFamilyFromParsed(FontListParser.Family family,
+    /*package*/ static FontFamily makeFamilyFromParsed(FontConfig.Family family,
             Map<String, ByteBuffer> bufferForPath) {
-        FontFamily fontFamily = new FontFamily(family.lang, family.variant);
-        for (FontListParser.Font font : family.fonts) {
-            FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, font.fontName, font.weight,
-                    font.isItalic);
+        FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
+        for (FontConfig.Font font : family.getFonts()) {
+            FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, font.getFontName(),
+                    font.getWeight(), font.isItalic());
         }
         fontFamily.freeze();
         return fontFamily;
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 5ed5460..56898f1 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -340,12 +340,6 @@
     }
 
     @Override
-    public Rect getBoundsForNewConfiguration(int stackId) throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
     public void setScreenCaptureDisabled(int userId, boolean disabled) {
         // TODO Auto-generated method stub
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 663e56d..dff4f69 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -40,6 +40,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Notification;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -1324,6 +1325,12 @@
     }
 
     @Override
+    public Context createContextForSplit(String splitName) {
+        // pass
+        return null;
+    }
+
+    @Override
     public String[] databaseList() {
         // pass
         return null;
@@ -1820,6 +1827,13 @@
     }
 
     @Override
+    public ComponentName startServiceInForeground(Intent service,
+            int id, Notification notification) {
+        // pass
+        return null;
+    }
+
+    @Override
     public boolean stopService(Intent arg0) {
         // pass
         return false;
@@ -1832,6 +1846,13 @@
     }
 
     @Override
+    public ComponentName startServiceInForegroundAsUser(Intent service,
+            int id, Notification notification, UserHandle user) {
+        // pass
+        return null;
+    }
+
+    @Override
     public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
         // pass
         return false;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index bc7bc74..d325ee9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -42,14 +42,15 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VersionedPackage;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Handler;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
 import java.util.List;
@@ -71,6 +72,23 @@
     }
 
     @Override
+    public PackageInfo getPackageInfo(VersionedPackage versionedPackage,
+            @PackageInfoFlags int flags) throws NameNotFoundException {
+        return null;
+    }
+
+    @Override
+    public List<SharedLibraryInfo> getSharedLibraries(@InstallFlags int flags) {
+        return null;
+    }
+
+    @Override
+    public List<SharedLibraryInfo> getSharedLibrariesAsUser(@InstallFlags int flags,
+            int userId) {
+        return null;
+    }
+
+    @Override
     public String[] currentToCanonicalPackageNames(String[] names) {
         return new String[0];
     }
@@ -873,4 +891,9 @@
     public int getInstallReason(String packageName, UserHandle user) {
         return INSTALL_REASON_UNKNOWN;
     }
+
+    @Override
+    public boolean canRequestPackageInstalls() {
+        return false;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index a83f100..a51ad2e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -108,6 +108,10 @@
     }
 
     @Override
+    public void dispatchPointerCaptureChanged(boolean hasCapture) {
+    }
+
+    @Override
     public IBinder asBinder() {
         // pass for now.
         return null;
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index b071093..9dceba2 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -48,6 +48,8 @@
             # they may be used by apps for other purposes.)
             "en_XA": "~~~A",
             "ar_XB": "~~~B",
+            # Removed data from later versions of ICU
+            "ji": "Hebr", # Old code for Yiddish, still used in Java and Android
         }
         representative_locales = {
             # Android's additions
@@ -69,7 +71,7 @@
                 _, to_scr, to_region = get_locale_parts(to_locale)
                 if from_lang == 'und':
                     continue  # not very useful for our purposes
-                if from_region is None and to_region != '001':
+                if from_region is None and to_region not in ['001', 'ZZ']:
                     representative_locales.add(to_locale)
                 if from_scr is None:
                     likely_script_dict[from_locale] = to_scr
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 958279b..8cf7a24 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -22,6 +22,7 @@
 import android.net.IpConfiguration.ProxySettings;
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -708,6 +709,16 @@
 
     /**
      * @hide
+     * Indicate that a WifiConfiguration is temporary and should not be saved
+     * nor considered by AutoJoin.
+     */
+    @SystemApi
+    public boolean isEphemeral() {
+      return ephemeral;
+    }
+
+    /**
+     * @hide
      * A hint about whether or not the network represented by this WifiConfiguration
      * is metered. This is hinted at via the meteredHint bit on DHCP results set in
      * {@link com.android.server.wifi.WifiStateMachine}, or via a network score in
@@ -1805,14 +1816,48 @@
         mIpConfiguration.proxySettings = proxySettings;
     }
 
-    /** @hide */
+    /**
+     * Returns the HTTP proxy used by this object.
+     * @return a {@link ProxyInfo httpProxy} representing the proxy specified by this
+     *                  WifiConfiguration, or {@code null} if no proxy is specified.
+     */
     public ProxyInfo getHttpProxy() {
-        return mIpConfiguration.httpProxy;
+        if (mIpConfiguration.proxySettings == IpConfiguration.ProxySettings.NONE) {
+            return null;
+        }
+        return new ProxyInfo(mIpConfiguration.httpProxy);
     }
 
-    /** @hide */
+    /**
+     * Set the {@link ProxyInfo} for this WifiConfiguration.
+     * @param httpProxy {@link ProxyInfo} representing the httpProxy to be used by this
+     *                  WifiConfiguration. Setting this {@code null} will explicitly set no proxy,
+     *                  removing any proxy that was previously set.
+     * @exception throw IllegalArgumentException for invalid httpProxy
+     */
     public void setHttpProxy(ProxyInfo httpProxy) {
-        mIpConfiguration.httpProxy = httpProxy;
+        if (httpProxy == null) {
+            mIpConfiguration.setProxySettings(IpConfiguration.ProxySettings.NONE);
+            mIpConfiguration.setHttpProxy(null);
+            return;
+        }
+        ProxyInfo httpProxyCopy;
+        ProxySettings proxySettingCopy;
+        if (!Uri.EMPTY.equals(httpProxy.getPacFileUrl())) {
+            proxySettingCopy = IpConfiguration.ProxySettings.PAC;
+            // Construct a new PAC URL Proxy
+            httpProxyCopy = new ProxyInfo(httpProxy.getPacFileUrl(), httpProxy.getPort());
+        } else {
+            proxySettingCopy = IpConfiguration.ProxySettings.STATIC;
+            // Construct a new HTTP Proxy
+            httpProxyCopy = new ProxyInfo(httpProxy.getHost(), httpProxy.getPort(),
+                    httpProxy.getExclusionListAsString());
+        }
+        if (!httpProxyCopy.isValid()) {
+            throw new IllegalArgumentException("Invalid ProxyInfo: " + httpProxyCopy.toString());
+        }
+        mIpConfiguration.setProxySettings(proxySettingCopy);
+        mIpConfiguration.setHttpProxy(httpProxyCopy);
     }
 
     /** @hide */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ab6b3e3..3b6e76f 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -791,6 +791,8 @@
      *
      * @param config the set of variables that describe the configuration,
      *            contained in a {@link WifiConfiguration} object.
+     *            If the {@link WifiConfiguration} has an Http Proxy set
+     *            the calling app must be System, or be provisioned as the Profile or Device Owner.
      * @return the ID of the newly created network description. This is used in
      *         other operations to specified the network to be acted upon.
      *         Returns {@code -1} on failure.
@@ -811,6 +813,8 @@
      *            be sparse, so that only the items that are being changed
      *            are non-<code>null</code>. The {@code networkId} field
      *            must be set to the ID of the existing network being updated.
+     *            If the {@link WifiConfiguration} has an Http Proxy set
+     *            the calling app must be System, or be provisioned as the Profile or Device Owner.
      * @return Returns the {@code networkId} of the supplied
      *         {@code WifiConfiguration} on success.
      *         <br/>
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
index 71124bb..36e4717 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
@@ -43,7 +43,7 @@
  */
 public class WifiNetworkScoreCache extends INetworkScoreCache.Stub {
     private static final String TAG = "WifiNetworkScoreCache";
-    private static final boolean DBG = false;
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     // A Network scorer returns a score in the range [-128, +127]
     // We treat the lowest possible score as though there were no score, effectively allowing the
@@ -83,18 +83,28 @@
         if (networks == null || networks.isEmpty()) {
            return;
         }
-        Log.d(TAG, "updateScores list size=" + networks.size());
+        if (DBG) {
+            Log.d(TAG, "updateScores list size=" + networks.size());
+        }
+
+        boolean changed = false;
 
         synchronized(mNetworkCache) {
             for (ScoredNetwork network : networks) {
                 String networkKey = buildNetworkKey(network);
-                if (networkKey == null) continue;
+                if (networkKey == null) {
+                    if (DBG) {
+                        Log.d(TAG, "Failed to build network key for ScoredNetwork" + network);
+                    }
+                    continue;
+                }
                 mNetworkCache.put(networkKey, network);
+                changed = true;
             }
         }
 
         synchronized (mCacheLock) {
-            if (mListener != null) {
+            if (mListener != null && changed) {
                 mListener.post(networks);
             }
         }
@@ -170,25 +180,49 @@
         return score;
     }
 
-    private ScoredNetwork getScoredNetwork(ScanResult result) {
+    @Nullable
+    public ScoredNetwork getScoredNetwork(ScanResult result) {
         String key = buildNetworkKey(result);
         if (key == null) return null;
 
-        //find it
         synchronized(mNetworkCache) {
             ScoredNetwork network = mNetworkCache.get(key);
             return network;
         }
     }
 
-     private String buildNetworkKey(ScoredNetwork network) {
-        if (network == null || network.networkKey == null) return null;
-        if (network.networkKey.wifiKey == null) return null;
-        if (network.networkKey.type == NetworkKey.TYPE_WIFI) {
-            String key = network.networkKey.wifiKey.ssid;
+    /** Returns the ScoredNetwork for the given key. */
+    @Nullable
+    public ScoredNetwork getScoredNetwork(NetworkKey networkKey) {
+        String key = buildNetworkKey(networkKey);
+        if (key == null) {
+            if (DBG) {
+                Log.d(TAG, "Could not build key string for Network Key: " + networkKey);
+            }
+            return null;
+        }
+        synchronized (mNetworkCache) {
+            return mNetworkCache.get(key);
+        }
+    }
+
+    private String buildNetworkKey(ScoredNetwork network) {
+        if (network == null) {
+            return null;
+        }
+        return buildNetworkKey(network.networkKey);
+    }
+
+    private String buildNetworkKey(NetworkKey networkKey) {
+        if (networkKey == null) {
+            return null;
+        }
+        if (networkKey.wifiKey == null) return null;
+        if (networkKey.type == NetworkKey.TYPE_WIFI) {
+            String key = networkKey.wifiKey.ssid;
             if (key == null) return null;
-            if (network.networkKey.wifiKey.bssid != null) {
-                key = key + network.networkKey.wifiKey.bssid;
+            if (networkKey.wifiKey.bssid != null) {
+                key = key + networkKey.wifiKey.bssid;
             }
             return key;
         }
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
index 65a49ea..98fd0f3 100644
--- a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java
@@ -21,11 +21,17 @@
 import android.net.wifi.hotspot2.pps.HomeSP;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.xml.sax.SAXException;
@@ -131,16 +137,34 @@
     private static final String NODE_FQDN = "FQDN";
     private static final String NODE_FRIENDLY_NAME = "FriendlyName";
     private static final String NODE_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI";
+    private static final String NODE_NETWORK_ID = "NetworkID";
+    private static final String NODE_SSID = "SSID";
+    private static final String NODE_HESSID = "HESSID";
+    private static final String NODE_ICON_URL = "IconURL";
+    private static final String NODE_HOME_OI_LIST = "HomeOIList";
+    private static final String NODE_HOME_OI = "HomeOI";
+    private static final String NODE_HOME_OI_REQUIRED = "HomeOIRequired";
+    private static final String NODE_OTHER_HOME_PARTNERS = "OtherHomePartners";
 
     /**
      * Fields under Credential subtree.
      */
     private static final String NODE_CREDENTIAL = "Credential";
+    private static final String NODE_CREATION_DATE = "CreationDate";
+    private static final String NODE_EXPIRATION_DATE = "ExpirationDate";
     private static final String NODE_USERNAME_PASSWORD = "UsernamePassword";
     private static final String NODE_USERNAME = "Username";
     private static final String NODE_PASSWORD = "Password";
+    private static final String NODE_MACHINE_MANAGED = "MachineManaged";
+    private static final String NODE_SOFT_TOKEN_APP = "SoftTokenApp";
+    private static final String NODE_ABLE_TO_SHARE = "AbleToShare";
     private static final String NODE_EAP_METHOD = "EAPMethod";
     private static final String NODE_EAP_TYPE = "EAPType";
+    private static final String NODE_VENDOR_ID = "VendorId";
+    private static final String NODE_VENDOR_TYPE = "VendorType";
+    private static final String NODE_INNER_EAP_TYPE = "InnerEAPType";
+    private static final String NODE_INNER_VENDOR_ID = "InnerVendorID";
+    private static final String NODE_INNER_VENDOR_TYPE = "InnerVendorType";
     private static final String NODE_INNER_METHOD = "InnerMethod";
     private static final String NODE_DIGITAL_CERTIFICATE = "DigitalCertificate";
     private static final String NODE_CERTIFICATE_TYPE = "CertificateType";
@@ -148,6 +172,7 @@
     private static final String NODE_REALM = "Realm";
     private static final String NODE_SIM = "SIM";
     private static final String NODE_SIM_IMSI = "IMSI";
+    private static final String NODE_CHECK_AAA_SERVER_CERT_STATUS = "CheckAAAServerCertStatus";
 
     /**
      * URN (Unique Resource Name) for PerProviderSubscription Management Object Tree.
@@ -558,6 +583,20 @@
                     homeSp.roamingConsortiumOIs =
                             parseRoamingConsortiumOI(getPpsNodeValue(child));
                     break;
+                case NODE_ICON_URL:
+                    homeSp.iconUrl = getPpsNodeValue(child);
+                    break;
+                case NODE_NETWORK_ID:
+                    homeSp.homeNetworkIds = parseNetworkIds(child);
+                    break;
+                case NODE_HOME_OI_LIST:
+                    Pair<List<Long>, List<Long>> homeOIs = parseHomeOIList(child);
+                    homeSp.matchAllOIs = convertFromLongList(homeOIs.first);
+                    homeSp.matchAnyOIs = convertFromLongList(homeOIs.second);
+                    break;
+                case NODE_OTHER_HOME_PARTNERS:
+                    homeSp.otherHomePartners = parseOtherHomePartners(child);
+                    break;
                 default:
                     throw new ParsingException("Unknown node under HomeSP: " + child.getName());
             }
@@ -587,6 +626,192 @@
     }
 
     /**
+     * Parse configurations under PerProviderSubscription/HomeSP/NetworkID subtree.
+     *
+     * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP/NetworkID
+     *             subtree
+     * @return HashMap<String, Long> representing list of <SSID, HESSID> pair.
+     * @throws ParsingException
+     */
+    static private Map<String, Long> parseNetworkIds(PPSNode node)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for NetworkID");
+        }
+
+        Map<String, Long> networkIds = new HashMap<>();
+        for (PPSNode child : node.getChildren()) {
+            Pair<String, Long> networkId = parseNetworkIdInstance(child);
+            networkIds.put(networkId.first, networkId.second);
+        }
+        return networkIds;
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/HomeSP/NetworkID/<X+> subtree.
+     * The instance name (<X+>) is irrelevant and must be unique for each instance, which
+     * is verified when the PPS tree is constructed {@link #buildPpsNode}.
+     *
+     * @param node PPSNode representing the root of the
+     *             PerProviderSubscription/HomeSP/NetworkID/<X+> subtree
+     * @return Pair<String, Long> representing <SSID, HESSID> pair.
+     * @throws ParsingException
+     */
+    static private Pair<String, Long> parseNetworkIdInstance(PPSNode node)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for NetworkID instance");
+        }
+
+        String ssid = null;
+        Long hessid = null;
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_SSID:
+                    ssid = getPpsNodeValue(child);
+                    break;
+                case NODE_HESSID:
+                    try {
+                        hessid = Long.parseLong(getPpsNodeValue(child), 16);
+                    } catch (NumberFormatException e) {
+                        throw new ParsingException("Invalid HESSID: " + getPpsNodeValue(child));
+                    }
+                    break;
+                default:
+                    throw new ParsingException("Unknown node under NetworkID instance: " +
+                            child.getName());
+            }
+        }
+        if (ssid == null)
+            throw new ParsingException("NetworkID instance missing SSID");
+
+        return new Pair<String, Long>(ssid, hessid);
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/HomeSP/HomeOIList subtree.
+     *
+     * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP/HomeOIList
+     *             subtree
+     * @return Pair<List<Long>, List<Long>> containing both MatchAllOIs and MatchAnyOIs list.
+     * @throws ParsingException
+     */
+    private static Pair<List<Long>, List<Long>> parseHomeOIList(PPSNode node)
+            throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for HomeOIList");
+        }
+
+        List<Long> matchAllOIs = new ArrayList<Long>();
+        List<Long> matchAnyOIs = new ArrayList<Long>();
+        for (PPSNode child : node.getChildren()) {
+            Pair<Long, Boolean> homeOI = parseHomeOIInstance(child);
+            if (homeOI.second.booleanValue()) {
+                matchAllOIs.add(homeOI.first);
+            } else {
+                matchAnyOIs.add(homeOI.first);
+            }
+        }
+        return new Pair<List<Long>, List<Long>>(matchAllOIs, matchAnyOIs);
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/HomeSP/HomeOIList/<X+> subtree.
+     * The instance name (<X+>) is irrelevant and must be unique for each instance, which
+     * is verified when the PPS tree is constructed {@link #buildPpsNode}.
+     *
+     * @param node PPSNode representing the root of the
+     *             PerProviderSubscription/HomeSP/HomeOIList/<X+> subtree
+     * @return Pair<Long, Boolean> containing a HomeOI and a HomeOIRequired flag
+     * @throws ParsingException
+     */
+    private static Pair<Long, Boolean> parseHomeOIInstance(PPSNode node) throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for HomeOI instance");
+        }
+
+        Long oi = null;
+        Boolean required = null;
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_HOME_OI:
+                    try {
+                        oi = Long.valueOf(getPpsNodeValue(child), 16);
+                    } catch (NumberFormatException e) {
+                        throw new ParsingException("Invalid HomeOI: " + getPpsNodeValue(child));
+                    }
+                    break;
+                case NODE_HOME_OI_REQUIRED:
+                    required = Boolean.valueOf(getPpsNodeValue(child));
+                    break;
+                default:
+                    throw new ParsingException("Unknown node under NetworkID instance: " +
+                            child.getName());
+            }
+        }
+        if (oi == null) {
+            throw new ParsingException("HomeOI instance missing OI field");
+        }
+        if (required == null) {
+            throw new ParsingException("HomeOI instance missing required field");
+        }
+        return new Pair<Long, Boolean>(oi, required);
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/HomeSP/OtherHomePartners subtree.
+     * This contains a list of FQDN (Fully Qualified Domain Name) that are considered
+     * home partners.
+     *
+     * @param node PPSNode representing the root of the
+     *             PerProviderSubscription/HomeSP/OtherHomePartners subtree
+     * @return String[] list of partner's FQDN
+     * @throws ParsingException
+     */
+    private static String[] parseOtherHomePartners(PPSNode node) throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for OtherHomePartners");
+        }
+        List<String> otherHomePartners = new ArrayList<String>();
+        for (PPSNode child : node.getChildren()) {
+            String fqdn = parseOtherHomePartnerInstance(child);
+            otherHomePartners.add(fqdn);
+        }
+        return otherHomePartners.toArray(new String[otherHomePartners.size()]);
+    }
+
+    /**
+     * Parse configurations under PerProviderSubscription/HomeSP/OtherHomePartners/<X+> subtree.
+     * The instance name (<X+>) is irrelevant and must be unique for each instance, which
+     * is verified when the PPS tree is constructed {@link #buildPpsNode}.
+     *
+     * @param node PPSNode representing the root of the
+     *             PerProviderSubscription/HomeSP/OtherHomePartners/<X+> subtree
+     * @return String FQDN of the partner
+     * @throws ParsingException
+     */
+    private static String parseOtherHomePartnerInstance(PPSNode node) throws ParsingException {
+        if (node.isLeaf()) {
+            throw new ParsingException("Leaf node not expected for OtherHomePartner instance");
+        }
+        String fqdn = null;
+        for (PPSNode child : node.getChildren()) {
+            switch (child.getName()) {
+                case NODE_FQDN:
+                    fqdn = getPpsNodeValue(child);
+                    break;
+                default:
+                    throw new ParsingException(
+                            "Unknown node under OtherHomePartner instance: " + child.getName());
+            }
+        }
+        if (fqdn == null) {
+            throw new ParsingException("OtherHomePartner instance missing FQDN field");
+        }
+        return fqdn;
+    }
+
+    /**
      * Parse configurations under PerProviderSubscription/Credential subtree.
      *
      * @param node PPSNode representing the root of the PerProviderSubscription/Credential subtree
@@ -601,6 +826,12 @@
         Credential credential = new Credential();
         for (PPSNode child: node.getChildren()) {
             switch (child.getName()) {
+                case NODE_CREATION_DATE:
+                    credential.creationTimeInMs = parseDate(getPpsNodeValue(child));
+                    break;
+                case NODE_EXPIRATION_DATE:
+                    credential.expirationTimeInMs = parseDate(getPpsNodeValue(child));
+                    break;
                 case NODE_USERNAME_PASSWORD:
                     credential.userCredential = parseUserCredential(child);
                     break;
@@ -610,6 +841,10 @@
                 case NODE_REALM:
                     credential.realm = getPpsNodeValue(child);
                     break;
+                case NODE_CHECK_AAA_SERVER_CERT_STATUS:
+                    credential.checkAAAServerCertStatus =
+                            Boolean.parseBoolean(getPpsNodeValue(child));
+                    break;
                 case NODE_SIM:
                     credential.simCredential = parseSimCredential(child);
                     break;
@@ -644,6 +879,15 @@
                 case NODE_PASSWORD:
                     userCred.password = getPpsNodeValue(child);
                     break;
+                case NODE_MACHINE_MANAGED:
+                    userCred.machineManaged = Boolean.parseBoolean(getPpsNodeValue(child));
+                    break;
+                case NODE_SOFT_TOKEN_APP:
+                    userCred.softTokenApp = getPpsNodeValue(child);
+                    break;
+                case NODE_ABLE_TO_SHARE:
+                    userCred.ableToShare = Boolean.parseBoolean(getPpsNodeValue(child));
+                    break;
                 case NODE_EAP_METHOD:
                     parseEAPMethod(child, userCred);
                     break;
@@ -678,6 +922,15 @@
                 case NODE_INNER_METHOD:
                     userCred.nonEapInnerMethod = getPpsNodeValue(child);
                     break;
+                case NODE_VENDOR_ID:
+                case NODE_VENDOR_TYPE:
+                case NODE_INNER_EAP_TYPE:
+                case NODE_INNER_VENDOR_ID:
+                case NODE_INNER_VENDOR_TYPE:
+                    // Only EAP-TTLS is currently supported for user credential, which doesn't
+                    // use any of these parameters.
+                    Log.d(TAG, "Ignore unsupported EAP method parameter: " + child.getName());
+                    break;
                 default:
                     throw new ParsingException("Unknown node under EAPMethod: " + child.getName());
             }
@@ -770,6 +1023,22 @@
     }
 
     /**
+     * Convert a date string to the number of milliseconds since January 1, 1970, 00:00:00 GMT.
+     *
+     * @param dateStr String in the format of yyyy-MM-dd'T'HH:mm:ss'Z'
+     * @return number of milliseconds
+     * @throws ParsingException
+     */
+    private static long parseDate(String dateStr) throws ParsingException {
+        try {
+            DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+            return format.parse(dateStr).getTime();
+        } catch (ParseException pe) {
+            throw new ParsingException("Badly formatted time: " + dateStr);
+        }
+    }
+
+    /**
      * Parse an integer string.
      *
      * @param value String of integer value
@@ -783,4 +1052,19 @@
             throw new ParsingException("Invalid integer value: " + value);
         }
     }
+
+    /**
+     * Convert a List<Long> to a primitive long array long[].
+     *
+     * @param list List to be converted
+     * @return long[]
+     */
+    private static long[] convertFromLongList(List<Long> list) {
+        Long[] objectArray = list.toArray(new Long[list.size()]);
+        long[] primitiveArray = new long[objectArray.length];
+        for (int i = 0; i < objectArray.length; i++) {
+            primitiveArray[i] = objectArray[i].longValue();
+        }
+        return primitiveArray;
+    }
 }
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 790dfaf..3374f42d 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -23,6 +23,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
@@ -41,8 +42,6 @@
  * In addition to the fields in the Credential subtree, this will also maintain necessary
  * information for the private key and certificates associated with this credential.
  *
- * Currently we only support the nodes that are used by Hotspot 2.0 Release 1.
- *
  * @hide
  */
 public final class Credential implements Parcelable {
@@ -52,7 +51,21 @@
      * Max string length for realm.  Refer to Credential/Realm node in Hotspot 2.0 Release 2
      * Technical Specification Section 9.1 for more info.
      */
-    private static final int MAX_REALM_LENGTH = 253;
+    private static final int MAX_REALM_BYTES = 253;
+
+    /**
+     * The time this credential is created. It is in the format of number
+     * of milliseconds since January 1, 1970, 00:00:00 GMT.
+     * Using Long.MIN_VALUE to indicate unset value.
+     */
+    public long creationTimeInMs = Long.MIN_VALUE;
+
+    /**
+     * The time this credential will expire. It is in the format of number
+     * of milliseconds since January 1, 1970, 00:00:00 GMT.
+    * Using Long.MIN_VALUE to indicate unset value.
+     */
+    public long expirationTimeInMs = Long.MIN_VALUE;
 
     /**
      * The realm associated with this credential.  It will be used to determine
@@ -62,6 +75,13 @@
     public String realm = null;
 
     /**
+     * When set to true, the device should check AAA (Authentication, Authorization,
+     * and Accounting) server's certificate during EAP (Extensible Authentication
+     * Protocol) authentication.
+     */
+    public boolean checkAAAServerCertStatus = false;
+
+    /**
      * Username-password based credential.
      * Contains the fields under PerProviderSubscription/Credential/UsernamePassword subtree.
      */
@@ -70,13 +90,13 @@
          * Maximum string length for username.  Refer to Credential/UsernamePassword/Username
          * node in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info.
          */
-        private static final int MAX_USERNAME_LENGTH = 63;
+        private static final int MAX_USERNAME_BYTES = 63;
 
         /**
          * Maximum string length for password.  Refer to Credential/UsernamePassword/Password
          * in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info.
          */
-        private static final int MAX_PASSWORD_LENGTH = 255;
+        private static final int MAX_PASSWORD_BYTES = 255;
 
         /**
          * Supported Non-EAP inner methods.  Refer to
@@ -97,6 +117,21 @@
         public String password = null;
 
         /**
+         * Flag indicating if the password is machine managed.
+         */
+        public boolean machineManaged = false;
+
+        /**
+         * The name of the application used to generate the password.
+         */
+        public String softTokenApp = null;
+
+        /**
+         * Flag indicating if this credential is usable on other mobile devices as well.
+         */
+        public boolean ableToShare = false;
+
+        /**
          * EAP (Extensible Authentication Protocol) method type.
          * Refer to http://www.iana.org/assignments/eap-numbers/eap-numbers.xml#eap-numbers-4
          * for valid values.
@@ -123,6 +158,9 @@
             if (source != null) {
                 username = source.username;
                 password = source.password;
+                machineManaged = source.machineManaged;
+                softTokenApp = source.softTokenApp;
+                ableToShare = source.ableToShare;
                 eapType = source.eapType;
                 nonEapInnerMethod = source.nonEapInnerMethod;
             }
@@ -137,6 +175,9 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeString(username);
             dest.writeString(password);
+            dest.writeInt(machineManaged ? 1 : 0);
+            dest.writeString(softTokenApp);
+            dest.writeInt(ableToShare ? 1 : 0);
             dest.writeInt(eapType);
             dest.writeString(nonEapInnerMethod);
         }
@@ -151,10 +192,13 @@
             }
 
             UserCredential that = (UserCredential) thatObject;
-            return TextUtils.equals(username, that.username) &&
-                    TextUtils.equals(password, that.password) &&
-                    eapType == that.eapType &&
-                    TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod);
+            return TextUtils.equals(username, that.username)
+                    && TextUtils.equals(password, that.password)
+                    && machineManaged == that.machineManaged
+                    && TextUtils.equals(softTokenApp, that.softTokenApp)
+                    && ableToShare == that.ableToShare
+                    && eapType == that.eapType
+                    && TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod);
         }
 
         /**
@@ -167,8 +211,9 @@
                 Log.d(TAG, "Missing username");
                 return false;
             }
-            if (username.length() > MAX_USERNAME_LENGTH) {
-                Log.d(TAG, "username exceeding maximum length: " + username.length());
+            if (username.getBytes(StandardCharsets.UTF_8).length > MAX_USERNAME_BYTES) {
+                Log.d(TAG, "username exceeding maximum length: "
+                        + username.getBytes(StandardCharsets.UTF_8).length);
                 return false;
             }
 
@@ -176,8 +221,9 @@
                 Log.d(TAG, "Missing password");
                 return false;
             }
-            if (password.length() > MAX_PASSWORD_LENGTH) {
-                Log.d(TAG, "password exceeding maximum length: " + password.length());
+            if (password.getBytes(StandardCharsets.UTF_8).length > MAX_PASSWORD_BYTES) {
+                Log.d(TAG, "password exceeding maximum length: "
+                        + password.getBytes(StandardCharsets.UTF_8).length);
                 return false;
             }
 
@@ -202,6 +248,9 @@
                     UserCredential userCredential = new UserCredential();
                     userCredential.username = in.readString();
                     userCredential.password = in.readString();
+                    userCredential.machineManaged = in.readInt() != 0;
+                    userCredential.softTokenApp = in.readString();
+                    userCredential.ableToShare = in.readInt() != 0;
                     userCredential.eapType = in.readInt();
                     userCredential.nonEapInnerMethod = in.readString();
                     return userCredential;
@@ -281,8 +330,8 @@
             }
 
             CertificateCredential that = (CertificateCredential) thatObject;
-            return TextUtils.equals(certType, that.certType) &&
-                    Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint);
+            return TextUtils.equals(certType, that.certType)
+                    && Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint);
         }
 
         /**
@@ -295,8 +344,8 @@
                 Log.d(TAG, "Unsupported certificate type: " + certType);
                 return false;
             }
-            if (certSha256FingerPrint == null ||
-                    certSha256FingerPrint.length != CERT_SHA256_FINGER_PRINT_LENGTH) {
+            if (certSha256FingerPrint == null
+                    || certSha256FingerPrint.length != CERT_SHA256_FINGER_PRINT_LENGTH) {
                 Log.d(TAG, "Invalid SHA-256 fingerprint");
                 return false;
             }
@@ -378,8 +427,8 @@
             }
 
             SimCredential that = (SimCredential) thatObject;
-            return TextUtils.equals(imsi, that.imsi) &&
-                    eapType == that.eapType;
+            return TextUtils.equals(imsi, that.imsi)
+                    && eapType == that.eapType;
         }
 
         @Override
@@ -400,8 +449,8 @@
             if (!verifyImsi()) {
                 return false;
             }
-            if (eapType != EAPConstants.EAP_SIM && eapType != EAPConstants.EAP_AKA &&
-                    eapType != EAPConstants.EAP_AKA_PRIME) {
+            if (eapType != EAPConstants.EAP_SIM && eapType != EAPConstants.EAP_AKA
+                    && eapType != EAPConstants.EAP_AKA_PRIME) {
                 Log.d(TAG, "Invalid EAP Type for SIM credential: " + eapType);
                 return false;
             }
@@ -490,7 +539,10 @@
      */
     public Credential(Credential source) {
         if (source != null) {
+            creationTimeInMs = source.creationTimeInMs;
+            expirationTimeInMs = source.expirationTimeInMs;
             realm = source.realm;
+            checkAAAServerCertStatus = source.checkAAAServerCertStatus;
             if (source.userCredential != null) {
                 userCredential = new UserCredential(source.userCredential);
             }
@@ -516,7 +568,10 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(creationTimeInMs);
+        dest.writeLong(expirationTimeInMs);
         dest.writeString(realm);
+        dest.writeInt(checkAAAServerCertStatus ? 1 : 0);
         dest.writeParcelable(userCredential, flags);
         dest.writeParcelable(certCredential, flags);
         dest.writeParcelable(simCredential, flags);
@@ -535,16 +590,19 @@
         }
 
         Credential that = (Credential) thatObject;
-        return TextUtils.equals(realm, that.realm) &&
-                (userCredential == null ? that.userCredential == null :
-                    userCredential.equals(that.userCredential)) &&
-                (certCredential == null ? that.certCredential == null :
-                    certCredential.equals(that.certCredential)) &&
-                (simCredential == null ? that.simCredential == null :
-                    simCredential.equals(that.simCredential)) &&
-                isX509CertificateEquals(caCertificate, that.caCertificate) &&
-                isX509CertificatesEquals(clientCertificateChain, that.clientCertificateChain) &&
-                isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey);
+        return TextUtils.equals(realm, that.realm)
+                && creationTimeInMs == that.creationTimeInMs
+                && expirationTimeInMs == that.expirationTimeInMs
+                && checkAAAServerCertStatus == that.checkAAAServerCertStatus
+                && (userCredential == null ? that.userCredential == null
+                    : userCredential.equals(that.userCredential))
+                && (certCredential == null ? that.certCredential == null
+                    : certCredential.equals(that.certCredential))
+                && (simCredential == null ? that.simCredential == null
+                    : simCredential.equals(that.simCredential))
+                && isX509CertificateEquals(caCertificate, that.caCertificate)
+                && isX509CertificatesEquals(clientCertificateChain, that.clientCertificateChain)
+                && isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey);
     }
 
     /**
@@ -557,8 +615,9 @@
             Log.d(TAG, "Missing realm");
             return false;
         }
-        if (realm.length() > MAX_REALM_LENGTH) {
-            Log.d(TAG, "realm exceeding maximum length: " + realm.length());
+        if (realm.getBytes(StandardCharsets.UTF_8).length > MAX_REALM_BYTES) {
+            Log.d(TAG, "realm exceeding maximum length: "
+                    + realm.getBytes(StandardCharsets.UTF_8).length);
             return false;
         }
 
@@ -588,7 +647,10 @@
             @Override
             public Credential createFromParcel(Parcel in) {
                 Credential credential = new Credential();
+                credential.creationTimeInMs = in.readLong();
+                credential.expirationTimeInMs = in.readLong();
                 credential.realm = in.readString();
+                credential.checkAAAServerCertStatus = in.readInt() != 0;
                 credential.userCredential = in.readParcelable(null);
                 credential.certCredential = in.readParcelable(null);
                 credential.simCredential = in.readParcelable(null);
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
index d4a5792..4ddf210 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
@@ -21,7 +21,11 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Class representing HomeSP subtree in PerProviderSubscription (PPS)
@@ -30,14 +34,22 @@
  * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0
  * Release 2 Technical Specification.
  *
- * Currently we only support the nodes that are used by Hotspot 2.0 Release 1.
- *
  * @hide
  */
 public final class HomeSP implements Parcelable {
     private static final String TAG = "HomeSP";
 
     /**
+     * Maximum number of bytes allowed for a SSID.
+     */
+    private static final int MAX_SSID_BYTES = 32;
+
+    /**
+     * Integer value used for indicating null value in the Parcel.
+     */
+    private static final int NULL_VALUE = -1;
+
+    /**
      * FQDN (Fully Qualified Domain Name) of this home service provider.
      */
     public String fqdn = null;
@@ -48,6 +60,55 @@
     public String friendlyName = null;
 
     /**
+     * Icon URL of this home service provider.
+     */
+    public String iconUrl = null;
+
+    /**
+     * <SSID, HESSID> duple of the networks that are consider home networks.
+     *
+     * According to the Section 9.1.2 of the Hotspot 2.0 Release 2 Technical Specification,
+     * all nodes in the PSS MO are encoded using UTF-8 unless stated otherwise.  Thus, the SSID
+     * string is assumed to be encoded using UTF-8.
+     */
+    public Map<String, Long> homeNetworkIds = null;
+
+    /**
+     * Used for determining if this provider is a member of a given Hotspot provider.
+     * Every Organization Identifiers (OIs) in this list are required to match an OI in the
+     * the Roaming Consortium advertised by a Hotspot, in order to consider this provider
+     * as a member of that Hotspot provider (e.g. successful authentication with such Hotspot
+     * is possible).
+     *
+     * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object
+     * (MO) tree for more detail.
+     */
+    public long[] matchAllOIs = null;
+
+    /**
+     * Used for determining if this provider is a member of a given Hotspot provider.
+     * Matching of any Organization Identifiers (OIs) in this list with an OI in the
+     * Roaming Consortium advertised by a Hotspot, will consider this provider as a member
+     * of that Hotspot provider (e.g. successful authentication with such Hotspot
+     * is possible).
+     *
+     * {@link #matchAllOIs} will have precedence over this one, meaning this list will
+     * only be used for matching if {@link #matchAllOIs} is null or empty.
+     *
+     * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object
+     * (MO) tree for more detail.
+     */
+    public long[] matchAnyOIs = null;
+
+    /**
+     * List of FQDN (Fully Qualified Domain Name) of partner providers.
+     * These providers should also be regarded as home Hotspot operators.
+     * This relationship is most likely achieved via a commercial agreement or
+     * operator merges between the providers.
+     */
+    public String[] otherHomePartners = null;
+
+    /**
      * List of Organization Identifiers (OIs) identifying a roaming consortium of
      * which this provider is a member.
      */
@@ -64,13 +125,28 @@
      * @param source The source to copy from
      */
     public HomeSP(HomeSP source) {
-        if (source != null) {
-            fqdn = source.fqdn;
-            friendlyName = source.friendlyName;
-            if (source.roamingConsortiumOIs != null) {
-                roamingConsortiumOIs = Arrays.copyOf(source.roamingConsortiumOIs,
-                                                     source.roamingConsortiumOIs.length);
-            }
+        if (source == null) {
+            return;
+        }
+        fqdn = source.fqdn;
+        friendlyName = source.friendlyName;
+        iconUrl = source.iconUrl;
+        if (source.homeNetworkIds != null) {
+            homeNetworkIds = Collections.unmodifiableMap(source.homeNetworkIds);
+        }
+        if (source.matchAllOIs != null) {
+            matchAllOIs = Arrays.copyOf(source.matchAllOIs, source.matchAllOIs.length);
+        }
+        if (source.matchAnyOIs != null) {
+            matchAnyOIs = Arrays.copyOf(source.matchAnyOIs, source.matchAnyOIs.length);
+        }
+        if (source.otherHomePartners != null) {
+            otherHomePartners = Arrays.copyOf(source.otherHomePartners,
+                    source.otherHomePartners.length);
+        }
+        if (source.roamingConsortiumOIs != null) {
+            roamingConsortiumOIs = Arrays.copyOf(source.roamingConsortiumOIs,
+                    source.roamingConsortiumOIs.length);
         }
     }
 
@@ -83,6 +159,11 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(fqdn);
         dest.writeString(friendlyName);
+        dest.writeString(iconUrl);
+        writeHomeNetworkIds(dest, homeNetworkIds);
+        dest.writeLongArray(matchAllOIs);
+        dest.writeLongArray(matchAnyOIs);
+        dest.writeStringArray(otherHomePartners);
         dest.writeLongArray(roamingConsortiumOIs);
     }
 
@@ -96,9 +177,15 @@
         }
         HomeSP that = (HomeSP) thatObject;
 
-        return TextUtils.equals(fqdn, that.fqdn) &&
-                TextUtils.equals(friendlyName, that.friendlyName) &&
-                Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs);
+        return TextUtils.equals(fqdn, that.fqdn)
+                && TextUtils.equals(friendlyName, that.friendlyName)
+                && TextUtils.equals(iconUrl, that.iconUrl)
+                && (homeNetworkIds == null ? that.homeNetworkIds == null
+                        : homeNetworkIds.equals(that.homeNetworkIds))
+                && Arrays.equals(matchAllOIs, that.matchAllOIs)
+                && Arrays.equals(matchAnyOIs, that.matchAnyOIs)
+                && Arrays.equals(otherHomePartners, that.otherHomePartners)
+                && Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs);
     }
 
     /**
@@ -115,6 +202,16 @@
             Log.d(TAG, "Missing friendly name");
             return false;
         }
+        // Verify SSIDs specified in the NetworkID
+        if (homeNetworkIds != null) {
+            for (Map.Entry<String, Long> entry : homeNetworkIds.entrySet()) {
+                if (entry.getKey() == null ||
+                        entry.getKey().getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
+                    Log.d(TAG, "Invalid SSID in HomeNetworkIDs");
+                    return false;
+                }
+            }
+        }
         return true;
     }
 
@@ -125,6 +222,11 @@
                 HomeSP homeSp = new HomeSP();
                 homeSp.fqdn = in.readString();
                 homeSp.friendlyName = in.readString();
+                homeSp.iconUrl = in.readString();
+                homeSp.homeNetworkIds = readHomeNetworkIds(in);
+                homeSp.matchAllOIs = in.createLongArray();
+                homeSp.matchAnyOIs = in.createLongArray();
+                homeSp.otherHomePartners = in.createStringArray();
                 homeSp.roamingConsortiumOIs = in.createLongArray();
                 return homeSp;
             }
@@ -133,5 +235,51 @@
             public HomeSP[] newArray(int size) {
                 return new HomeSP[size];
             }
+
+            /**
+             * Helper function for reading a Home Network IDs map from a Parcel.
+             *
+             * @param in The Parcel to read from
+             * @return Map of home network IDs
+             */
+            private Map<String, Long> readHomeNetworkIds(Parcel in) {
+                int size = in.readInt();
+                if (size == NULL_VALUE) {
+                    return null;
+                }
+                Map<String, Long> networkIds = new HashMap<>(size);
+                for (int i = 0; i < size; i++) {
+                    String key = in.readString();
+                    Long value = null;
+                    long readValue = in.readLong();
+                    if (readValue != NULL_VALUE) {
+                        value = Long.valueOf(readValue);
+                    }
+                    networkIds.put(key, value);
+                }
+                return networkIds;
+            }
         };
+
+    /**
+     * Helper function for writing Home Network IDs map to a Parcel.
+     *
+     * @param dest The Parcel to write to
+     * @param networkIds The map of home network IDs
+     */
+    private static void writeHomeNetworkIds(Parcel dest, Map<String, Long> networkIds) {
+        if (networkIds == null) {
+            dest.writeInt(NULL_VALUE);
+            return;
+        }
+        dest.writeInt(networkIds.size());
+        for (Map.Entry<String, Long> entry : networkIds.entrySet()) {
+            dest.writeString(entry.getKey());
+            if (entry.getValue() == null) {
+                dest.writeLong(NULL_VALUE);
+            } else {
+                dest.writeLong(entry.getValue());
+            }
+        }
+    }
 }
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
index 53d38ad..3969f69 100644
--- a/wifi/tests/assets/pps/PerProviderSubscription.xml
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -23,14 +23,86 @@
           <NodeName>RoamingConsortiumOI</NodeName>
           <Value>112233,445566</Value>
         </Node>
+        <Node>
+          <NodeName>IconURL</NodeName>
+          <Value>icon.test.com</Value>
+        </Node>
+        <Node>
+          <NodeName>NetworkID</NodeName>
+          <Node>
+            <NodeName>n001</NodeName>
+            <Node>
+              <NodeName>SSID</NodeName>
+              <Value>TestSSID</Value>
+            </Node>
+            <Node>
+              <NodeName>HESSID</NodeName>
+              <Value>12345678</Value>
+            </Node>
+          </Node>
+          <Node>
+            <NodeName>n002</NodeName>
+            <Node>
+              <NodeName>SSID</NodeName>
+              <Value>NullHESSID</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>HomeOIList</NodeName>
+          <Node>
+            <NodeName>h001</NodeName>
+            <Node>
+              <NodeName>HomeOI</NodeName>
+              <Value>11223344</Value>
+            </Node>
+            <Node>
+              <NodeName>HomeOIRequired</NodeName>
+              <Value>true</Value>
+            </Node>
+          </Node>
+          <Node>
+            <NodeName>h002</NodeName>
+            <Node>
+              <NodeName>HomeOI</NodeName>
+              <Value>55667788</Value>
+            </Node>
+            <Node>
+              <NodeName>HomeOIRequired</NodeName>
+              <Value>false</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>OtherHomePartners</NodeName>
+          <Node>
+            <NodeName>o001</NodeName>
+            <Node>
+              <NodeName>FQDN</NodeName>
+              <Value>other.fqdn.com</Value>
+            </Node>
+          </Node>
+        </Node>
       </Node>
       <Node>
         <NodeName>Credential</NodeName>
         <Node>
+          <NodeName>CreationDate</NodeName>
+          <Value>2016-01-01T10:00:00Z</Value>
+        </Node>
+        <Node>
+          <NodeName>ExpirationDate</NodeName>
+          <Value>2016-02-01T10:00:00Z</Value>
+        </Node>
+        <Node>
           <NodeName>Realm</NodeName>
           <Value>shaken.stirred.com</Value>
         </Node>
         <Node>
+          <NodeName>CheckAAAServerCertStatus</NodeName>
+          <Value>true</Value>
+        </Node>
+        <Node>
           <NodeName>UsernamePassword</NodeName>
           <Node>
             <NodeName>Username</NodeName>
@@ -41,6 +113,18 @@
             <Value>Ym9uZDAwNw==</Value>
           </Node>
           <Node>
+            <NodeName>MachineManaged</NodeName>
+            <Value>true</Value>
+          </Node>
+          <Node>
+            <NodeName>SoftTokenApp</NodeName>
+            <Value>TestApp</Value>
+          </Node>
+          <Node>
+            <NodeName>AbleToShare</NodeName>
+            <Value>true</Value>
+          </Node>
+          <Node>
             <NodeName>EAPMethod</NodeName>
             <Node>
               <NodeName>EAPType</NodeName>
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
index 10b0267..1c7508e 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java
@@ -31,7 +31,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.HashMap;
 
 /**
  * Unit tests for {@link android.net.wifi.hotspot2.omadm.PPSMOParser}.
@@ -77,7 +80,7 @@
      *
      * @return {@link PasspointConfiguration}
      */
-    private PasspointConfiguration generateConfigurationFromPPSMOTree() {
+    private PasspointConfiguration generateConfigurationFromPPSMOTree() throws Exception {
         PasspointConfiguration config = new PasspointConfiguration();
 
         // HomeSP configuration.
@@ -85,13 +88,27 @@
         config.homeSp.friendlyName = "Century House";
         config.homeSp.fqdn = "mi6.co.uk";
         config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L};
+        config.homeSp.iconUrl = "icon.test.com";
+        config.homeSp.homeNetworkIds = new HashMap<>();
+        config.homeSp.homeNetworkIds.put("TestSSID", 0x12345678L);
+        config.homeSp.homeNetworkIds.put("NullHESSID", null);
+        config.homeSp.matchAllOIs = new long[] {0x11223344};
+        config.homeSp.matchAnyOIs = new long[] {0x55667788};
+        config.homeSp.otherHomePartners = new String[] {"other.fqdn.com"};
 
         // Credential configuration.
+        DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
         config.credential = new Credential();
+        config.credential.creationTimeInMs = format.parse("2016-01-01T10:00:00Z").getTime();
+        config.credential.expirationTimeInMs = format.parse("2016-02-01T10:00:00Z").getTime();
         config.credential.realm = "shaken.stirred.com";
+        config.credential.checkAAAServerCertStatus = true;
         config.credential.userCredential = new Credential.UserCredential();
         config.credential.userCredential.username = "james";
         config.credential.userCredential.password = "Ym9uZDAwNw==";
+        config.credential.userCredential.machineManaged = true;
+        config.credential.userCredential.softTokenApp = "TestApp";
+        config.credential.userCredential.ableToShare = true;
         config.credential.userCredential.eapType = 21;
         config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
         config.credential.certCredential = new Credential.CertificateCredential();
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index 9c8b749..f571c7f 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -37,6 +37,17 @@
  */
 @SmallTest
 public class CredentialTest {
+    /**
+     * Helper function for generating Credential for testing.
+     *
+     * @param userCred Instance of UserCredential
+     * @param certCred Instance of CertificateCredential
+     * @param simCred Instance of SimCredential
+     * @param caCert CA certificate
+     * @param clientCertificateChain Chain of client certificates
+     * @param clientPrivateKey Client private key
+     * @return {@link Credential}
+     */
     private static Credential createCredential(Credential.UserCredential userCred,
                                                Credential.CertificateCredential certCred,
                                                Credential.SimCredential simCred,
@@ -44,7 +55,10 @@
                                                X509Certificate[] clientCertificateChain,
                                                PrivateKey clientPrivateKey) {
         Credential cred = new Credential();
+        cred.creationTimeInMs = 123455L;
+        cred.expirationTimeInMs = 2310093L;
         cred.realm = "realm";
+        cred.checkAAAServerCertStatus = true;
         cred.userCredential = userCred;
         cred.certCredential = certCred;
         cred.simCredential = simCred;
@@ -54,6 +68,11 @@
         return cred;
     }
 
+    /**
+     * Helper function for generating certificate credential for testing.
+     *
+     * @return {@link Credential}
+     */
     private static Credential createCredentialWithCertificateCredential() {
         Credential.CertificateCredential certCred = new Credential.CertificateCredential();
         certCred.certType = "x509v3";
@@ -62,6 +81,11 @@
                 new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
     }
 
+    /**
+     * Helper function for generating SIM credential for testing.
+     *
+     * @return {@link Credential}
+     */
     private static Credential createCredentialWithSimCredential() {
         Credential.SimCredential simCred = new Credential.SimCredential();
         simCred.imsi = "1234*";
@@ -69,10 +93,18 @@
         return createCredential(null, null, simCred, null, null, null);
     }
 
+    /**
+     * Helper function for generating user credential for testing.
+     *
+     * @return {@link Credential}
+     */
     private static Credential createCredentialWithUserCredential() {
         Credential.UserCredential userCred = new Credential.UserCredential();
         userCred.username = "username";
         userCred.password = "password";
+        userCred.machineManaged = true;
+        userCred.ableToShare = true;
+        userCred.softTokenApp = "TestApp";
         userCred.eapType = EAPConstants.EAP_TTLS;
         userCred.nonEapInnerMethod = "MS-CHAP";
         return createCredential(userCred, null, null, FakeKeys.CA_CERT0,
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
index c707993..45fdbea 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
@@ -24,19 +24,71 @@
 
 import org.junit.Test;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Unit tests for {@link android.net.wifi.hotspot2.pps.HomeSP}.
  */
 @SmallTest
 public class HomeSPTest {
-    private static HomeSP createHomeSp() {
+
+    /**
+     * Helper function for creating a map of home network IDs for testing.
+     *
+     * @return Map of home network IDs
+     */
+    private static Map<String, Long> createHomeNetworkIds() {
+        Map<String, Long> homeNetworkIds = new HashMap<>();
+        homeNetworkIds.put("ssid", 0x1234L);
+        homeNetworkIds.put("nullhessid", null);
+        return homeNetworkIds;
+    }
+
+    /**
+     * Helper function for creating a HomeSP for testing.
+     *
+     * @param homeNetworkIds The map of home network IDs associated with HomeSP
+     * @return {@link HomeSP}
+     */
+    private static HomeSP createHomeSp(Map<String, Long> homeNetworkIds) {
         HomeSP homeSp = new HomeSP();
         homeSp.fqdn = "fqdn";
         homeSp.friendlyName = "friendly name";
+        homeSp.iconUrl = "icon.url";
+        homeSp.homeNetworkIds = homeNetworkIds;
+        homeSp.matchAllOIs = new long[] {0x11L, 0x22L};
+        homeSp.matchAnyOIs = new long[] {0x33L, 0x44L};
+        homeSp.otherHomePartners = new String[] {"partner1", "partner2"};
         homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
         return homeSp;
     }
 
+    /**
+     * Helper function for creating a HomeSP with home network IDs for testing.
+     *
+     * @return {@link HomeSP}
+     */
+    private static HomeSP createHomeSpWithHomeNetworkIds() {
+        return createHomeSp(createHomeNetworkIds());
+    }
+
+    /**
+     * Helper function for creating a HomeSP without home network IDs for testing.
+     *
+     * @return {@link HomeSP}
+     */
+    private static HomeSP createHomeSpWithoutHomeNetworkIds() {
+        return createHomeSp(null);
+    }
+
+    /**
+     * Helper function for verifying HomeSP after parcel write then read.
+     * @param writeHomeSp
+     * @throws Exception
+     */
     private static void verifyParcel(HomeSP writeHomeSp) throws Exception {
         Parcel parcel = Parcel.obtain();
         writeHomeSp.writeToParcel(parcel, 0);
@@ -57,13 +109,23 @@
     }
 
     /**
-     * Verify parcel read/write for a valid HomeSP.
+     * Verify parcel read/write for a HomeSP containing Home Network IDs.
      *
      * @throws Exception
      */
     @Test
-    public void verifyParcelWithValidHomeSP() throws Exception {
-        verifyParcel(createHomeSp());
+    public void verifyParcelWithHomeNetworkIds() throws Exception {
+        verifyParcel(createHomeSpWithHomeNetworkIds());
+    }
+
+    /**
+     * Verify parcel read/write for a HomeSP without Home Network IDs.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void verifyParcelWithoutHomeNetworkIds() throws Exception {
+        verifyParcel(createHomeSpWithoutHomeNetworkIds());
     }
 
     /**
@@ -120,6 +182,49 @@
     }
 
     /**
+     * Verify that a HomeSP is valid when the optional Home Network IDs are
+     * provided.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateHomeSpWithHomeNetworkIds() throws Exception {
+        HomeSP homeSp = createHomeSpWithHomeNetworkIds();
+        assertTrue(homeSp.validate());
+    }
+
+    /**
+     * Verify that a HomeSP is valid when the optional Home Network IDs are
+     * not provided.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateHomeSpWithoutHomeNetworkIds() throws Exception {
+        HomeSP homeSp = createHomeSpWithoutHomeNetworkIds();
+        assertTrue(homeSp.validate());
+    }
+
+    /**
+     * Verify that a HomeSP is invalid when the optional Home Network IDs
+     * contained an invalid SSID (exceeding maximum number of bytes).
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateHomeSpWithInvalidHomeNetworkIds() throws Exception {
+        HomeSP homeSp = new HomeSP();
+        homeSp.fqdn = "fqdn";
+        homeSp.friendlyName = "friendly name";
+        homeSp.homeNetworkIds = new HashMap<>();
+        byte[] rawSsidBytes = new byte[33];
+        Arrays.fill(rawSsidBytes, (byte) 'a');
+        homeSp.homeNetworkIds.put(
+                StringFactory.newStringFromBytes(rawSsidBytes, StandardCharsets.UTF_8), 0x1234L);
+        assertFalse(homeSp.validate());
+    }
+
+    /**
      * Verify that copy constructor works when pass in a null source.
      *
      * @throws Exception
@@ -138,10 +243,7 @@
      */
     @Test
     public void validateCopyConstructorFromValidSource() throws Exception {
-        HomeSP sourceSp = new HomeSP();
-        sourceSp.fqdn = "fqdn";
-        sourceSp.friendlyName = "friendlyName";
-        sourceSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+        HomeSP sourceSp = createHomeSpWithHomeNetworkIds();
         HomeSP copySp = new HomeSP(sourceSp);
         assertTrue(copySp.equals(sourceSp));
     }